summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/aspeed/Makefile1
-rw-r--r--drivers/clk/aspeed/clk_ast2600.c1173
-rw-r--r--drivers/clk/mediatek/Makefile1
-rw-r--r--drivers/clk/mediatek/clk-mt8183.c823
-rw-r--r--drivers/dma/bcm6348-iudma.c4
-rw-r--r--drivers/mmc/fsl_esdhc_spl.c3
-rw-r--r--drivers/mmc/pci_mmc.c6
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/mw_eeprom.c238
-rw-r--r--drivers/mtd/nand/raw/Kconfig12
-rw-r--r--drivers/mtd/nand/raw/Makefile1
-rw-r--r--drivers/mtd/nand/raw/cortina_nand.c1390
-rw-r--r--drivers/mtd/nand/raw/cortina_nand.h293
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.c1
-rw-r--r--drivers/power/pmic/Kconfig15
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/mp5416.c98
-rw-r--r--drivers/qe/qe.c1
-rw-r--r--drivers/ram/aspeed/Kconfig61
-rw-r--r--drivers/ram/aspeed/Makefile3
-rw-r--r--drivers/ram/aspeed/sdram_ast2600.c1061
-rw-r--r--drivers/reset/Kconfig9
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/reset-ast2600.c108
-rw-r--r--drivers/sysreset/sysreset_ast.c5
-rw-r--r--drivers/timer/mtk_timer.c10
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/ast2600_wdt.c110
29 files changed, 5191 insertions, 249 deletions
diff --git a/drivers/clk/aspeed/Makefile b/drivers/clk/aspeed/Makefile
index 81764b43917..84776e5265e 100644
--- a/drivers/clk/aspeed/Makefile
+++ b/drivers/clk/aspeed/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_ASPEED_AST2500) += clk_ast2500.o
+obj-$(CONFIG_ASPEED_AST2600) += clk_ast2600.o
diff --git a/drivers/clk/aspeed/clk_ast2600.c b/drivers/clk/aspeed/clk_ast2600.c
new file mode 100644
index 00000000000..f72d384047f
--- /dev/null
+++ b/drivers/clk/aspeed/clk_ast2600.c
@@ -0,0 +1,1173 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) ASPEED Technology Inc.
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <dm/lists.h>
+#include <linux/delay.h>
+#include <asm/arch/scu_ast2600.h>
+#include <dt-bindings/clock/ast2600-clock.h>
+#include <dt-bindings/reset/ast2600-reset.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define CLKIN_25M 25000000UL
+
+/* MAC Clock Delay settings */
+#define MAC12_DEF_DELAY_1G 0x0041b75d
+#define MAC12_DEF_DELAY_100M 0x00417410
+#define MAC12_DEF_DELAY_10M 0x00417410
+#define MAC34_DEF_DELAY_1G 0x0010438a
+#define MAC34_DEF_DELAY_100M 0x00104208
+#define MAC34_DEF_DELAY_10M 0x00104208
+
+/*
+ * 3-bit encode of CPU freqeucy
+ * Some code is duplicated
+ */
+enum ast2600_cpu_freq {
+ CPU_FREQ_1200M_1,
+ CPU_FREQ_1600M_1,
+ CPU_FREQ_1200M_2,
+ CPU_FREQ_1600M_2,
+ CPU_FREQ_800M_1,
+ CPU_FREQ_800M_2,
+ CPU_FREQ_800M_3,
+ CPU_FREQ_800M_4,
+};
+
+struct ast2600_clk_priv {
+ struct ast2600_scu *scu;
+};
+
+/*
+ * Clock divider/multiplier configuration struct.
+ * For H-PLL and M-PLL the formula is
+ * (Output Frequency) = CLKIN * ((M + 1) / (N + 1)) / (P + 1)
+ * M - Numerator
+ * N - Denumerator
+ * P - Post Divider
+ * They have the same layout in their control register.
+ *
+ * D-PLL and D2-PLL have extra divider (OD + 1), which is not
+ * yet needed and ignored by clock configurations.
+ */
+union ast2600_pll_reg {
+ uint32_t w;
+ struct {
+ unsigned int m : 13;
+ unsigned int n : 6;
+ unsigned int p : 4;
+ unsigned int off : 1;
+ unsigned int bypass : 1;
+ unsigned int reset : 1;
+ unsigned int reserved : 6;
+ } b;
+};
+
+struct ast2600_pll_cfg {
+ union ast2600_pll_reg reg;
+ unsigned int ext_reg;
+};
+
+struct ast2600_pll_desc {
+ uint32_t in;
+ uint32_t out;
+ struct ast2600_pll_cfg cfg;
+};
+
+static const struct ast2600_pll_desc ast2600_pll_lookup[] = {
+ {
+ .in = CLKIN_25M,
+ .out = 400000000,
+ .cfg.reg.b.m = 95,
+ .cfg.reg.b.n = 2,
+ .cfg.reg.b.p = 1,
+ .cfg.ext_reg = 0x31,
+ },
+ {
+ .in = CLKIN_25M,
+ .out = 200000000,
+ .cfg.reg.b.m = 127,
+ .cfg.reg.b.n = 0,
+ .cfg.reg.b.p = 15,
+ .cfg.ext_reg = 0x3f,
+ },
+ {
+ .in = CLKIN_25M,
+ .out = 334000000,
+ .cfg.reg.b.m = 667,
+ .cfg.reg.b.n = 4,
+ .cfg.reg.b.p = 9,
+ .cfg.ext_reg = 0x14d,
+ },
+ {
+ .in = CLKIN_25M,
+ .out = 1000000000,
+ .cfg.reg.b.m = 119,
+ .cfg.reg.b.n = 2,
+ .cfg.reg.b.p = 0,
+ .cfg.ext_reg = 0x3d,
+ },
+ {
+ .in = CLKIN_25M,
+ .out = 50000000,
+ .cfg.reg.b.m = 95,
+ .cfg.reg.b.n = 2,
+ .cfg.reg.b.p = 15,
+ .cfg.ext_reg = 0x31,
+ },
+};
+
+/* divisor tables */
+static uint32_t axi_ahb_div0_table[] = {
+ 3, 2, 3, 4,
+};
+
+static uint32_t axi_ahb_div1_table[] = {
+ 3, 4, 6, 8,
+};
+
+static uint32_t axi_ahb_default_table[] = {
+ 3, 4, 3, 4, 2, 2, 2, 2,
+};
+
+extern uint32_t ast2600_get_pll_rate(struct ast2600_scu *scu, int pll_idx)
+{
+ union ast2600_pll_reg pll_reg;
+ uint32_t hwstrap1;
+ uint32_t cpu_freq;
+ uint32_t mul = 1, div = 1;
+
+ switch (pll_idx) {
+ case ASPEED_CLK_APLL:
+ pll_reg.w = readl(&scu->apll);
+ break;
+ case ASPEED_CLK_DPLL:
+ pll_reg.w = readl(&scu->dpll);
+ break;
+ case ASPEED_CLK_EPLL:
+ pll_reg.w = readl(&scu->epll);
+ break;
+ case ASPEED_CLK_HPLL:
+ pll_reg.w = readl(&scu->hpll);
+ break;
+ case ASPEED_CLK_MPLL:
+ pll_reg.w = readl(&scu->mpll);
+ break;
+ }
+
+ if (!pll_reg.b.bypass) {
+ /* F = 25Mhz * [(M + 2) / (n + 1)] / (p + 1)
+ * HPLL Numerator (M) = fix 0x5F when SCU500[10]=1
+ * Fixed 0xBF when SCU500[10]=0 and SCU500[8]=1
+ * SCU200[12:0] (default 0x8F) when SCU510[10]=0 and SCU510[8]=0
+ * HPLL Denumerator (N) = SCU200[18:13] (default 0x2)
+ * HPLL Divider (P) = SCU200[22:19] (default 0x0)
+ * HPLL Bandwidth Adj (NB) = fix 0x2F when SCU500[10]=1
+ * Fixed 0x5F when SCU500[10]=0 and SCU500[8]=1
+ * SCU204[11:0] (default 0x31) when SCU500[10]=0 and SCU500[8]=0
+ */
+ if (pll_idx == ASPEED_CLK_HPLL) {
+ hwstrap1 = readl(&scu->hwstrap1);
+ cpu_freq = (hwstrap1 & SCU_HWSTRAP1_CPU_FREQ_MASK) >>
+ SCU_HWSTRAP1_CPU_FREQ_SHIFT;
+
+ switch (cpu_freq) {
+ case CPU_FREQ_800M_1:
+ case CPU_FREQ_800M_2:
+ case CPU_FREQ_800M_3:
+ case CPU_FREQ_800M_4:
+ pll_reg.b.m = 0x5f;
+ break;
+ case CPU_FREQ_1600M_1:
+ case CPU_FREQ_1600M_2:
+ pll_reg.b.m = 0xbf;
+ break;
+ default:
+ pll_reg.b.m = 0x8f;
+ break;
+ }
+ }
+
+ mul = (pll_reg.b.m + 1) / (pll_reg.b.n + 1);
+ div = (pll_reg.b.p + 1);
+ }
+
+ return ((CLKIN_25M * mul) / div);
+}
+
+static uint32_t ast2600_get_hclk_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = ast2600_get_pll_rate(scu, ASPEED_CLK_HPLL);
+ uint32_t axi_div, ahb_div;
+ uint32_t hwstrap1 = readl(&scu->hwstrap1);
+ uint32_t cpu_freq = (hwstrap1 & SCU_HWSTRAP1_CPU_FREQ_MASK) >>
+ SCU_HWSTRAP1_CPU_FREQ_SHIFT;
+ uint32_t axi_ahb_ratio = (hwstrap1 & SCU_HWSTRAP1_AXI_AHB_CLK_RATIO_MASK) >>
+ SCU_HWSTRAP1_AXI_AHB_CLK_RATIO_SHIFT;
+
+ if (hwstrap1 & SCU_HWSTRAP1_CPU_AXI_CLK_RATIO) {
+ axi_ahb_div1_table[0] = axi_ahb_default_table[cpu_freq] * 2;
+ axi_div = 1;
+ ahb_div = axi_ahb_div1_table[axi_ahb_ratio];
+ } else {
+ axi_ahb_div0_table[0] = axi_ahb_default_table[cpu_freq];
+ axi_div = 2;
+ ahb_div = axi_ahb_div0_table[axi_ahb_ratio];
+ }
+
+ return (rate / axi_div / ahb_div);
+}
+
+static uint32_t ast2600_get_bclk_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = ast2600_get_pll_rate(scu, ASPEED_CLK_HPLL);
+ uint32_t clksrc1 = readl(&scu->clksrc1);
+ uint32_t bclk_div = (clksrc1 & SCU_CLKSRC1_BCLK_DIV_MASK) >>
+ SCU_CLKSRC1_BCLK_DIV_SHIFT;
+
+ return (rate / ((bclk_div + 1) * 4));
+}
+
+static uint32_t ast2600_get_pclk1_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = ast2600_get_pll_rate(scu, ASPEED_CLK_HPLL);
+ uint32_t clksrc1 = readl(&scu->clksrc1);
+ uint32_t pclk_div = (clksrc1 & SCU_CLKSRC1_PCLK_DIV_MASK) >>
+ SCU_CLKSRC1_PCLK_DIV_SHIFT;
+
+ return (rate / ((pclk_div + 1) * 4));
+}
+
+static uint32_t ast2600_get_pclk2_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = ast2600_get_hclk_rate(scu);
+ uint32_t clksrc4 = readl(&scu->clksrc4);
+ uint32_t pclk_div = (clksrc4 & SCU_CLKSRC4_PCLK_DIV_MASK) >>
+ SCU_CLKSRC4_PCLK_DIV_SHIFT;
+
+ return (rate / ((pclk_div + 1) * 2));
+}
+
+static uint32_t ast2600_get_uxclk_in_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = 0;
+ uint32_t clksrc5 = readl(&scu->clksrc5);
+ uint32_t uxclk = (clksrc5 & SCU_CLKSRC5_UXCLK_MASK) >>
+ SCU_CLKSRC5_UXCLK_SHIFT;
+
+ switch (uxclk) {
+ case 0:
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL) / 4;
+ break;
+ case 1:
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL) / 2;
+ break;
+ case 2:
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL);
+ break;
+ case 3:
+ rate = ast2600_get_hclk_rate(scu);
+ break;
+ }
+
+ return rate;
+}
+
+static uint32_t ast2600_get_huxclk_in_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = 0;
+ uint32_t clksrc5 = readl(&scu->clksrc5);
+ uint32_t huxclk = (clksrc5 & SCU_CLKSRC5_HUXCLK_MASK) >>
+ SCU_CLKSRC5_HUXCLK_SHIFT;
+
+ switch (huxclk) {
+ case 0:
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL) / 4;
+ break;
+ case 1:
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL) / 2;
+ break;
+ case 2:
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL);
+ break;
+ case 3:
+ rate = ast2600_get_hclk_rate(scu);
+ break;
+ }
+
+ return rate;
+}
+
+static uint32_t ast2600_get_uart_uxclk_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = ast2600_get_uxclk_in_rate(scu);
+ uint32_t uart_clkgen = readl(&scu->uart_clkgen);
+ uint32_t n = (uart_clkgen & SCU_UART_CLKGEN_N_MASK) >>
+ SCU_UART_CLKGEN_N_SHIFT;
+ uint32_t r = (uart_clkgen & SCU_UART_CLKGEN_R_MASK) >>
+ SCU_UART_CLKGEN_R_SHIFT;
+
+ return ((rate * r) / (n * 2));
+}
+
+static uint32_t ast2600_get_uart_huxclk_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = ast2600_get_huxclk_in_rate(scu);
+ uint32_t huart_clkgen = readl(&scu->huart_clkgen);
+ uint32_t n = (huart_clkgen & SCU_HUART_CLKGEN_N_MASK) >>
+ SCU_HUART_CLKGEN_N_SHIFT;
+ uint32_t r = (huart_clkgen & SCU_HUART_CLKGEN_R_MASK) >>
+ SCU_HUART_CLKGEN_R_SHIFT;
+
+ return ((rate * r) / (n * 2));
+}
+
+static uint32_t ast2600_get_sdio_clk_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = 0;
+ uint32_t clksrc4 = readl(&scu->clksrc4);
+ uint32_t sdio_div = (clksrc4 & SCU_CLKSRC4_SDIO_DIV_MASK) >>
+ SCU_CLKSRC4_SDIO_DIV_SHIFT;
+
+ if (clksrc4 & SCU_CLKSRC4_SDIO)
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL);
+ else
+ rate = ast2600_get_hclk_rate(scu);
+
+ return (rate / ((sdio_div + 1) * 2));
+}
+
+static uint32_t ast2600_get_emmc_clk_rate(struct ast2600_scu *scu)
+{
+ uint32_t rate = ast2600_get_pll_rate(scu, ASPEED_CLK_HPLL);
+ uint32_t clksrc1 = readl(&scu->clksrc1);
+ uint32_t emmc_div = (clksrc1 & SCU_CLKSRC1_EMMC_DIV_MASK) >>
+ SCU_CLKSRC1_EMMC_DIV_SHIFT;
+
+ return (rate / ((emmc_div + 1) * 4));
+}
+
+static uint32_t ast2600_get_uart_clk_rate(struct ast2600_scu *scu, int uart_idx)
+{
+ uint32_t rate = 0;
+ uint32_t uart5_clk = 0;
+ uint32_t clksrc2 = readl(&scu->clksrc2);
+ uint32_t clksrc4 = readl(&scu->clksrc4);
+ uint32_t clksrc5 = readl(&scu->clksrc5);
+ uint32_t misc_ctrl1 = readl(&scu->misc_ctrl1);
+
+ switch (uart_idx) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ if (clksrc4 & BIT(uart_idx - 1))
+ rate = ast2600_get_uart_huxclk_rate(scu);
+ else
+ rate = ast2600_get_uart_uxclk_rate(scu);
+ break;
+ case 5:
+ /*
+ * SCU0C[12] and SCU304[14] together decide
+ * the UART5 clock generation
+ */
+ if (misc_ctrl1 & SCU_MISC_CTRL1_UART5_DIV)
+ uart5_clk = 0x1 << 1;
+
+ if (clksrc2 & SCU_CLKSRC2_UART5)
+ uart5_clk |= 0x1;
+
+ switch (uart5_clk) {
+ case 0:
+ rate = 24000000;
+ break;
+ case 1:
+ rate = 192000000;
+ break;
+ case 2:
+ rate = 24000000 / 13;
+ break;
+ case 3:
+ rate = 192000000 / 13;
+ break;
+ }
+
+ break;
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ if (clksrc5 & BIT(uart_idx - 1))
+ rate = ast2600_get_uart_huxclk_rate(scu);
+ else
+ rate = ast2600_get_uart_uxclk_rate(scu);
+ break;
+ }
+
+ return rate;
+}
+
+static ulong ast2600_clk_get_rate(struct clk *clk)
+{
+ struct ast2600_clk_priv *priv = dev_get_priv(clk->dev);
+ ulong rate = 0;
+
+ switch (clk->id) {
+ case ASPEED_CLK_HPLL:
+ case ASPEED_CLK_EPLL:
+ case ASPEED_CLK_DPLL:
+ case ASPEED_CLK_MPLL:
+ case ASPEED_CLK_APLL:
+ rate = ast2600_get_pll_rate(priv->scu, clk->id);
+ break;
+ case ASPEED_CLK_AHB:
+ rate = ast2600_get_hclk_rate(priv->scu);
+ break;
+ case ASPEED_CLK_APB1:
+ rate = ast2600_get_pclk1_rate(priv->scu);
+ break;
+ case ASPEED_CLK_APB2:
+ rate = ast2600_get_pclk2_rate(priv->scu);
+ break;
+ case ASPEED_CLK_GATE_UART1CLK:
+ rate = ast2600_get_uart_clk_rate(priv->scu, 1);
+ break;
+ case ASPEED_CLK_GATE_UART2CLK:
+ rate = ast2600_get_uart_clk_rate(priv->scu, 2);
+ break;
+ case ASPEED_CLK_GATE_UART3CLK:
+ rate = ast2600_get_uart_clk_rate(priv->scu, 3);
+ break;
+ case ASPEED_CLK_GATE_UART4CLK:
+ rate = ast2600_get_uart_clk_rate(priv->scu, 4);
+ break;
+ case ASPEED_CLK_GATE_UART5CLK:
+ rate = ast2600_get_uart_clk_rate(priv->scu, 5);
+ break;
+ case ASPEED_CLK_BCLK:
+ rate = ast2600_get_bclk_rate(priv->scu);
+ break;
+ case ASPEED_CLK_SDIO:
+ rate = ast2600_get_sdio_clk_rate(priv->scu);
+ break;
+ case ASPEED_CLK_EMMC:
+ rate = ast2600_get_emmc_clk_rate(priv->scu);
+ break;
+ case ASPEED_CLK_UARTX:
+ rate = ast2600_get_uart_uxclk_rate(priv->scu);
+ break;
+ case ASPEED_CLK_HUARTX:
+ rate = ast2600_get_uart_huxclk_rate(priv->scu);
+ break;
+ default:
+ debug("can't get clk rate\n");
+ return -ENOENT;
+ }
+
+ return rate;
+}
+
+/**
+ * @brief lookup PLL divider config by input/output rate
+ * @param[in] *pll - PLL descriptor
+ * @return true - if PLL divider config is found, false - else
+ * The function caller shall fill "pll->in" and "pll->out",
+ * then this function will search the lookup table
+ * to find a valid PLL divider configuration.
+ */
+static bool ast2600_search_clock_config(struct ast2600_pll_desc *pll)
+{
+ uint32_t i;
+ const struct ast2600_pll_desc *def_desc;
+ bool is_found = false;
+
+ for (i = 0; i < ARRAY_SIZE(ast2600_pll_lookup); i++) {
+ def_desc = &ast2600_pll_lookup[i];
+
+ if (def_desc->in == pll->in && def_desc->out == pll->out) {
+ is_found = true;
+ pll->cfg.reg.w = def_desc->cfg.reg.w;
+ pll->cfg.ext_reg = def_desc->cfg.ext_reg;
+ break;
+ }
+ }
+ return is_found;
+}
+
+static uint32_t ast2600_configure_pll(struct ast2600_scu *scu,
+ struct ast2600_pll_cfg *p_cfg, int pll_idx)
+{
+ uint32_t addr, addr_ext;
+ uint32_t reg;
+
+ switch (pll_idx) {
+ case ASPEED_CLK_HPLL:
+ addr = (uint32_t)(&scu->hpll);
+ addr_ext = (uint32_t)(&scu->hpll_ext);
+ break;
+ case ASPEED_CLK_MPLL:
+ addr = (uint32_t)(&scu->mpll);
+ addr_ext = (uint32_t)(&scu->mpll_ext);
+ break;
+ case ASPEED_CLK_DPLL:
+ addr = (uint32_t)(&scu->dpll);
+ addr_ext = (uint32_t)(&scu->dpll_ext);
+ break;
+ case ASPEED_CLK_EPLL:
+ addr = (uint32_t)(&scu->epll);
+ addr_ext = (uint32_t)(&scu->epll_ext);
+ break;
+ case ASPEED_CLK_APLL:
+ addr = (uint32_t)(&scu->apll);
+ addr_ext = (uint32_t)(&scu->apll_ext);
+ break;
+ default:
+ debug("unknown PLL index\n");
+ return 1;
+ }
+
+ p_cfg->reg.b.bypass = 0;
+ p_cfg->reg.b.off = 1;
+ p_cfg->reg.b.reset = 1;
+
+ reg = readl(addr);
+ reg &= ~GENMASK(25, 0);
+ reg |= p_cfg->reg.w;
+ writel(reg, addr);
+
+ /* write extend parameter */
+ writel(p_cfg->ext_reg, addr_ext);
+ udelay(100);
+ p_cfg->reg.b.off = 0;
+ p_cfg->reg.b.reset = 0;
+ reg &= ~GENMASK(25, 0);
+ reg |= p_cfg->reg.w;
+ writel(reg, addr);
+ while (!(readl(addr_ext) & BIT(31)))
+ ;
+
+ return 0;
+}
+
+static uint32_t ast2600_configure_ddr(struct ast2600_scu *scu, ulong rate)
+{
+ struct ast2600_pll_desc mpll;
+
+ mpll.in = CLKIN_25M;
+ mpll.out = rate;
+ if (ast2600_search_clock_config(&mpll) == false) {
+ printf("error!! unable to find valid DDR clock setting\n");
+ return 0;
+ }
+ ast2600_configure_pll(scu, &mpll.cfg, ASPEED_CLK_MPLL);
+
+ return ast2600_get_pll_rate(scu, ASPEED_CLK_MPLL);
+}
+
+static ulong ast2600_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct ast2600_clk_priv *priv = dev_get_priv(clk->dev);
+ ulong new_rate;
+
+ switch (clk->id) {
+ case ASPEED_CLK_MPLL:
+ new_rate = ast2600_configure_ddr(priv->scu, rate);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return new_rate;
+}
+
+static uint32_t ast2600_configure_mac12_clk(struct ast2600_scu *scu)
+{
+ /* scu340[25:0]: 1G default delay */
+ clrsetbits_le32(&scu->mac12_clk_delay, GENMASK(25, 0),
+ MAC12_DEF_DELAY_1G);
+
+ /* set 100M/10M default delay */
+ writel(MAC12_DEF_DELAY_100M, &scu->mac12_clk_delay_100M);
+ writel(MAC12_DEF_DELAY_10M, &scu->mac12_clk_delay_10M);
+
+ /* MAC AHB = HPLL / 6 */
+ clrsetbits_le32(&scu->clksrc1, SCU_CLKSRC1_MAC_DIV_MASK,
+ (0x2 << SCU_CLKSRC1_MAC_DIV_SHIFT));
+
+ return 0;
+}
+
+static uint32_t ast2600_configure_mac34_clk(struct ast2600_scu *scu)
+{
+ /*
+ * scu350[31] RGMII 125M source: 0 = from IO pin
+ * scu350[25:0] MAC 1G delay
+ */
+ clrsetbits_le32(&scu->mac34_clk_delay, (BIT(31) | GENMASK(25, 0)),
+ MAC34_DEF_DELAY_1G);
+ writel(MAC34_DEF_DELAY_100M, &scu->mac34_clk_delay_100M);
+ writel(MAC34_DEF_DELAY_10M, &scu->mac34_clk_delay_10M);
+
+ /*
+ * clock source seletion and divider
+ * scu310[26:24] : MAC AHB bus clock = HCLK / 2
+ * scu310[18:16] : RMII 50M = HCLK_200M / 4
+ */
+ clrsetbits_le32(&scu->clksrc4,
+ (SCU_CLKSRC4_MAC_DIV_MASK | SCU_CLKSRC4_RMII34_DIV_MASK),
+ ((0x0 << SCU_CLKSRC4_MAC_DIV_SHIFT)
+ | (0x3 << SCU_CLKSRC4_RMII34_DIV_SHIFT)));
+
+ /*
+ * set driving strength
+ * scu458[3:2] : MAC4 driving strength
+ * scu458[1:0] : MAC3 driving strength
+ */
+ clrsetbits_le32(&scu->pinmux16,
+ SCU_PINCTRL16_MAC4_DRIVING_MASK | SCU_PINCTRL16_MAC3_DRIVING_MASK,
+ (0x3 << SCU_PINCTRL16_MAC4_DRIVING_SHIFT)
+ | (0x3 << SCU_PINCTRL16_MAC3_DRIVING_SHIFT));
+
+ return 0;
+}
+
+/**
+ * ast2600 RGMII clock source tree
+ * 125M from external PAD -------->|\
+ * HPLL -->|\ | |---->RGMII 125M for MAC#1 & MAC#2
+ * | |---->| divider |---->|/ +
+ * EPLL -->|/ |
+ * |
+ * +---------<-----------|RGMIICK PAD output enable|<-------------+
+ * |
+ * +--------------------------->|\
+ * | |----> RGMII 125M for MAC#3 & MAC#4
+ * HCLK 200M ---->|divider|---->|/
+ * To simplify the control flow:
+ * 1. RGMII 1/2 always use EPLL as the internal clock source
+ * 2. RGMII 3/4 always use RGMIICK pad as the RGMII 125M source
+ * 125M from external PAD -------->|\
+ * | |---->RGMII 125M for MAC#1 & MAC#2
+ * EPLL---->| divider |--->|/ +
+ * |
+ * +<--------------------|RGMIICK PAD output enable|<-------------+
+ * |
+ * +--------------------------->RGMII 125M for MAC#3 & MAC#4
+ */
+#define RGMIICK_SRC_PAD 0
+#define RGMIICK_SRC_EPLL 1 /* recommended */
+#define RGMIICK_SRC_HPLL 2
+
+#define RGMIICK_DIV2 1
+#define RGMIICK_DIV3 2
+#define RGMIICK_DIV4 3
+#define RGMIICK_DIV5 4
+#define RGMIICK_DIV6 5
+#define RGMIICK_DIV7 6
+#define RGMIICK_DIV8 7 /* recommended */
+
+#define RMIICK_DIV4 0
+#define RMIICK_DIV8 1
+#define RMIICK_DIV12 2
+#define RMIICK_DIV16 3
+#define RMIICK_DIV20 4 /* recommended */
+#define RMIICK_DIV24 5
+#define RMIICK_DIV28 6
+#define RMIICK_DIV32 7
+
+struct ast2600_mac_clk_div {
+ uint32_t src; /* 0=external PAD, 1=internal PLL */
+ uint32_t fin; /* divider input speed */
+ uint32_t n; /* 0=div2, 1=div2, 2=div3, 3=div4,...,7=div8 */
+ uint32_t fout; /* fout = fin / n */
+};
+
+struct ast2600_mac_clk_div rgmii_clk_defconfig = {
+ .src = ASPEED_CLK_EPLL,
+ .fin = 1000000000,
+ .n = RGMIICK_DIV8,
+ .fout = 125000000,
+};
+
+struct ast2600_mac_clk_div rmii_clk_defconfig = {
+ .src = ASPEED_CLK_EPLL,
+ .fin = 1000000000,
+ .n = RMIICK_DIV20,
+ .fout = 50000000,
+};
+
+static void ast2600_init_mac_pll(struct ast2600_scu *p_scu,
+ struct ast2600_mac_clk_div *p_cfg)
+{
+ struct ast2600_pll_desc pll;
+
+ pll.in = CLKIN_25M;
+ pll.out = p_cfg->fin;
+ if (ast2600_search_clock_config(&pll) == false) {
+ pr_err("unable to find valid ETHNET MAC clock setting\n");
+ return;
+ }
+ ast2600_configure_pll(p_scu, &pll.cfg, p_cfg->src);
+}
+
+static void ast2600_init_rgmii_clk(struct ast2600_scu *p_scu,
+ struct ast2600_mac_clk_div *p_cfg)
+{
+ uint32_t reg_304 = readl(&p_scu->clksrc2);
+ uint32_t reg_340 = readl(&p_scu->mac12_clk_delay);
+ uint32_t reg_350 = readl(&p_scu->mac34_clk_delay);
+
+ reg_340 &= ~GENMASK(31, 29);
+ /* scu340[28]: RGMIICK PAD output enable (to MAC 3/4) */
+ reg_340 |= BIT(28);
+ if (p_cfg->src == ASPEED_CLK_EPLL || p_cfg->src == ASPEED_CLK_HPLL) {
+ /*
+ * re-init PLL if the current PLL output frequency doesn't match
+ * the divider setting
+ */
+ if (p_cfg->fin != ast2600_get_pll_rate(p_scu, p_cfg->src))
+ ast2600_init_mac_pll(p_scu, p_cfg);
+ /* scu340[31]: select RGMII 125M from internal source */
+ reg_340 |= BIT(31);
+ }
+
+ reg_304 &= ~GENMASK(23, 20);
+
+ /* set clock divider */
+ reg_304 |= (p_cfg->n & 0x7) << 20;
+
+ /* select internal clock source */
+ if (p_cfg->src == ASPEED_CLK_HPLL)
+ reg_304 |= BIT(23);
+
+ /* RGMII 3/4 clock source select */
+ reg_350 &= ~BIT(31);
+
+ writel(reg_304, &p_scu->clksrc2);
+ writel(reg_340, &p_scu->mac12_clk_delay);
+ writel(reg_350, &p_scu->mac34_clk_delay);
+}
+
+/**
+ * ast2600 RMII/NCSI clock source tree
+ * HPLL -->|\
+ * | |---->| divider |----> RMII 50M for MAC#1 & MAC#2
+ * EPLL -->|/
+ * HCLK(SCLICLK)---->| divider |----> RMII 50M for MAC#3 & MAC#4
+ */
+static void ast2600_init_rmii_clk(struct ast2600_scu *p_scu,
+ struct ast2600_mac_clk_div *p_cfg)
+{
+ uint32_t clksrc2 = readl(&p_scu->clksrc2);
+ uint32_t clksrc4 = readl(&p_scu->clksrc4);
+
+ if (p_cfg->src == ASPEED_CLK_EPLL || p_cfg->src == ASPEED_CLK_HPLL) {
+ /*
+ * re-init PLL if the current PLL output frequency doesn't match
+ * the divider setting
+ */
+ if (p_cfg->fin != ast2600_get_pll_rate(p_scu, p_cfg->src))
+ ast2600_init_mac_pll(p_scu, p_cfg);
+ }
+
+ clksrc2 &= ~(SCU_CLKSRC2_RMII12 | SCU_CLKSRC2_RMII12_DIV_MASK);
+
+ /* set RMII 1/2 clock divider */
+ clksrc2 |= (p_cfg->n & 0x7) << 16;
+
+ /* RMII clock source selection */
+ if (p_cfg->src == ASPEED_CLK_HPLL)
+ clksrc2 |= SCU_CLKSRC2_RMII12;
+
+ /* set RMII 3/4 clock divider */
+ clksrc4 &= ~SCU_CLKSRC4_RMII34_DIV_MASK;
+ clksrc4 |= (0x3 << SCU_CLKSRC4_RMII34_DIV_SHIFT);
+
+ writel(clksrc2, &p_scu->clksrc2);
+ writel(clksrc4, &p_scu->clksrc4);
+}
+
+static uint32_t ast2600_configure_mac(struct ast2600_scu *scu, int index)
+{
+ uint32_t reset_bit;
+ uint32_t clkgate_bit;
+
+ switch (index) {
+ case 1:
+ reset_bit = BIT(ASPEED_RESET_MAC1);
+ clkgate_bit = SCU_CLKGATE1_MAC1;
+ writel(reset_bit, &scu->modrst_ctrl1);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr1);
+ mdelay(10);
+ writel(reset_bit, &scu->modrst_clr1);
+ break;
+ case 2:
+ reset_bit = BIT(ASPEED_RESET_MAC2);
+ clkgate_bit = SCU_CLKGATE1_MAC2;
+ writel(reset_bit, &scu->modrst_ctrl1);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr1);
+ mdelay(10);
+ writel(reset_bit, &scu->modrst_clr1);
+ break;
+ case 3:
+ reset_bit = BIT(ASPEED_RESET_MAC3 - 32);
+ clkgate_bit = SCU_CLKGATE2_MAC3;
+ writel(reset_bit, &scu->modrst_ctrl2);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr2);
+ mdelay(10);
+ writel(reset_bit, &scu->modrst_clr2);
+ break;
+ case 4:
+ reset_bit = BIT(ASPEED_RESET_MAC4 - 32);
+ clkgate_bit = SCU_CLKGATE2_MAC4;
+ writel(reset_bit, &scu->modrst_ctrl2);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr2);
+ mdelay(10);
+ writel(reset_bit, &scu->modrst_clr2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ast2600_configure_rsa_ecc_clk(struct ast2600_scu *scu)
+{
+ uint32_t clksrc1 = readl(&scu->clksrc1);
+
+ /* Configure RSA clock = HPLL/3 */
+ clksrc1 |= SCU_CLKSRC1_ECC_RSA;
+ clksrc1 &= ~SCU_CLKSRC1_ECC_RSA_DIV_MASK;
+ clksrc1 |= (2 << SCU_CLKSRC1_ECC_RSA_DIV_SHIFT);
+
+ writel(clksrc1, &scu->clksrc1);
+}
+
+static ulong ast2600_enable_sdclk(struct ast2600_scu *scu)
+{
+ uint32_t reset_bit;
+ uint32_t clkgate_bit;
+
+ reset_bit = BIT(ASPEED_RESET_SD - 32);
+ clkgate_bit = SCU_CLKGATE2_SDIO;
+
+ writel(reset_bit, &scu->modrst_ctrl2);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr2);
+ mdelay(10);
+ writel(reset_bit, &scu->modrst_clr2);
+
+ return 0;
+}
+
+static ulong ast2600_enable_extsdclk(struct ast2600_scu *scu)
+{
+ int i = 0;
+ uint32_t div = 0;
+ uint32_t rate = 0;
+ uint32_t clksrc4 = readl(&scu->clksrc4);
+
+ /*
+ * ast2600 SD controller max clk is 200Mhz
+ * use apll for clock source 800/4 = 200
+ * controller max is 200mhz
+ */
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_APLL);
+ for (i = 0; i < 8; i++) {
+ div = (i + 1) * 2;
+ if ((rate / div) <= 200000000)
+ break;
+ }
+ clksrc4 &= ~SCU_CLKSRC4_SDIO_DIV_MASK;
+ clksrc4 |= (i << SCU_CLKSRC4_SDIO_DIV_SHIFT);
+ clksrc4 |= SCU_CLKSRC4_SDIO;
+ writel(clksrc4, &scu->clksrc4);
+
+ setbits_le32(&scu->clksrc4, SCU_CLKSRC4_SDIO_EN);
+
+ return 0;
+}
+
+static ulong ast2600_enable_emmcclk(struct ast2600_scu *scu)
+{
+ uint32_t reset_bit;
+ uint32_t clkgate_bit;
+
+ reset_bit = BIT(ASPEED_RESET_EMMC);
+ clkgate_bit = SCU_CLKGATE1_EMMC;
+
+ writel(reset_bit, &scu->modrst_ctrl1);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr1);
+ mdelay(10);
+ writel(reset_bit, &scu->modrst_clr1);
+
+ return 0;
+}
+
+static ulong ast2600_enable_extemmcclk(struct ast2600_scu *scu)
+{
+ int i = 0;
+ uint32_t div = 0;
+ uint32_t rate = 0;
+ uint32_t clksrc1 = readl(&scu->clksrc1);
+
+ /*
+ * ast2600 eMMC controller max clk is 200Mhz
+ * HPll->1/2->|\
+ * |->SCU300[11]->SCU300[14:12][1/N] +
+ * MPLL------>|/ |
+ * +----------------------------------------------+
+ * |
+ * +---------> EMMC12C[15:8][1/N]-> eMMC clk
+ */
+ rate = ast2600_get_pll_rate(scu, ASPEED_CLK_MPLL);
+ for (i = 0; i < 8; i++) {
+ div = (i + 1) * 2;
+ if ((rate / div) <= 200000000)
+ break;
+ }
+
+ clksrc1 &= ~SCU_CLKSRC1_EMMC_DIV_MASK;
+ clksrc1 |= (i << SCU_CLKSRC1_EMMC_DIV_SHIFT);
+ clksrc1 |= SCU_CLKSRC1_EMMC;
+ writel(clksrc1, &scu->clksrc1);
+
+ setbits_le32(&scu->clksrc1, SCU_CLKSRC1_EMMC_EN);
+
+ return 0;
+}
+
+static ulong ast2600_enable_fsiclk(struct ast2600_scu *scu)
+{
+ uint32_t reset_bit;
+ uint32_t clkgate_bit;
+
+ reset_bit = BIT(ASPEED_RESET_FSI % 32);
+ clkgate_bit = SCU_CLKGATE2_FSI;
+
+ /* The FSI clock is shared between masters. If it's already on
+ * don't touch it, as that will reset the existing master.
+ */
+ if (!(readl(&scu->clkgate_ctrl2) & clkgate_bit)) {
+ debug("%s: already running, not touching it\n", __func__);
+ return 0;
+ }
+
+ writel(reset_bit, &scu->modrst_ctrl2);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr2);
+ mdelay(10);
+ writel(reset_bit, &scu->modrst_clr2);
+
+ return 0;
+}
+
+static ulong ast2600_enable_usbahclk(struct ast2600_scu *scu)
+{
+ uint32_t reset_bit;
+ uint32_t clkgate_bit;
+
+ reset_bit = BIT(ASPEED_RESET_EHCI_P1);
+ clkgate_bit = SCU_CLKGATE1_USB_HUB;
+
+ writel(reset_bit, &scu->modrst_ctrl1);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_ctrl1);
+ mdelay(20);
+ writel(reset_bit, &scu->modrst_clr1);
+
+ return 0;
+}
+
+static ulong ast2600_enable_usbbhclk(struct ast2600_scu *scu)
+{
+ uint32_t reset_bit;
+ uint32_t clkgate_bit;
+
+ reset_bit = BIT(ASPEED_RESET_EHCI_P2);
+ clkgate_bit = SCU_CLKGATE1_USB_HOST2;
+
+ writel(reset_bit, &scu->modrst_ctrl1);
+ udelay(100);
+ writel(clkgate_bit, &scu->clkgate_clr1);
+ mdelay(20);
+ writel(reset_bit, &scu->modrst_clr1);
+
+ return 0;
+}
+
+static int ast2600_clk_enable(struct clk *clk)
+{
+ struct ast2600_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case ASPEED_CLK_GATE_MAC1CLK:
+ ast2600_configure_mac(priv->scu, 1);
+ break;
+ case ASPEED_CLK_GATE_MAC2CLK:
+ ast2600_configure_mac(priv->scu, 2);
+ break;
+ case ASPEED_CLK_GATE_MAC3CLK:
+ ast2600_configure_mac(priv->scu, 3);
+ break;
+ case ASPEED_CLK_GATE_MAC4CLK:
+ ast2600_configure_mac(priv->scu, 4);
+ break;
+ case ASPEED_CLK_GATE_SDCLK:
+ ast2600_enable_sdclk(priv->scu);
+ break;
+ case ASPEED_CLK_GATE_SDEXTCLK:
+ ast2600_enable_extsdclk(priv->scu);
+ break;
+ case ASPEED_CLK_GATE_EMMCCLK:
+ ast2600_enable_emmcclk(priv->scu);
+ break;
+ case ASPEED_CLK_GATE_EMMCEXTCLK:
+ ast2600_enable_extemmcclk(priv->scu);
+ break;
+ case ASPEED_CLK_GATE_FSICLK:
+ ast2600_enable_fsiclk(priv->scu);
+ break;
+ case ASPEED_CLK_GATE_USBPORT1CLK:
+ ast2600_enable_usbahclk(priv->scu);
+ break;
+ case ASPEED_CLK_GATE_USBPORT2CLK:
+ ast2600_enable_usbbhclk(priv->scu);
+ break;
+ default:
+ pr_err("can't enable clk\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+struct clk_ops ast2600_clk_ops = {
+ .get_rate = ast2600_clk_get_rate,
+ .set_rate = ast2600_clk_set_rate,
+ .enable = ast2600_clk_enable,
+};
+
+static int ast2600_clk_probe(struct udevice *dev)
+{
+ struct ast2600_clk_priv *priv = dev_get_priv(dev);
+
+ priv->scu = devfdt_get_addr_ptr(dev);
+ if (IS_ERR(priv->scu))
+ return PTR_ERR(priv->scu);
+
+ ast2600_init_rgmii_clk(priv->scu, &rgmii_clk_defconfig);
+ ast2600_init_rmii_clk(priv->scu, &rmii_clk_defconfig);
+ ast2600_configure_mac12_clk(priv->scu);
+ ast2600_configure_mac34_clk(priv->scu);
+ ast2600_configure_rsa_ecc_clk(priv->scu);
+
+ return 0;
+}
+
+static int ast2600_clk_bind(struct udevice *dev)
+{
+ int ret;
+
+ /* The reset driver does not have a device node, so bind it here */
+ ret = device_bind_driver(gd->dm_root, "ast_sysreset", "reset", &dev);
+ if (ret)
+ debug("Warning: No reset driver: ret=%d\n", ret);
+
+ return 0;
+}
+
+struct aspeed_clks {
+ ulong id;
+ const char *name;
+};
+
+static struct aspeed_clks aspeed_clk_names[] = {
+ { ASPEED_CLK_HPLL, "hpll" },
+ { ASPEED_CLK_MPLL, "mpll" },
+ { ASPEED_CLK_APLL, "apll" },
+ { ASPEED_CLK_EPLL, "epll" },
+ { ASPEED_CLK_DPLL, "dpll" },
+ { ASPEED_CLK_AHB, "hclk" },
+ { ASPEED_CLK_APB1, "pclk1" },
+ { ASPEED_CLK_APB2, "pclk2" },
+ { ASPEED_CLK_BCLK, "bclk" },
+ { ASPEED_CLK_UARTX, "uxclk" },
+ { ASPEED_CLK_HUARTX, "huxclk" },
+};
+
+int soc_clk_dump(void)
+{
+ struct udevice *dev;
+ struct clk clk;
+ unsigned long rate;
+ int i, ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(aspeed_scu),
+ &dev);
+ if (ret)
+ return ret;
+
+ printf("Clk\t\tHz\n");
+
+ for (i = 0; i < ARRAY_SIZE(aspeed_clk_names); i++) {
+ clk.id = aspeed_clk_names[i].id;
+ ret = clk_request(dev, &clk);
+ if (ret < 0) {
+ debug("%s clk_request() failed: %d\n", __func__, ret);
+ continue;
+ }
+
+ ret = clk_get_rate(&clk);
+ rate = ret;
+
+ clk_free(&clk);
+
+ if (ret == -ENOTSUPP) {
+ printf("clk ID %lu not supported yet\n",
+ aspeed_clk_names[i].id);
+ continue;
+ }
+ if (ret < 0) {
+ printf("%s %lu: get_rate err: %d\n", __func__,
+ aspeed_clk_names[i].id, ret);
+ continue;
+ }
+
+ printf("%s(%3lu):\t%lu\n", aspeed_clk_names[i].name,
+ aspeed_clk_names[i].id, rate);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id ast2600_clk_ids[] = {
+ { .compatible = "aspeed,ast2600-scu", },
+ { },
+};
+
+U_BOOT_DRIVER(aspeed_ast2600_scu) = {
+ .name = "aspeed_ast2600_scu",
+ .id = UCLASS_CLK,
+ .of_match = ast2600_clk_ids,
+ .priv_auto = sizeof(struct ast2600_clk_priv),
+ .ops = &ast2600_clk_ops,
+ .bind = ast2600_clk_bind,
+ .probe = ast2600_clk_probe,
+};
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 237fd17f167..522e7242214 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_MT8512) += clk-mt8512.o
obj-$(CONFIG_TARGET_MT7623) += clk-mt7623.o
obj-$(CONFIG_TARGET_MT7622) += clk-mt7622.o
obj-$(CONFIG_TARGET_MT7629) += clk-mt7629.o
+obj-$(CONFIG_TARGET_MT8183) += clk-mt8183.o
obj-$(CONFIG_TARGET_MT8516) += clk-mt8516.o
obj-$(CONFIG_TARGET_MT8518) += clk-mt8518.o
diff --git a/drivers/clk/mediatek/clk-mt8183.c b/drivers/clk/mediatek/clk-mt8183.c
new file mode 100644
index 00000000000..17e653a1f00
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8183.c
@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek clock driver for MT8183 SoC
+ *
+ * Copyright (C) 2020 BayLibre, SAS
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Fabien Parent <fparent@baylibre.com>
+ * Author: Weiyi Lu <weiyi.lu@mediatek.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <dt-bindings/clock/mt8183-clk.h>
+
+#include "clk-mtk.h"
+
+#define MT8183_PLL_FMAX (3800UL * MHZ)
+#define MT8183_PLL_FMIN (1500UL * MHZ)
+
+/* apmixedsys */
+#define PLL(_id, _reg, _pwr_reg, _en_mask, _flags, _rst_bar_mask, _pcwbits, \
+ _pcwibits, _pd_reg, _pd_shift, _pcw_reg, _pcw_shift) { \
+ .id = _id, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .fmax = MT8183_PLL_FMAX, \
+ .fmin = MT8183_PLL_FMIN, \
+ .flags = _flags, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = _pcwibits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ }
+
+static const struct mtk_pll_data apmixed_plls[] = {
+ PLL(CLK_APMIXED_ARMPLL_LL, 0x0200, 0x020C, 0x00000001,
+ HAVE_RST_BAR, BIT(24), 22, 8, 0x0204, 24,
+ 0x0204, 0),
+ PLL(CLK_APMIXED_ARMPLL_L, 0x0210, 0x021C, 0x00000001,
+ HAVE_RST_BAR, BIT(24), 22, 8, 0x0214, 24,
+ 0x0214, 0),
+ PLL(CLK_APMIXED_CCIPLL, 0x0290, 0x029C, 0x00000001,
+ HAVE_RST_BAR, BIT(24), 22, 8, 0x0294, 24,
+ 0x0294, 0),
+ PLL(CLK_APMIXED_MAINPLL, 0x0220, 0x022C, 0x00000001,
+ HAVE_RST_BAR, BIT(24), 22, 8, 0x0224, 24,
+ 0x0224, 0),
+ PLL(CLK_APMIXED_UNIV2PLL, 0x0230, 0x023C, 0x00000001,
+ HAVE_RST_BAR, BIT(24), 22, 8, 0x0234, 24,
+ 0x0234, 0),
+ PLL(CLK_APMIXED_MSDCPLL, 0x0250, 0x025C, 0x00000001,
+ 0, 0, 22, 8, 0x0254, 24, 0x0254, 0),
+ PLL(CLK_APMIXED_MMPLL, 0x0270, 0x027C, 0x00000001,
+ HAVE_RST_BAR, BIT(23), 22, 8, 0x0274, 24,
+ 0x0274, 0),
+ PLL(CLK_APMIXED_MFGPLL, 0x0240, 0x024C, 0x00000001,
+ 0, 0, 22, 8, 0x0244, 24, 0x0244, 0),
+ PLL(CLK_APMIXED_TVDPLL, 0x0260, 0x026C, 0x00000001,
+ 0, 0, 22, 8, 0x0264, 24, 0x0264, 0),
+ PLL(CLK_APMIXED_APLL1, 0x02A0, 0x02B0, 0x00000001,
+ 0, 0, 32, 8, 0x02A0, 1, 0x02A4, 0),
+ PLL(CLK_APMIXED_APLL2, 0x02b4, 0x02c4, 0x00000001,
+ 0, 0, 32, 8, 0x02B4, 1, 0x02B8, 0),
+};
+
+static const struct mtk_fixed_clk top_fixed_clks[] = {
+ FIXED_CLK(CLK_TOP_CLK26M, CLK_XTAL, 26000000),
+ FIXED_CLK(CLK_TOP_ULPOSC, CLK_XTAL, 250000),
+ FIXED_CLK(CLK_TOP_UNIVP_192M, CLK_TOP_UNIVPLL, 192000000),
+};
+
+static const struct mtk_fixed_factor top_fixed_divs[] = {
+ FACTOR(CLK_TOP_CLK13M, CLK_TOP_CLK26M, 1, 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_F26M_CK_D2, CLK_TOP_CLK26M, 1, 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_CK, CLK_APMIXED_MAINPLL, 1,
+ 1, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_SYSPLL_D2, CLK_TOP_SYSPLL_CK, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D3, CLK_APMIXED_MAINPLL, 1,
+ 3, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_SYSPLL_D5, CLK_APMIXED_MAINPLL, 1,
+ 5, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_SYSPLL_D7, CLK_APMIXED_MAINPLL, 1,
+ 7, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_SYSPLL_D2_D2, CLK_TOP_SYSPLL_D2, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D2_D4, CLK_TOP_SYSPLL_D2, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D2_D8, CLK_TOP_SYSPLL_D2, 1,
+ 8, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D2_D16, CLK_TOP_SYSPLL_D2, 1,
+ 16, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D3_D2, CLK_TOP_SYSPLL_D3, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D3_D4, CLK_TOP_SYSPLL_D3, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D3_D8, CLK_TOP_SYSPLL_D3, 1,
+ 8, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D5_D2, CLK_TOP_SYSPLL_D5, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D5_D4, CLK_TOP_SYSPLL_D5, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D7_D2, CLK_TOP_SYSPLL_D7, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_SYSPLL_D7_D4, CLK_TOP_SYSPLL_D7, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_CK, CLK_TOP_UNIVPLL, 1, 1, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D2, CLK_TOP_UNIVPLL_CK, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D3, CLK_TOP_UNIVPLL, 1, 3, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D5, CLK_TOP_UNIVPLL, 1, 5, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D7, CLK_TOP_UNIVPLL, 1, 7, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D2_D2, CLK_TOP_UNIVPLL_D2, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D2_D4, CLK_TOP_UNIVPLL_D2, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D2_D8, CLK_TOP_UNIVPLL_D2, 1,
+ 8, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D3_D2, CLK_TOP_UNIVPLL_D3, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D3_D4, CLK_TOP_UNIVPLL_D3, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D3_D8, CLK_TOP_UNIVPLL_D3, 1,
+ 8, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D2, CLK_TOP_UNIVPLL_D5, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D4, CLK_TOP_UNIVPLL_D5, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D8, CLK_TOP_UNIVPLL_D5, 1,
+ 8, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVP_192M_CK, CLK_TOP_UNIVP_192M, 1, 1,
+ CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVP_192M_D2, CLK_TOP_UNIVP_192M_CK, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVP_192M_D4, CLK_TOP_UNIVP_192M_CK, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVP_192M_D8, CLK_TOP_UNIVP_192M_CK, 1,
+ 8, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVP_192M_D16, CLK_TOP_UNIVP_192M_CK, 1,
+ 16, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVP_192M_D32, CLK_TOP_UNIVP_192M_CK, 1,
+ 32, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_APLL1_CK, CLK_APMIXED_APLL1, 1, 1, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_APLL1_D2, CLK_APMIXED_APLL1, 1, 2, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_APLL1_D4, CLK_APMIXED_APLL1, 1, 4, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_APLL1_D8, CLK_APMIXED_APLL1, 1, 8, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_APLL2_CK, CLK_APMIXED_APLL2, 1, 1, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_APLL2_D2, CLK_APMIXED_APLL2, 1, 2, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_APLL2_D4, CLK_APMIXED_APLL2, 1, 4, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_APLL2_D8, CLK_APMIXED_APLL2, 1, 8, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_TVDPLL_CK, CLK_APMIXED_TVDPLL, 1, 1, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_TVDPLL_D2, CLK_TOP_TVDPLL_CK, 1, 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_TVDPLL_D4, CLK_APMIXED_TVDPLL, 1, 4, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_TVDPLL_D8, CLK_APMIXED_TVDPLL, 1, 8, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_TVDPLL_D16, CLK_APMIXED_TVDPLL, 1,
+ 16, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MMPLL_CK, CLK_APMIXED_MMPLL, 1, 1, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MMPLL_D4, CLK_APMIXED_MMPLL, 1, 4, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MMPLL_D4_D2, CLK_TOP_MMPLL_D4, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_MMPLL_D4_D4, CLK_TOP_MMPLL_D4, 1, 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_MMPLL_D5, CLK_APMIXED_MMPLL, 1, 5, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MMPLL_D5_D2, CLK_TOP_MMPLL_D5, 1,
+ 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_MMPLL_D5_D4, CLK_TOP_MMPLL_D5, 1,
+ 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_MMPLL_D6, CLK_APMIXED_MMPLL, 1, 6, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MMPLL_D7, CLK_APMIXED_MMPLL, 1, 7, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MFGPLL_CK, CLK_APMIXED_MFGPLL, 1, 1, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MSDCPLL_CK, CLK_APMIXED_MSDCPLL, 1,
+ 1, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MSDCPLL_D2, CLK_APMIXED_MSDCPLL, 1,
+ 2, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MSDCPLL_D4, CLK_APMIXED_MSDCPLL, 1,
+ 4, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MSDCPLL_D8, CLK_APMIXED_MSDCPLL, 1,
+ 8, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_MSDCPLL_D16, CLK_APMIXED_MSDCPLL, 1,
+ 16, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_AD_OSC_CK, CLK_TOP_ULPOSC, 1, 1, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_OSC_D2, CLK_TOP_ULPOSC, 1, 2, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_OSC_D4, CLK_TOP_ULPOSC, 1, 4, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_OSC_D8, CLK_TOP_ULPOSC, 1, 8, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_OSC_D16, CLK_TOP_ULPOSC, 1, 16, CLK_PARENT_TOPCKGEN),
+ FACTOR(CLK_TOP_UNIVPLL, CLK_APMIXED_UNIV2PLL, 1, 2, CLK_PARENT_APMIXED),
+ FACTOR(CLK_TOP_UNIVPLL_D3_D16, CLK_TOP_UNIVPLL_D3, 1,
+ 16, CLK_PARENT_TOPCKGEN),
+};
+
+static const int axi_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D4,
+ CLK_TOP_SYSPLL_D7,
+ CLK_TOP_OSC_D4
+};
+
+static const int mm_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MMPLL_D7,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_SYSPLL_D3_D2
+};
+
+static const int img_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MMPLL_D6,
+ CLK_TOP_UNIVPLL_D3,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_SYSPLL_D3_D2
+};
+
+static const int cam_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2,
+ CLK_TOP_MMPLL_D6,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_MMPLL_D7,
+ CLK_TOP_UNIVPLL_D3,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_SYSPLL_D3_D2,
+ CLK_TOP_UNIVPLL_D3_D2
+};
+
+static const int dsp_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MMPLL_D6,
+ CLK_TOP_MMPLL_D7,
+ CLK_TOP_UNIVPLL_D3,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_SYSPLL_D3_D2
+};
+
+static const int dsp1_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MMPLL_D6,
+ CLK_TOP_MMPLL_D7,
+ CLK_TOP_UNIVPLL_D3,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_SYSPLL_D3_D2
+};
+
+static const int dsp2_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MMPLL_D6,
+ CLK_TOP_MMPLL_D7,
+ CLK_TOP_UNIVPLL_D3,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_SYSPLL_D3_D2
+};
+
+static const int ipu_if_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MMPLL_D6,
+ CLK_TOP_MMPLL_D7,
+ CLK_TOP_UNIVPLL_D3,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_SYSPLL_D3_D2
+};
+
+static const int mfg_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MFGPLL_CK,
+ CLK_TOP_UNIVPLL_D3,
+ CLK_TOP_SYSPLL_D3
+};
+
+static const int f52m_mfg_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_UNIVPLL_D3_D4,
+ CLK_TOP_UNIVPLL_D3_D8
+};
+
+static const int camtg_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVP_192M_D8,
+ CLK_TOP_UNIVPLL_D3_D8,
+ CLK_TOP_UNIVP_192M_D4,
+ CLK_TOP_UNIVPLL_D3_D16,
+ CLK_TOP_F26M_CK_D2,
+ CLK_TOP_UNIVP_192M_D16,
+ CLK_TOP_UNIVP_192M_D32
+};
+
+static const int camtg2_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVP_192M_D8,
+ CLK_TOP_UNIVPLL_D3_D8,
+ CLK_TOP_UNIVP_192M_D4,
+ CLK_TOP_UNIVPLL_D3_D16,
+ CLK_TOP_F26M_CK_D2,
+ CLK_TOP_UNIVP_192M_D16,
+ CLK_TOP_UNIVP_192M_D32
+};
+
+static const int camtg3_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVP_192M_D8,
+ CLK_TOP_UNIVPLL_D3_D8,
+ CLK_TOP_UNIVP_192M_D4,
+ CLK_TOP_UNIVPLL_D3_D16,
+ CLK_TOP_F26M_CK_D2,
+ CLK_TOP_UNIVP_192M_D16,
+ CLK_TOP_UNIVP_192M_D32
+};
+
+static const int camtg4_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVP_192M_D8,
+ CLK_TOP_UNIVPLL_D3_D8,
+ CLK_TOP_UNIVP_192M_D4,
+ CLK_TOP_UNIVPLL_D3_D16,
+ CLK_TOP_F26M_CK_D2,
+ CLK_TOP_UNIVP_192M_D16,
+ CLK_TOP_UNIVP_192M_D32
+};
+
+static const int uart_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D3_D8
+};
+
+static const int spi_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D5_D2,
+ CLK_TOP_SYSPLL_D3_D4,
+ CLK_TOP_MSDCPLL_D4
+};
+
+static const int msdc50_hclk_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_SYSPLL_D3_D2
+};
+
+static const int msdc50_0_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_MSDCPLL_CK,
+ CLK_TOP_MSDCPLL_D2,
+ CLK_TOP_UNIVPLL_D2_D4,
+ CLK_TOP_SYSPLL_D3_D2,
+ CLK_TOP_UNIVPLL_D2_D2
+};
+
+static const int msdc30_1_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_SYSPLL_D3_D2,
+ CLK_TOP_SYSPLL_D7,
+ CLK_TOP_MSDCPLL_D2
+};
+
+static const int msdc30_2_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_SYSPLL_D3_D2,
+ CLK_TOP_SYSPLL_D7,
+ CLK_TOP_MSDCPLL_D2
+};
+
+static const int audio_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D5_D4,
+ CLK_TOP_SYSPLL_D7_D4,
+ CLK_TOP_SYSPLL_D2_D16
+};
+
+static const int aud_intbus_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D4,
+ CLK_TOP_SYSPLL_D7_D2
+};
+
+static const int pmicspi_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D8,
+ CLK_TOP_OSC_D8
+};
+
+static const int fpwrap_ulposc_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_OSC_D16,
+ CLK_TOP_OSC_D4,
+ CLK_TOP_OSC_D8
+};
+
+static const int atb_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_SYSPLL_D5
+};
+
+static const int sspm_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D2_D4,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D3
+};
+
+static const int dpi0_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_TVDPLL_D2,
+ CLK_TOP_TVDPLL_D4,
+ CLK_TOP_TVDPLL_D8,
+ CLK_TOP_TVDPLL_D16,
+ CLK_TOP_UNIVPLL_D5_D2,
+ CLK_TOP_UNIVPLL_D3_D4,
+ CLK_TOP_SYSPLL_D3_D4,
+ CLK_TOP_UNIVPLL_D3_D8
+};
+
+static const int scam_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D5_D2
+};
+
+static const int disppwm_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D3_D4,
+ CLK_TOP_OSC_D2,
+ CLK_TOP_OSC_D4,
+ CLK_TOP_OSC_D16
+};
+
+static const int usb_top_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D5_D4,
+ CLK_TOP_UNIVPLL_D3_D4,
+ CLK_TOP_UNIVPLL_D5_D2
+};
+
+static const int ssusb_top_xhci_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D5_D4,
+ CLK_TOP_UNIVPLL_D3_D4,
+ CLK_TOP_UNIVPLL_D5_D2
+};
+
+static const int spm_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D8
+};
+
+static const int i2c_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D8,
+ CLK_TOP_UNIVPLL_D5_D2
+};
+
+static const int scp_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D2_D8,
+ CLK_TOP_SYSPLL_D5,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_UNIVPLL_D3
+};
+
+static const int seninf_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_UNIVPLL_D2_D2,
+ CLK_TOP_UNIVPLL_D3_D2,
+ CLK_TOP_UNIVPLL_D2_D4
+};
+
+static const int dxcc_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_SYSPLL_D2_D4,
+ CLK_TOP_SYSPLL_D2_D8
+};
+
+static const int aud_engen1_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_APLL1_D2,
+ CLK_TOP_APLL1_D4,
+ CLK_TOP_APLL1_D8
+};
+
+static const int aud_engen2_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_APLL2_D2,
+ CLK_TOP_APLL2_D4,
+ CLK_TOP_APLL2_D8
+};
+
+static const int faes_ufsfde_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2,
+ CLK_TOP_SYSPLL_D2_D2,
+ CLK_TOP_SYSPLL_D3,
+ CLK_TOP_SYSPLL_D2_D4,
+ CLK_TOP_UNIVPLL_D3
+};
+
+static const int fufs_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_SYSPLL_D2_D4,
+ CLK_TOP_SYSPLL_D2_D8,
+ CLK_TOP_SYSPLL_D2_D16
+};
+
+static const int aud_1_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_APLL1_CK
+};
+
+static const int aud_2_parents[] = {
+ CLK_TOP_CLK26M,
+ CLK_TOP_APLL2_CK
+};
+
+static const struct mtk_composite top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX(CLK_TOP_MUX_AXI, axi_parents, 0x40, 0, 2),
+ MUX(CLK_TOP_MUX_MM, mm_parents, 0x40, 8, 3),
+ MUX(CLK_TOP_MUX_IMG, img_parents, 0x40, 16, 3),
+ MUX(CLK_TOP_MUX_CAM, cam_parents, 0x40, 24, 4),
+ /* CLK_CFG_1 */
+ MUX(CLK_TOP_MUX_DSP, dsp_parents, 0x50, 0, 4),
+ MUX(CLK_TOP_MUX_DSP1, dsp1_parents, 0x50, 8, 4),
+ MUX(CLK_TOP_MUX_DSP2, dsp2_parents, 0x50, 16, 4),
+ MUX(CLK_TOP_MUX_IPU_IF, ipu_if_parents, 0x50, 24, 4),
+ /* CLK_CFG_2 */
+ MUX(CLK_TOP_MUX_MFG, mfg_parents, 0x60, 0, 2),
+ MUX(CLK_TOP_MUX_F52M_MFG, f52m_mfg_parents, 0x60, 8, 2),
+ MUX(CLK_TOP_MUX_CAMTG, camtg_parents, 0x60, 16, 3),
+ MUX(CLK_TOP_MUX_CAMTG2, camtg2_parents, 0x60, 24, 3),
+ /* CLK_CFG_3 */
+ MUX(CLK_TOP_MUX_CAMTG3, camtg3_parents, 0x70, 0, 3),
+ MUX(CLK_TOP_MUX_CAMTG4, camtg4_parents, 0x70, 8, 3),
+ MUX(CLK_TOP_MUX_UART, uart_parents, 0x70, 16, 1),
+ MUX(CLK_TOP_MUX_SPI, spi_parents, 0x70, 24, 2),
+ /* CLK_CFG_4 */
+ MUX(CLK_TOP_MUX_MSDC50_0_HCLK, msdc50_hclk_parents, 0x80, 0, 2),
+ MUX(CLK_TOP_MUX_MSDC50_0, msdc50_0_parents, 0x80, 8, 3),
+ MUX(CLK_TOP_MUX_MSDC30_1, msdc30_1_parents, 0x80, 16, 3),
+ MUX(CLK_TOP_MUX_MSDC30_2, msdc30_2_parents, 0x80, 24, 3),
+ /* CLK_CFG_5 */
+ MUX(CLK_TOP_MUX_AUDIO, audio_parents, 0x90, 0, 2),
+ MUX(CLK_TOP_MUX_AUD_INTBUS, aud_intbus_parents, 0x90, 8, 2),
+ MUX(CLK_TOP_MUX_PMICSPI, pmicspi_parents, 0x90, 16, 2),
+ MUX(CLK_TOP_MUX_FPWRAP_ULPOSC, fpwrap_ulposc_parents, 0x90, 24, 2),
+ /* CLK_CFG_6 */
+ MUX(CLK_TOP_MUX_ATB, atb_parents, 0xa0, 0, 2),
+ MUX(CLK_TOP_MUX_SSPM, sspm_parents, 0xa0, 8, 3),
+ MUX(CLK_TOP_MUX_DPI0, dpi0_parents, 0xa0, 16, 4),
+ MUX(CLK_TOP_MUX_SCAM, scam_parents, 0xa0, 24, 1),
+ /* CLK_CFG_7 */
+ MUX(CLK_TOP_MUX_DISP_PWM, disppwm_parents, 0xb0, 0, 3),
+ MUX(CLK_TOP_MUX_USB_TOP, usb_top_parents, 0xb0, 8, 2),
+ MUX(CLK_TOP_MUX_SSUSB_TOP_XHCI, ssusb_top_xhci_parents, 0xb0, 16, 2),
+ MUX(CLK_TOP_MUX_SPM, spm_parents, 0xb0, 24, 1),
+ /* CLK_CFG_8 */
+ MUX(CLK_TOP_MUX_I2C, i2c_parents, 0xc0, 0, 2),
+ MUX(CLK_TOP_MUX_SCP, scp_parents, 0xc0, 8, 3),
+ MUX(CLK_TOP_MUX_SENINF, seninf_parents, 0xc0, 16, 2),
+ MUX(CLK_TOP_MUX_DXCC, dxcc_parents, 0xc0, 24, 2),
+ /* CLK_CFG_9 */
+ MUX(CLK_TOP_MUX_AUD_ENG1, aud_engen1_parents, 0xd0, 0, 2),
+ MUX(CLK_TOP_MUX_AUD_ENG2, aud_engen2_parents, 0xd0, 8, 2),
+ MUX(CLK_TOP_MUX_FAES_UFSFDE, faes_ufsfde_parents, 0xd0, 16, 3),
+ MUX(CLK_TOP_MUX_FUFS, fufs_parents, 0xd0, 24, 2),
+ /* CLK_CFG_10 */
+ MUX(CLK_TOP_MUX_AUD_1, aud_1_parents, 0xe0, 0, 1),
+ MUX(CLK_TOP_MUX_AUD_2, aud_2_parents, 0xe0, 8, 1),
+};
+
+static const struct mtk_clk_tree mt8183_clk_tree = {
+ .xtal_rate = 26 * MHZ,
+ .xtal2_rate = 26 * MHZ,
+ .fdivs_offs = CLK_TOP_CLK13M,
+ .muxes_offs = CLK_TOP_MUX_AXI,
+ .plls = apmixed_plls,
+ .fclks = top_fixed_clks,
+ .fdivs = top_fixed_divs,
+ .muxes = top_muxes,
+};
+
+static const struct mtk_gate_regs infra0_cg_regs = {
+ .set_ofs = 0x80,
+ .clr_ofs = 0x84,
+ .sta_ofs = 0x90,
+};
+
+static const struct mtk_gate_regs infra1_cg_regs = {
+ .set_ofs = 0x88,
+ .clr_ofs = 0x8c,
+ .sta_ofs = 0x94,
+};
+
+static const struct mtk_gate_regs infra2_cg_regs = {
+ .set_ofs = 0xa4,
+ .clr_ofs = 0xa8,
+ .sta_ofs = 0xac,
+};
+
+static const struct mtk_gate_regs infra3_cg_regs = {
+ .set_ofs = 0xc0,
+ .clr_ofs = 0xc4,
+ .sta_ofs = 0xc8,
+};
+
+#define GATE_INFRA0(_id, _parent, _shift) { \
+ .id = _id, \
+ .parent = _parent, \
+ .regs = &infra0_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \
+ }
+
+#define GATE_INFRA1(_id, _parent, _shift) { \
+ .id = _id, \
+ .parent = _parent, \
+ .regs = &infra1_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \
+ }
+
+#define GATE_INFRA2(_id, _parent, _shift) { \
+ .id = _id, \
+ .parent = _parent, \
+ .regs = &infra2_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \
+ }
+
+#define GATE_INFRA3(_id, _parent, _shift) { \
+ .id = _id, \
+ .parent = _parent, \
+ .regs = &infra3_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \
+ }
+
+static const struct mtk_gate infra_clks[] = {
+ /* INFRA0 */
+ GATE_INFRA0(CLK_INFRA_PMIC_TMR, CLK_TOP_MUX_AXI, 0),
+ GATE_INFRA0(CLK_INFRA_PMIC_AP, CLK_TOP_MUX_AXI, 1),
+ GATE_INFRA0(CLK_INFRA_PMIC_MD, CLK_TOP_MUX_AXI, 2),
+ GATE_INFRA0(CLK_INFRA_PMIC_CONN, CLK_TOP_MUX_AXI, 3),
+ GATE_INFRA0(CLK_INFRA_SCPSYS, CLK_TOP_MUX_SCP, 4),
+ GATE_INFRA0(CLK_INFRA_SEJ, CLK_TOP_CLK26M, 5),
+ GATE_INFRA0(CLK_INFRA_APXGPT, CLK_TOP_MUX_AXI, 6),
+ GATE_INFRA0(CLK_INFRA_ICUSB, CLK_TOP_MUX_AXI, 8),
+ GATE_INFRA0(CLK_INFRA_GCE, CLK_TOP_MUX_AXI, 9),
+ GATE_INFRA0(CLK_INFRA_THERM, CLK_TOP_MUX_AXI, 10),
+ GATE_INFRA0(CLK_INFRA_I2C0, CLK_TOP_MUX_I2C, 11),
+ GATE_INFRA0(CLK_INFRA_I2C1, CLK_TOP_MUX_I2C, 12),
+ GATE_INFRA0(CLK_INFRA_I2C2, CLK_TOP_MUX_I2C, 13),
+ GATE_INFRA0(CLK_INFRA_I2C3, CLK_TOP_MUX_I2C, 14),
+ GATE_INFRA0(CLK_INFRA_PWM_HCLK, CLK_TOP_MUX_AXI, 15),
+ GATE_INFRA0(CLK_INFRA_PWM1, CLK_TOP_MUX_I2C, 16),
+ GATE_INFRA0(CLK_INFRA_PWM2, CLK_TOP_MUX_I2C, 17),
+ GATE_INFRA0(CLK_INFRA_PWM3, CLK_TOP_MUX_I2C, 18),
+ GATE_INFRA0(CLK_INFRA_PWM4, CLK_TOP_MUX_I2C, 19),
+ GATE_INFRA0(CLK_INFRA_PWM, CLK_TOP_MUX_I2C, 21),
+ GATE_INFRA0(CLK_INFRA_UART0, CLK_TOP_MUX_UART, 22),
+ GATE_INFRA0(CLK_INFRA_UART1, CLK_TOP_MUX_UART, 23),
+ GATE_INFRA0(CLK_INFRA_UART2, CLK_TOP_MUX_UART, 24),
+ GATE_INFRA0(CLK_INFRA_UART3, CLK_TOP_MUX_UART, 25),
+ GATE_INFRA0(CLK_INFRA_GCE_26M, CLK_TOP_MUX_AXI, 27),
+ GATE_INFRA0(CLK_INFRA_CQ_DMA_FPC, CLK_TOP_MUX_AXI, 28),
+ GATE_INFRA0(CLK_INFRA_BTIF, CLK_TOP_MUX_AXI, 31),
+ /* INFRA1 */
+ GATE_INFRA1(CLK_INFRA_SPI0, CLK_TOP_MUX_SPI, 1),
+ GATE_INFRA1(CLK_INFRA_MSDC0, CLK_TOP_MUX_MSDC50_0_HCLK, 2),
+ GATE_INFRA1(CLK_INFRA_MSDC1, CLK_TOP_MUX_AXI, 4),
+ GATE_INFRA1(CLK_INFRA_MSDC2, CLK_TOP_MUX_AXI, 5),
+ GATE_INFRA1(CLK_INFRA_MSDC0_SCK, CLK_TOP_MUX_MSDC50_0, 6),
+ GATE_INFRA1(CLK_INFRA_DVFSRC, CLK_TOP_CLK26M, 7),
+ GATE_INFRA1(CLK_INFRA_GCPU, CLK_TOP_MUX_AXI, 8),
+ GATE_INFRA1(CLK_INFRA_TRNG, CLK_TOP_MUX_AXI, 9),
+ GATE_INFRA1(CLK_INFRA_AUXADC, CLK_TOP_CLK26M, 10),
+ GATE_INFRA1(CLK_INFRA_CPUM, CLK_TOP_MUX_AXI, 11),
+ GATE_INFRA1(CLK_INFRA_CCIF1_AP, CLK_TOP_MUX_AXI, 12),
+ GATE_INFRA1(CLK_INFRA_CCIF1_MD, CLK_TOP_MUX_AXI, 13),
+ GATE_INFRA1(CLK_INFRA_AUXADC_MD, CLK_TOP_CLK26M, 14),
+ GATE_INFRA1(CLK_INFRA_MSDC1_SCK, CLK_TOP_MUX_MSDC30_1, 16),
+ GATE_INFRA1(CLK_INFRA_MSDC2_SCK, CLK_TOP_MUX_MSDC30_2, 17),
+ GATE_INFRA1(CLK_INFRA_AP_DMA, CLK_TOP_MUX_AXI, 18),
+ GATE_INFRA1(CLK_INFRA_XIU, CLK_TOP_MUX_AXI, 19),
+ GATE_INFRA1(CLK_INFRA_DEVICE_APC, CLK_TOP_MUX_AXI, 20),
+ GATE_INFRA1(CLK_INFRA_CCIF_AP, CLK_TOP_MUX_AXI, 23),
+ GATE_INFRA1(CLK_INFRA_DEBUGSYS, CLK_TOP_MUX_AXI, 24),
+ GATE_INFRA1(CLK_INFRA_AUDIO, CLK_TOP_MUX_AXI, 25),
+ GATE_INFRA1(CLK_INFRA_CCIF_MD, CLK_TOP_MUX_AXI, 26),
+ GATE_INFRA1(CLK_INFRA_DXCC_SEC_CORE, CLK_TOP_MUX_DXCC, 27),
+ GATE_INFRA1(CLK_INFRA_DXCC_AO, CLK_TOP_MUX_DXCC, 28),
+ GATE_INFRA1(CLK_INFRA_DEVMPU_BCLK, CLK_TOP_MUX_AXI, 30),
+ GATE_INFRA1(CLK_INFRA_DRAMC_F26M, CLK_TOP_CLK26M, 31),
+ /* INFRA2 */
+ GATE_INFRA2(CLK_INFRA_IRTX, CLK_TOP_CLK26M, 0),
+ GATE_INFRA2(CLK_INFRA_USB, CLK_TOP_MUX_USB_TOP, 1),
+ GATE_INFRA2(CLK_INFRA_DISP_PWM, CLK_TOP_MUX_AXI, 2),
+ GATE_INFRA2(CLK_INFRA_CLDMA_BCLK, CLK_TOP_MUX_AXI, 3),
+ GATE_INFRA2(CLK_INFRA_AUDIO_26M_BCLK, CLK_TOP_CLK26M, 4),
+ GATE_INFRA2(CLK_INFRA_SPI1, CLK_TOP_MUX_SPI, 6),
+ GATE_INFRA2(CLK_INFRA_I2C4, CLK_TOP_MUX_I2C, 7),
+ GATE_INFRA2(CLK_INFRA_MODEM_TEMP_SHARE, CLK_TOP_CLK26M, 8),
+ GATE_INFRA2(CLK_INFRA_SPI2, CLK_TOP_MUX_SPI, 9),
+ GATE_INFRA2(CLK_INFRA_SPI3, CLK_TOP_MUX_SPI, 10),
+ GATE_INFRA2(CLK_INFRA_UNIPRO_SCK, CLK_TOP_MUX_SSUSB_TOP_XHCI, 11),
+ GATE_INFRA2(CLK_INFRA_UNIPRO_TICK, CLK_TOP_MUX_FUFS, 12),
+ GATE_INFRA2(CLK_INFRA_UFS_MP_SAP_BCLK, CLK_TOP_MUX_FUFS, 13),
+ GATE_INFRA2(CLK_INFRA_MD32_BCLK, CLK_TOP_MUX_AXI, 14),
+ GATE_INFRA2(CLK_INFRA_UNIPRO_MBIST, CLK_TOP_MUX_AXI, 16),
+ GATE_INFRA2(CLK_INFRA_I2C5, CLK_TOP_MUX_I2C, 18),
+ GATE_INFRA2(CLK_INFRA_I2C5_ARBITER, CLK_TOP_MUX_I2C, 19),
+ GATE_INFRA2(CLK_INFRA_I2C5_IMM, CLK_TOP_MUX_I2C, 20),
+ GATE_INFRA2(CLK_INFRA_I2C1_ARBITER, CLK_TOP_MUX_I2C, 21),
+ GATE_INFRA2(CLK_INFRA_I2C1_IMM, CLK_TOP_MUX_I2C, 22),
+ GATE_INFRA2(CLK_INFRA_I2C2_ARBITER, CLK_TOP_MUX_I2C, 23),
+ GATE_INFRA2(CLK_INFRA_I2C2_IMM, CLK_TOP_MUX_I2C, 24),
+ GATE_INFRA2(CLK_INFRA_SPI4, CLK_TOP_MUX_SPI, 25),
+ GATE_INFRA2(CLK_INFRA_SPI5, CLK_TOP_MUX_SPI, 26),
+ GATE_INFRA2(CLK_INFRA_CQ_DMA, CLK_TOP_MUX_AXI, 27),
+ GATE_INFRA2(CLK_INFRA_UFS, CLK_TOP_MUX_FUFS, 28),
+ GATE_INFRA2(CLK_INFRA_AES_UFSFDE, CLK_TOP_MUX_FAES_UFSFDE, 29),
+ GATE_INFRA2(CLK_INFRA_UFS_TICK, CLK_TOP_MUX_FUFS, 30),
+ /* INFRA3 */
+ GATE_INFRA3(CLK_INFRA_MSDC0_SELF, CLK_TOP_MUX_MSDC50_0, 0),
+ GATE_INFRA3(CLK_INFRA_MSDC1_SELF, CLK_TOP_MUX_MSDC50_0, 1),
+ GATE_INFRA3(CLK_INFRA_MSDC2_SELF, CLK_TOP_MUX_MSDC50_0, 2),
+ GATE_INFRA3(CLK_INFRA_UFS_AXI, CLK_TOP_MUX_AXI, 5),
+ GATE_INFRA3(CLK_INFRA_I2C6, CLK_TOP_MUX_I2C, 6),
+ GATE_INFRA3(CLK_INFRA_AP_MSDC0, CLK_TOP_MUX_MSDC50_0_HCLK, 7),
+ GATE_INFRA3(CLK_INFRA_MD_MSDC0, CLK_TOP_MUX_MSDC50_0_HCLK, 8),
+ GATE_INFRA3(CLK_INFRA_CCIF2_AP, CLK_TOP_MUX_AXI, 16),
+ GATE_INFRA3(CLK_INFRA_CCIF2_MD, CLK_TOP_MUX_AXI, 17),
+ GATE_INFRA3(CLK_INFRA_CCIF3_AP, CLK_TOP_MUX_AXI, 18),
+ GATE_INFRA3(CLK_INFRA_CCIF3_MD, CLK_TOP_MUX_AXI, 19),
+ GATE_INFRA3(CLK_INFRA_SEJ_F13M, CLK_TOP_CLK26M, 20),
+ GATE_INFRA3(CLK_INFRA_AES_BCLK, CLK_TOP_MUX_AXI, 21),
+ GATE_INFRA3(CLK_INFRA_I2C7, CLK_TOP_MUX_I2C, 22),
+ GATE_INFRA3(CLK_INFRA_I2C8, CLK_TOP_MUX_I2C, 23),
+ GATE_INFRA3(CLK_INFRA_FBIST2FPC, CLK_TOP_MUX_MSDC50_0, 24),
+};
+
+static int mt8183_apmixedsys_probe(struct udevice *dev)
+{
+ return mtk_common_clk_init(dev, &mt8183_clk_tree);
+}
+
+static int mt8183_topckgen_probe(struct udevice *dev)
+{
+ return mtk_common_clk_init(dev, &mt8183_clk_tree);
+}
+
+static int mt8183_infracfg_probe(struct udevice *dev)
+{
+ return mtk_common_clk_gate_init(dev, &mt8183_clk_tree, infra_clks);
+}
+
+static const struct udevice_id mt8183_apmixed_compat[] = {
+ { .compatible = "mediatek,mt8183-apmixedsys", },
+ { }
+};
+
+static const struct udevice_id mt8183_topckgen_compat[] = {
+ { .compatible = "mediatek,mt8183-topckgen", },
+ { }
+};
+
+static const struct udevice_id mt8183_infracfg_compat[] = {
+ { .compatible = "mediatek,mt8183-infracfg", },
+ { }
+};
+
+U_BOOT_DRIVER(mtk_clk_apmixedsys) = {
+ .name = "mt8183-apmixedsys",
+ .id = UCLASS_CLK,
+ .of_match = mt8183_apmixed_compat,
+ .probe = mt8183_apmixedsys_probe,
+ .priv_auto = sizeof(struct mtk_clk_priv),
+ .ops = &mtk_clk_apmixedsys_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DRIVER(mtk_clk_topckgen) = {
+ .name = "mt8183-topckgen",
+ .id = UCLASS_CLK,
+ .of_match = mt8183_topckgen_compat,
+ .probe = mt8183_topckgen_probe,
+ .priv_auto = sizeof(struct mtk_clk_priv),
+ .ops = &mtk_clk_topckgen_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DRIVER(mtk_clk_infracfg) = {
+ .name = "mt8183-infracfg",
+ .id = UCLASS_CLK,
+ .of_match = mt8183_infracfg_compat,
+ .probe = mt8183_infracfg_probe,
+ .priv_auto = sizeof(struct mtk_clk_priv),
+ .ops = &mtk_clk_gate_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/dma/bcm6348-iudma.c b/drivers/dma/bcm6348-iudma.c
index 98577601b58..c04aa55cb42 100644
--- a/drivers/dma/bcm6348-iudma.c
+++ b/drivers/dma/bcm6348-iudma.c
@@ -313,10 +313,10 @@ static int bcm6348_iudma_request(struct dma *dma)
ch_priv->desc_id = 0;
if (bcm6348_iudma_chan_is_rx(dma->id)) {
ch_priv->desc_cnt = 0;
- ch_priv->busy_desc = calloc(ch_priv->desc_cnt, sizeof(bool));
+ ch_priv->busy_desc = NULL;
} else {
ch_priv->desc_cnt = ch_priv->dma_ring_size;
- ch_priv->busy_desc = NULL;
+ ch_priv->busy_desc = calloc(ch_priv->desc_cnt, sizeof(bool));
}
return 0;
diff --git a/drivers/mmc/fsl_esdhc_spl.c b/drivers/mmc/fsl_esdhc_spl.c
index afe55fad9de..bee76572ac6 100644
--- a/drivers/mmc/fsl_esdhc_spl.c
+++ b/drivers/mmc/fsl_esdhc_spl.c
@@ -91,20 +91,17 @@ void __noreturn mmc_boot(void)
CONFIG_CFG_DATA_SECTOR, 1, tmp_buf);
if (err != 1) {
puts("spl: mmc read failed!!\n");
- free(tmp_buf);
hang();
}
val = *(tmp_buf + MBRDBR_BOOT_SIG_55);
if (0x55 != val) {
puts("spl: mmc signature is not valid!!\n");
- free(tmp_buf);
hang();
}
val = *(tmp_buf + MBRDBR_BOOT_SIG_AA);
if (0xAA != val) {
puts("spl: mmc signature is not valid!!\n");
- free(tmp_buf);
hang();
}
diff --git a/drivers/mmc/pci_mmc.c b/drivers/mmc/pci_mmc.c
index c71c495d581..b26eb034d07 100644
--- a/drivers/mmc/pci_mmc.c
+++ b/drivers/mmc/pci_mmc.c
@@ -52,9 +52,11 @@ static int pci_mmc_probe(struct udevice *dev)
static int pci_mmc_of_to_plat(struct udevice *dev)
{
- struct pci_mmc_priv *priv = dev_get_priv(dev);
+ if (CONFIG_IS_ENABLED(DM_GPIO)) {
+ struct pci_mmc_priv *priv = dev_get_priv(dev);
- gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
+ gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
+ }
return 0;
}
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 318788c5e2f..6d77ebfaa53 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -11,7 +11,6 @@ mtd-$(CONFIG_ALTERA_QSPI) += altera_qspi.o
mtd-$(CONFIG_FLASH_CFI_DRIVER) += cfi_flash.o
mtd-$(CONFIG_FLASH_CFI_MTD) += cfi_mtd.o
mtd-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o
-mtd-$(CONFIG_MW_EEPROM) += mw_eeprom.o
mtd-$(CONFIG_FLASH_PIC32) += pic32_flash.o
mtd-$(CONFIG_ST_SMI) += st_smi.o
mtd-$(CONFIG_STM32_FLASH) += stm32_flash.o
diff --git a/drivers/mtd/mw_eeprom.c b/drivers/mtd/mw_eeprom.c
deleted file mode 100644
index 9837733beee..00000000000
--- a/drivers/mtd/mw_eeprom.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */
-
-#include <common.h>
-#include <eeprom.h>
-#include <asm/ic/ssi.h>
-#include <linux/delay.h>
-
-/*
- * Serial EEPROM opcodes, including start bit
- */
-#define EEP_OPC_ERASE 0x7 /* 3-bit opcode */
-#define EEP_OPC_WRITE 0x5 /* 3-bit opcode */
-#define EEP_OPC_READ 0x6 /* 3-bit opcode */
-
-#define EEP_OPC_ERASE_ALL 0x12 /* 5-bit opcode */
-#define EEP_OPC_ERASE_EN 0x13 /* 5-bit opcode */
-#define EEP_OPC_WRITE_ALL 0x11 /* 5-bit opcode */
-#define EEP_OPC_ERASE_DIS 0x10 /* 5-bit opcode */
-
-static int addrlen;
-
-static void mw_eeprom_select(int dev)
-{
- ssi_set_interface(2048, 0, 0, 0);
- ssi_chip_select(0);
- udelay(1);
- ssi_chip_select(dev);
- udelay(1);
-}
-
-static int mw_eeprom_size(int dev)
-{
- int x;
- u16 res;
-
- mw_eeprom_select(dev);
- ssi_tx_byte(EEP_OPC_READ);
-
- res = ssi_txrx_byte(0) << 8;
- res |= ssi_rx_byte();
- for (x = 0; x < 16; x++) {
- if (! (res & 0x8000)) {
- break;
- }
- res <<= 1;
- }
- ssi_chip_select(0);
-
- return x;
-}
-
-int mw_eeprom_erase_enable(int dev)
-{
- mw_eeprom_select(dev);
- ssi_tx_byte(EEP_OPC_ERASE_EN);
- ssi_tx_byte(0);
- udelay(1);
- ssi_chip_select(0);
-
- return 0;
-}
-
-int mw_eeprom_erase_disable(int dev)
-{
- mw_eeprom_select(dev);
- ssi_tx_byte(EEP_OPC_ERASE_DIS);
- ssi_tx_byte(0);
- udelay(1);
- ssi_chip_select(0);
-
- return 0;
-}
-
-
-u32 mw_eeprom_read_word(int dev, int addr)
-{
- u16 rcv;
- u16 res;
- int bits;
-
- mw_eeprom_select(dev);
- ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f));
- rcv = ssi_txrx_byte(addr << (13 - addrlen));
- res = rcv << (16 - addrlen);
- bits = 4 + addrlen;
-
- while (bits>0) {
- rcv = ssi_rx_byte();
- if (bits > 7) {
- res |= rcv << (bits - 8);
- } else {
- res |= rcv >> (8 - bits);
- }
- bits -= 8;
- }
-
- ssi_chip_select(0);
-
- return res;
-}
-
-int mw_eeprom_write_word(int dev, int addr, u16 data)
-{
- u8 byte1=0;
- u8 byte2=0;
-
- mw_eeprom_erase_enable(dev);
- mw_eeprom_select(dev);
-
- switch (addrlen) {
- case 6:
- byte1 = EEP_OPC_WRITE >> 2;
- byte2 = (EEP_OPC_WRITE << 6)&0xc0;
- byte2 |= addr;
- break;
- case 7:
- byte1 = EEP_OPC_WRITE >> 1;
- byte2 = (EEP_OPC_WRITE << 7)&0x80;
- byte2 |= addr;
- break;
- case 8:
- byte1 = EEP_OPC_WRITE;
- byte2 = addr;
- break;
- case 9:
- byte1 = EEP_OPC_WRITE << 1;
- byte1 |= addr >> 8;
- byte2 = addr & 0xff;
- break;
- case 10:
- byte1 = EEP_OPC_WRITE << 2;
- byte1 |= addr >> 8;
- byte2 = addr & 0xff;
- break;
- default:
- printf("Unsupported number of address bits: %d\n", addrlen);
- return -1;
-
- }
-
- ssi_tx_byte(byte1);
- ssi_tx_byte(byte2);
- ssi_tx_byte(data >> 8);
- ssi_tx_byte(data & 0xff);
- ssi_chip_select(0);
- udelay(10000); /* Worst case */
- mw_eeprom_erase_disable(dev);
-
- return 0;
-}
-
-
-int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
-{
- int done;
-
- done = 0;
- if (addr & 1) {
- u16 temp = mw_eeprom_read_word(dev, addr >> 1);
- temp &= 0xff00;
- temp |= buffer[0];
-
- mw_eeprom_write_word(dev, addr >> 1, temp);
- len--;
- addr++;
- buffer++;
- done++;
- }
-
- while (len <= 2) {
- mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer);
- len-=2;
- addr+=2;
- buffer+=2;
- done+=2;
- }
-
- if (len) {
- u16 temp = mw_eeprom_read_word(dev, addr >> 1);
- temp &= 0x00ff;
- temp |= buffer[0] << 8;
-
- mw_eeprom_write_word(dev, addr >> 1, temp);
- len--;
- addr++;
- buffer++;
- done++;
- }
-
- return done;
-}
-
-
-int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
-{
- int done;
-
- done = 0;
- if (addr & 1) {
- u16 temp = mw_eeprom_read_word(dev, addr >> 1);
- buffer[0]= temp & 0xff;
-
- len--;
- addr++;
- buffer++;
- done++;
- }
-
- while (len <= 2) {
- *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1);
- len-=2;
- addr+=2;
- buffer+=2;
- done+=2;
- }
-
- if (len) {
- u16 temp = mw_eeprom_read_word(dev, addr >> 1);
- buffer[0] = temp >> 8;
-
- len--;
- addr++;
- buffer++;
- done++;
- }
-
- return done;
-}
-
-int mw_eeprom_probe(int dev)
-{
- addrlen = mw_eeprom_size(dev);
-
- if (addrlen < 6 || addrlen > 10) {
- return -1;
- }
- return 0;
-}
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 3cf3b14f05b..ed151ee0a5c 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -321,6 +321,18 @@ config NAND_STM32_FMC2
The controller supports a maximum 8k page size and supports
a maximum 8-bit correction error per sector of 512 bytes.
+config CORTINA_NAND
+ bool "Support for NAND controller on Cortina-Access SoCs"
+ depends on CORTINA_PLATFORM
+ select SYS_NAND_SELF_INIT
+ select DM_MTD
+ imply CMD_NAND
+ help
+ Enables support for NAND Flash chips on Coartina-Access SoCs platform
+ This controller is found on Presidio/Venus SoCs.
+ The controller supports a maximum 8k page size and supports
+ a maximum 40-bit error correction per sector of 1024 bytes.
+
comment "Generic NAND options"
config SYS_NAND_BLOCK_SIZE
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 24c51b6924a..f3f0e15a157 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
obj-$(CONFIG_NAND_STM32_FMC2) += stm32_fmc2_nand.o
+obj-$(CONFIG_CORTINA_NAND) += cortina_nand.o
else # minimal SPL drivers
diff --git a/drivers/mtd/nand/raw/cortina_nand.c b/drivers/mtd/nand/raw/cortina_nand.c
new file mode 100644
index 00000000000..12bd1ded83f
--- /dev/null
+++ b/drivers/mtd/nand/raw/cortina_nand.c
@@ -0,0 +1,1390 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Cortina Access Inc..
+ */
+
+#include <common.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/sizes.h>
+#include <log.h>
+#include <asm/io.h>
+#include <memalign.h>
+#include <nand.h>
+#include <dm/device_compat.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/errno.h>
+#include <asm/gpio.h>
+#include <fdtdec.h>
+#include <bouncebuf.h>
+#include <dm.h>
+#include "cortina_nand.h"
+
+static unsigned int *pread, *pwrite;
+
+static const struct udevice_id cortina_nand_dt_ids[] = {
+ {
+ .compatible = "cortina,ca-nand",
+ },
+ { /* sentinel */ }
+};
+
+static struct nand_ecclayout eccoob;
+
+/* Information about an attached NAND chip */
+struct fdt_nand {
+ int enabled; /* 1 to enable, 0 to disable */
+ s32 width; /* bit width, must be 8 */
+ u32 nand_ecc_strength;
+};
+
+struct nand_drv {
+ u32 fifo_index;
+ struct nand_ctlr *reg;
+ struct dma_global *dma_glb;
+ struct dma_ssp *dma_nand;
+ struct tx_descriptor_t *tx_desc;
+ struct rx_descriptor_t *rx_desc;
+ struct fdt_nand config;
+ unsigned int flash_base;
+};
+
+struct ca_nand_info {
+ struct udevice *dev;
+ struct nand_drv nand_ctrl;
+ struct nand_chip nand_chip;
+};
+
+/**
+ * Wait for command completion
+ *
+ * @param reg nand_ctlr structure
+ * @return
+ * 1 - Command completed
+ * 0 - Timeout
+ */
+static int nand_waitfor_cmd_completion(struct nand_ctlr *reg, unsigned int mask)
+{
+ unsigned int reg_v = 0;
+
+ if (readl_poll_timeout(&reg->flash_flash_access_start, reg_v,
+ !(reg_v & mask), (FLASH_LONG_DELAY << 2))) {
+ pr_err("Nand CMD timeout!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Read one byte from the chip
+ *
+ * @param mtd MTD device structure
+ * @return data byte
+ *
+ * Read function for 8bit bus-width
+ */
+static uint8_t read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info;
+ u8 ret_v;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ clrsetbits_le32(&info->reg->flash_flash_access_start, GENMASK(31, 0),
+ NFLASH_GO | NFLASH_RD);
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("%s: Command timeout\n", __func__);
+
+ ret_v = readl(&info->reg->flash_nf_data) >> (8 * info->fifo_index++);
+ info->fifo_index %= 4;
+
+ return (uint8_t)ret_v;
+}
+
+/**
+ * Read len bytes from the chip into a buffer
+ *
+ * @param mtd MTD device structure
+ * @param buf buffer to store data to
+ * @param len number of bytes to read
+ *
+ * Read function for 8bit bus-width
+ */
+static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ unsigned int reg;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+
+ for (i = 0; i < len; i++) {
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(31, 0), NFLASH_GO | NFLASH_RD);
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("%s: Command timeout\n", __func__);
+
+ reg = readl(&info->reg->flash_nf_data) >>
+ (8 * info->fifo_index++);
+ memcpy(buf + i, &reg, 1);
+ info->fifo_index %= 4;
+ }
+}
+
+/**
+ * Check READY pin status to see if it is ready or not
+ *
+ * @param mtd MTD device structure
+ * @return
+ * 1 - ready
+ * 0 - not ready
+ */
+static int nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int reg_val;
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+
+ reg_val = readl(&info->reg->flash_status);
+ if (reg_val & NFLASH_READY)
+ return 1;
+ else
+ return 0;
+}
+
+/* Dummy implementation: we don't support multiple chips */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ switch (chipnr) {
+ case -1:
+ case 0:
+ break;
+
+ default:
+ WARN_ON(chipnr);
+ }
+}
+
+int init_nand_dma(struct nand_chip *nand)
+{
+ int i;
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(nand);
+
+ setbits_le32(&info->dma_glb->dma_glb_dma_lso_ctrl, TX_DMA_ENABLE);
+ setbits_le32(&info->dma_glb->dma_glb_dma_ssp_rx_ctrl,
+ TX_DMA_ENABLE | DMA_CHECK_OWNER);
+ setbits_le32(&info->dma_glb->dma_glb_dma_ssp_tx_ctrl,
+ RX_DMA_ENABLE | DMA_CHECK_OWNER);
+
+ info->tx_desc = malloc_cache_aligned((sizeof(struct tx_descriptor_t) *
+ CA_DMA_DESC_NUM));
+ info->rx_desc = malloc_cache_aligned((sizeof(struct rx_descriptor_t) *
+ CA_DMA_DESC_NUM));
+
+ if (!info->rx_desc && info->tx_desc) {
+ printf("Fail to alloc DMA descript!\n");
+ kfree(info->tx_desc);
+ return -ENOMEM;
+ } else if (info->rx_desc && !info->tx_desc) {
+ printf("Fail to alloc DMA descript!\n");
+ kfree(info->tx_desc);
+ return -ENOMEM;
+ }
+
+ /* set RX DMA base address and depth */
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_base_depth,
+ GENMASK(31, 4), (uintptr_t)info->rx_desc);
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_base_depth,
+ GENMASK(3, 0), CA_DMA_DEPTH);
+
+ /* set TX DMA base address and depth */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_base_depth,
+ GENMASK(31, 4), (uintptr_t)info->tx_desc);
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_base_depth,
+ GENMASK(3, 0), CA_DMA_DEPTH);
+
+ memset((unsigned char *)info->tx_desc, 0,
+ (sizeof(struct tx_descriptor_t) * CA_DMA_DESC_NUM));
+ memset((unsigned char *)info->rx_desc, 0,
+ (sizeof(struct rx_descriptor_t) * CA_DMA_DESC_NUM));
+
+ for (i = 0; i < CA_DMA_DESC_NUM; i++) {
+ /* set owner bit as SW */
+ info->tx_desc[i].own = 1;
+ /* enable Scatter-Gather memory copy */
+ info->tx_desc[i].sgm = 0x1;
+ }
+
+ return 0;
+}
+
+/**
+ * Send command to NAND device
+ *
+ * @param mtd MTD device structure
+ * @param command the command to be sent
+ * @param column the column address for this command, -1 if none
+ * @param page_addr the page address for this command, -1 if none
+ */
+static void ca_nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info;
+ unsigned int reg_v = 0;
+ u32 cmd = 0, cnt = 0, addr1 = 0, addr2 = 0;
+ int ret;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+ /*
+ * Write out the command to the device.
+ *
+ * Only command NAND_CMD_RESET or NAND_CMD_READID will come
+ * here before mtd->writesize is initialized.
+ */
+
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+ assert(mtd->writesize != 0);
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Reset FIFO before issue new command */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+ ret =
+ readl_poll_timeout(&info->reg->flash_nf_ecc_reset, reg_v,
+ !(reg_v & RESET_NFLASH_FIFO), FLASH_SHORT_DELAY);
+ if (ret) {
+ printf("FIFO reset timeout\n");
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+ udelay(10);
+ }
+
+ /* Reset FIFO index
+ * Next read start from flash_nf_data[0]
+ */
+ info->fifo_index = 0;
+
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(11, 10),
+ NFLASH_REG_WIDTH_8);
+
+ /*
+ * Program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+ case NAND_CMD_READID:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_READID);
+ /* 1 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_1TOGO);
+ /* 1 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_1);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ REG_DATA_COUNT_DATA_4);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ column & ADDR1_MASK2);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ /* clear FLASH_NF_ACCESS */
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ DISABLE_AUTO_RESET);
+
+ break;
+ case NAND_CMD_PARAM:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_PARAM);
+ /* 1 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_1TOGO);
+ /* 1 byte ADDR cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_1);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ (SZ_4K - 1) << 8);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ column & ADDR1_MASK2);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ break;
+ case NAND_CMD_READ0:
+ if (chip->chipsize < SZ_32M) {
+ cmd = NAND_CMD_READ0;
+ cnt = REG_CMD_COUNT_1TOGO | REG_ADDR_COUNT_3;
+ addr1 = (((page_addr & ADDR1_MASK0) << 8));
+ addr2 = ((page_addr & ADDR2_MASK0) >> 24);
+ } else if (chip->chipsize >= SZ_32M &&
+ (chip->chipsize <= SZ_128M)) {
+ cmd = NAND_CMD_READ0;
+ cnt = REG_ADDR_COUNT_4;
+ if (mtd->writesize > (REG_DATA_COUNT_512_DATA >> 8)) {
+ cmd |= (NAND_CMD_READSTART << 8);
+ cnt |= REG_CMD_COUNT_2TOGO;
+ } else {
+ cnt |= REG_CMD_COUNT_1TOGO;
+ }
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ } else {
+ cmd = NAND_CMD_READ0 | (NAND_CMD_READSTART << 8);
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_5;
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ }
+
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ cmd);
+ /* CMD & ADDR cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(7, 0), cnt);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ (mtd->writesize - 1) << 8);
+ /* OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ (mtd->oobsize - 1) << 22);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ addr1);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ addr2);
+
+ return;
+ case NAND_CMD_SEQIN:
+ if (chip->chipsize < SZ_32M) {
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_3;
+ addr1 = (((page_addr & ADDR1_MASK0) << 8));
+ addr2 = ((page_addr & ADDR2_MASK0) >> 24);
+ } else if (chip->chipsize >= SZ_32M &&
+ (chip->chipsize <= SZ_128M)) {
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_4;
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ } else {
+ cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_5;
+ addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+ addr2 = (page_addr >> 16);
+ }
+
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_SEQIN | (NAND_CMD_PAGEPROG << 8));
+ /* CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(7, 0), cnt);
+ /* Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ (mtd->writesize - 1) << 8);
+ /* OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ (mtd->oobsize - 1) << 22);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ addr1);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ addr2);
+
+ return;
+ case NAND_CMD_PAGEPROG:
+ return;
+ case NAND_CMD_ERASE1:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8));
+ /* 2 byte CMD cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_2TOGO);
+ /* 3 byte ADDR cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_3);
+ /* 0 Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ REG_DATA_COUNT_EMPTY);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ page_addr);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ /* Issue command */
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(31, 0), NFLASH_GO | NFLASH_RD);
+ break;
+ case NAND_CMD_ERASE2:
+ return;
+ case NAND_CMD_STATUS:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_STATUS);
+ /* 1 byte CMD cycle */
+ clrbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0));
+ /* 0 byte Addr cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_EMPTY);
+ /* 1 Data cycle */
+ clrbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8));
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ break;
+ case NAND_CMD_RESET:
+ /* Command */
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_RESET);
+ /* 1 byte CMD cycle */
+ clrbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0));
+ /* 0 byte Addr cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+ REG_ADDR_COUNT_EMPTY);
+ /* 0 Data cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+ REG_DATA_COUNT_EMPTY);
+ /* 0 OOB cycle */
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+ REG_OOB_COUNT_EMPTY);
+
+ /* addresses */
+ clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+ column & ADDR1_MASK2);
+ clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+ 0);
+
+ /* Issue command */
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(31, 0), NFLASH_GO | NFLASH_WT);
+
+ break;
+ case NAND_CMD_RNDOUT:
+ default:
+ printf("%s: Unsupported command %d\n", __func__, command);
+ return;
+ }
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("Command 0x%02X timeout\n", command);
+}
+
+/**
+ * Set up NAND bus width and page size
+ *
+ * @param info nand_info structure
+ * @return 0 if ok, -1 on error
+ */
+static int set_bus_width_page_size(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+
+ if (info->config.width == SZ_8) {
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ NFLASH_REG_WIDTH_8);
+ } else if (info->config.width == SZ_16) {
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ NFLASH_REG_WIDTH_16);
+ } else {
+ debug("%s: Unsupported bus width %d\n", __func__,
+ info->config.width);
+ return -1;
+ }
+
+ if (mtd->writesize == SZ_512) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_512);
+ } else if (mtd->writesize == SZ_2K) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_2K);
+ } else if (mtd->writesize == SZ_4K) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_4K);
+ } else if (mtd->writesize == SZ_8K) {
+ setbits_le32(&info->reg->flash_type, FLASH_TYPE_8K);
+ } else {
+ debug("%s: Unsupported page size %d\n", __func__,
+ mtd->writesize);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ca_do_bch_correction(struct nand_chip *chip,
+ unsigned int err_num, u8 *buff_ptr, int i)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ unsigned int reg_v, err_loc0, err_loc1;
+ int k, max_bitflips;
+
+ for (k = 0; k < (err_num + 1) / 2; k++) {
+ reg_v = readl(&info->reg->flash_nf_bch_error_loc01 + k);
+ err_loc0 = reg_v & BCH_ERR_LOC_MASK;
+ err_loc1 = (reg_v >> 16) & BCH_ERR_LOC_MASK;
+
+ if (err_loc0 / 8 < BCH_DATA_UNIT) {
+ printf("pdata[%x]:%x =>", ((i / chip->ecc.bytes) *
+ chip->ecc.size + ((reg_v & 0x1fff) >> 3)),
+ buff_ptr[(reg_v & 0x1fff) >> 3]);
+
+ buff_ptr[err_loc0 / 8] ^=
+ (1 << (reg_v & BCH_CORRECT_LOC_MASK));
+
+ printf("%x\n", buff_ptr[(reg_v & 0x1fff) >> 3]);
+
+ max_bitflips++;
+ }
+
+ if (((k + 1) * 2) <= err_num && ((err_loc1 / 8) <
+ BCH_DATA_UNIT)) {
+ printf("pdata[%x]:%x =>", ((i / chip->ecc.bytes) *
+ chip->ecc.size + (((reg_v >> 16) & 0x1fff) >>
+ 3)), buff_ptr[((reg_v >> 16) & 0x1fff) >> 3]);
+
+ buff_ptr[err_loc1 / 8] ^= (1 << ((reg_v >> 16) &
+ BCH_CORRECT_LOC_MASK));
+
+ printf("%x\n", buff_ptr[((reg_v >> 16) & 0x1fff) >> 3]);
+
+ max_bitflips++;
+ }
+ }
+
+ return max_bitflips;
+}
+
+static int ca_do_bch_decode(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int page, unsigned int addr)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ unsigned int reg_v, err_num;
+ unsigned char *ecc_code = chip->buffers->ecccode;
+ unsigned char *ecc_end_pos;
+ int ret, i, j, k, n, step, eccsteps, max_bitflips = 0;
+ u8 *buff_ptr = (u8 *)buf;
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccoob.eccpos[i]];
+
+ for (i = 0, eccsteps = chip->ecc.steps; eccsteps;
+ i += chip->ecc.bytes, eccsteps--) {
+ ecc_end_pos = ecc_code + chip->ecc.bytes;
+
+ for (j = 0, k = 0; j < chip->ecc.bytes; j += 4, k++) {
+ reg_v = 0;
+ for (n = 0; n < 4 && ecc_code != ecc_end_pos;
+ ++n, ++ecc_code) {
+ reg_v |= *ecc_code << (8 * n);
+ }
+ clrsetbits_le32(&info->reg->flash_nf_bch_oob0 + k,
+ GENMASK(31, 0), reg_v);
+ }
+
+ /* Clear ECC buffer */
+ setbits_le32(&info->reg->flash_nf_ecc_reset, RESET_NFLASH_ECC);
+ ret = readl_poll_timeout(&info->reg->flash_nf_ecc_reset, reg_v,
+ !(reg_v & RESET_NFLASH_ECC),
+ FLASH_SHORT_DELAY);
+ if (ret)
+ pr_err("Reset ECC buffer fail\n");
+
+ clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(8, 8),
+ BCH_DISABLE);
+
+ /* Start BCH */
+ step = i / chip->ecc.bytes;
+ clrsetbits_le32(&info->reg->flash_nf_bch_control,
+ GENMASK(6, 4), step << 4);
+ setbits_le32(&info->reg->flash_nf_bch_control, BCH_ENABLE);
+ udelay(10);
+ setbits_le32(&info->reg->flash_nf_bch_control, BCH_COMPARE);
+
+ ret = readl_poll_timeout(&info->reg->flash_nf_bch_status, reg_v,
+ (reg_v & BCH_DECO_DONE),
+ FLASH_SHORT_DELAY);
+ if (ret)
+ pr_err("ECC Decode timeout\n");
+
+ /* Stop compare */
+ clrbits_le32(&info->reg->flash_nf_bch_control, BCH_COMPARE);
+
+ reg_v = readl(&info->reg->flash_nf_bch_status);
+ err_num = (reg_v >> 8) & BCH_ERR_NUM_MASK;
+ reg_v &= BCH_ERR_MASK;
+
+ /* Uncorrectable */
+ if (reg_v == BCH_UNCORRECTABLE) {
+ max_bitflips =
+ nand_check_erased_ecc_chunk(buff_ptr,
+ chip->ecc.size,
+ &chip->buffers->ecccode[i],
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+
+ if (max_bitflips) {
+ mtd->ecc_stats.failed++;
+ pr_err("Uncorrectable error\n");
+ pr_err(" Page:%x step:%d\n", page, step);
+
+ return -1;
+ }
+ } else if (reg_v == BCH_CORRECTABLE_ERR) {
+ printf("Correctable error(%x)!! addr:%lx\n",
+ err_num, (unsigned long)addr - mtd->writesize);
+ printf("Dst buf: %p [ColSel:%x ]\n",
+ buff_ptr + reg_v * BCH_DATA_UNIT, step);
+
+ max_bitflips =
+ ca_do_bch_correction(chip, err_num, buff_ptr, i);
+ }
+
+ buff_ptr += BCH_DATA_UNIT;
+ }
+
+ /* Disable BCH */
+ clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(31, 0),
+ BCH_DISABLE);
+
+ return max_bitflips;
+}
+
+static int ca_do_bch_encode(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct nand_drv *info;
+ unsigned int reg_v;
+ int i, j, n, eccsteps, gen_index;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ for (i = 0, n = 0, eccsteps = chip->ecc.steps; eccsteps;
+ i += chip->ecc.bytes, eccsteps--, n++) {
+ gen_index = 0;
+ for (j = 0; j < chip->ecc.bytes; j += 4, gen_index++) {
+ reg_v =
+ readl(&info->reg->flash_nf_bch_gen0_0 + gen_index +
+ 18 * n);
+ chip->oob_poi[eccoob.eccpos[i + j]] = reg_v & OOB_MASK;
+ chip->oob_poi[eccoob.eccpos[i + j + 1]] =
+ (reg_v >> 8) & OOB_MASK;
+ chip->oob_poi[eccoob.eccpos[i + j + 2]] =
+ (reg_v >> 16) & OOB_MASK;
+ chip->oob_poi[eccoob.eccpos[i + j + 3]] =
+ (reg_v >> 24) & OOB_MASK;
+ }
+ }
+
+ /* Disable BCH */
+ clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(8, 8),
+ BCH_DISABLE);
+
+ return 0;
+}
+
+/**
+ * Page read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @param page page number
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int page, int with_ecc, int is_writing)
+{
+ unsigned int reg_v, ext_addr, addr, dma_index;
+ struct tx_descriptor_t *tx_desc;
+ struct rx_descriptor_t *rx_desc;
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ /* reset ecc control */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ RESET_NFLASH_ECC);
+
+ /* flash interrupt */
+ clrsetbits_le32(&info->reg->flash_flash_interrupt, GENMASK(0, 0),
+ REGIRQ_CLEAR);
+
+ /* reset ecc control */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ RESET_NFLASH_ECC);
+
+ /* Disable TXQ */
+ clrbits_le32(&info->dma_nand->dma_q_txq_control, GENMASK(0, 0));
+
+ /* Clear interrupt */
+ setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, GENMASK(0, 0));
+ setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, GENMASK(0, 0));
+
+ if (with_ecc == 1) {
+ switch (info->config.nand_ecc_strength) {
+ case ECC_STRENGTH_8:
+ reg_v = BCH_ERR_CAP_8;
+ break;
+ case ECC_STRENGTH_16:
+ reg_v = BCH_ERR_CAP_16;
+ break;
+ case ECC_STRENGTH_24:
+ reg_v = BCH_ERR_CAP_24;
+ break;
+ case ECC_STRENGTH_40:
+ reg_v = BCH_ERR_CAP_40;
+ break;
+ default:
+ reg_v = BCH_ERR_CAP_16;
+ break;
+ }
+ reg_v |= BCH_ENABLE;
+
+ /* BCH decode for flash read */
+ if (is_writing == 0)
+ reg_v |= BCH_DECODE;
+ clrsetbits_le32(&info->reg->flash_nf_bch_control,
+ GENMASK(31, 0), reg_v);
+ } else {
+ clrsetbits_le32(&info->reg->flash_nf_bch_control,
+ GENMASK(31, 0), 0);
+ }
+
+ /* Fill Extend address */
+ ext_addr = ((page << chip->page_shift) / EXT_ADDR_MASK);
+
+ clrsetbits_le32(&info->reg->flash_nf_access,
+ GENMASK(7, 0), (uintptr_t)ext_addr);
+
+ addr = (uintptr_t)((page << chip->page_shift) % EXT_ADDR_MASK);
+ addr = (uintptr_t)(addr + info->flash_base);
+
+ dma_index = readl(&info->dma_nand->dma_q_txq_wptr) & CA_DMA_Q_PTR_MASK;
+
+ tx_desc = info->tx_desc;
+ rx_desc = info->rx_desc;
+
+ /* TX/RX descriptor for page data */
+ tx_desc[dma_index].own = OWN_DMA;
+ tx_desc[dma_index].buf_len = mtd->writesize;
+ rx_desc[dma_index].own = OWN_DMA;
+ rx_desc[dma_index].buf_len = mtd->writesize;
+ if (is_writing == 0) {
+ tx_desc[dma_index].buf_adr = (uintptr_t)addr;
+ rx_desc[dma_index].buf_adr = (uintptr_t)(buf);
+ } else {
+ tx_desc[dma_index].buf_adr = (uintptr_t)buf;
+ rx_desc[dma_index].buf_adr = (uintptr_t)(addr);
+ }
+
+ dma_index++;
+ dma_index %= CA_DMA_DESC_NUM;
+
+ /* TX/RX descriptor for OOB area */
+ addr = (uintptr_t)(addr + mtd->writesize);
+ tx_desc[dma_index].own = OWN_DMA;
+ tx_desc[dma_index].buf_len = mtd->oobsize;
+ rx_desc[dma_index].own = OWN_DMA;
+ rx_desc[dma_index].buf_len = mtd->oobsize;
+ if (is_writing) {
+ tx_desc[dma_index].buf_adr = (uintptr_t)(chip->oob_poi);
+ rx_desc[dma_index].buf_adr = (uintptr_t)addr;
+ } else {
+ tx_desc[dma_index].buf_adr = (uintptr_t)addr;
+ rx_desc[dma_index].buf_adr = (uintptr_t)(chip->oob_poi);
+ dma_index++;
+ dma_index %= CA_DMA_DESC_NUM;
+ }
+
+ if (is_writing == 1) {
+ clrsetbits_le32(&info->reg->flash_fifo_control, GENMASK(1, 0),
+ FIFO_WRITE);
+ } else {
+ clrsetbits_le32(&info->reg->flash_fifo_control, GENMASK(1, 0),
+ FIFO_READ);
+ }
+
+ /* Start FIFO request */
+ clrsetbits_le32(&info->reg->flash_flash_access_start, GENMASK(2, 2),
+ NFLASH_FIFO_REQ);
+
+ /* Update DMA write pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_wptr, GENMASK(12, 0),
+ dma_index);
+
+ /* Start DMA */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_control, GENMASK(0, 0),
+ TX_DMA_ENABLE);
+
+ /* Wait TX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_txq_coal_interrupt,
+ reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("TX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear tx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, 1);
+
+ /* Wait RX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_rxq_coal_interrupt, reg_v,
+ (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("RX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear rx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, 1);
+
+ /* wait NAND CMD done */
+ if (is_writing == 0) {
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_FIFO_REQ))
+ printf("%s: Command timeout\n", __func__);
+ }
+
+ /* Update DMA read pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_rptr, GENMASK(12, 0),
+ dma_index);
+
+ /* ECC correction */
+ if (with_ecc == 1) {
+ ret =
+ readl_poll_timeout(&info->reg->flash_nf_bch_status,
+ reg_v, (reg_v & BCH_GEN_DONE),
+ FLASH_LONG_DELAY);
+
+ if (ret) {
+ pr_err("BCH_GEN timeout! flash_nf_bch_status=[0x%x]\n",
+ reg_v);
+ return -ETIMEDOUT;
+ }
+
+ if (is_writing == 0)
+ ca_do_bch_decode(mtd, chip, buf, page, addr);
+ else
+ ca_do_bch_encode(mtd, chip, page);
+ }
+
+ if (is_writing) {
+ dma_index++;
+ dma_index %= CA_DMA_DESC_NUM;
+
+ /* Update DMA R/W pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_txq_wptr, GENMASK(12, 0),
+ dma_index);
+
+ /* Wait TX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_txq_coal_interrupt,
+ reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("TX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear tx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, 1);
+
+ /* Wait RX DMA done */
+ ret =
+ readl_poll_timeout(&info->dma_nand->dma_q_rxq_coal_interrupt,
+ reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+ if (ret) {
+ pr_err("RX DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* clear rx interrupt */
+ setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, 1);
+
+ /* wait NAND CMD done */
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_FIFO_REQ))
+ printf("%s: Command timeout\n", __func__);
+
+ /* Update DMA R/W pointer */
+ clrsetbits_le32(&info->dma_nand->dma_q_rxq_rptr, GENMASK(12, 0),
+ dma_index);
+ }
+
+ return 0;
+}
+
+/**
+ * Hardware ecc based page read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, buf, page, 1, 0);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * Hardware ecc based page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * Read raw page data without ecc
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, buf, page, 0, 0);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * Raw page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ ret = nand_rw_page(mtd, chip, buf, page, 0, 1);
+ if (ret)
+ return ret;
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+ ECC_RESET_ALL);
+
+ return 0;
+}
+
+/**
+ * OOB data read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int with_ecc, int is_writing)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ u32 reg_val;
+ int rw_index;
+
+ if (is_writing) {
+ reg_val = NFLASH_GO | NFLASH_WT;
+ pwrite = (unsigned int *)chip->oob_poi;
+ } else {
+ reg_val = NFLASH_GO | NFLASH_RD;
+ pread = (unsigned int *)chip->oob_poi;
+ }
+
+ for (rw_index = 0; rw_index < mtd->oobsize / 4; rw_index++) {
+ clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+ NFLASH_REG_WIDTH_32);
+ if (is_writing)
+ clrsetbits_le32(&info->reg->flash_nf_data,
+ GENMASK(31, 0), pwrite[rw_index]);
+
+ clrsetbits_le32(&info->reg->flash_flash_access_start,
+ GENMASK(11, 10), reg_val);
+
+ if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+ printf("%s: Command timeout\n", __func__);
+
+ if (!is_writing)
+ pread[rw_index] = readl(&info->reg->flash_nf_data);
+ }
+ return 0;
+}
+
+/**
+ * OOB data read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ */
+static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ if (mtd->writesize <= (REG_DATA_COUNT_512_DATA >> 8))
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(7, 0),
+ NAND_CMD_READOOB);
+ ret = nand_rw_oob(mtd, chip, page, 0, 0);
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset,
+ GENMASK(31, 0), ECC_RESET_ALL);
+
+ return ret;
+}
+
+/**
+ * OOB data write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to write
+ * @return 0 when successfully completed
+ * -ETIMEDOUT when command timeout
+ */
+static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(chip);
+ int ret;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ if (mtd->writesize <= (REG_DATA_COUNT_512_DATA >> 8)) {
+ clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+ NAND_CMD_READOOB | (NAND_CMD_SEQIN << 8) |
+ (NAND_CMD_PAGEPROG << 16));
+ clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+ REG_CMD_COUNT_3TOGO);
+ }
+ ret = nand_rw_oob(mtd, chip, page, 1, 1);
+
+ /* Reset FIFO */
+ clrsetbits_le32(&info->reg->flash_nf_ecc_reset,
+ GENMASK(31, 0), ECC_RESET_ALL);
+
+ return ret;
+}
+
+/**
+ * Decode NAND parameters from the device tree
+ *
+ * @param dev Driver model device
+ * @param config Device tree NAND configuration
+ */
+static int fdt_decode_nand(struct udevice *dev, struct nand_drv *info)
+{
+ int ecc_strength;
+
+ info->reg = (struct nand_ctlr *)dev_read_addr(dev);
+ info->dma_glb = (struct dma_global *)dev_read_addr_index(dev, 1);
+ info->dma_nand = (struct dma_ssp *)dev_read_addr_index(dev, 2);
+ info->config.enabled = dev_read_enabled(dev);
+ ecc_strength = dev_read_u32_default(dev, "nand-ecc-strength", 16);
+ info->flash_base =
+ dev_read_u32_default(dev, "nand_flash_base_addr", NAND_BASE_ADDR);
+
+ switch (ecc_strength) {
+ case ECC_STRENGTH_8:
+ info->config.nand_ecc_strength = ECC_STRENGTH_8;
+ break;
+ case ECC_STRENGTH_16:
+ info->config.nand_ecc_strength = ECC_STRENGTH_16;
+ break;
+ case ECC_STRENGTH_24:
+ info->config.nand_ecc_strength = ECC_STRENGTH_24;
+ break;
+ case ECC_STRENGTH_40:
+ info->config.nand_ecc_strength = ECC_STRENGTH_40;
+ break;
+ default:
+ info->config.nand_ecc_strength = ECC_STRENGTH_16;
+ }
+
+ return 0;
+}
+
+/**
+ * config flash type
+ *
+ * @param chip nand chip info structure
+ */
+static void nand_config_flash_type(struct nand_chip *nand)
+{
+ struct nand_drv *info =
+ (struct nand_drv *)nand_get_controller_data(nand);
+ struct mtd_info *mtd = nand_to_mtd(nand);
+
+ switch (mtd->writesize) {
+ case WRITE_SIZE_512:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_512);
+ break;
+ case WRITE_SIZE_2048:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_2K);
+ break;
+ case WRITE_SIZE_4096:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_4K);
+ break;
+ case WRITE_SIZE_8192:
+ clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+ FLASH_PIN | FLASH_TYPE_8K);
+ break;
+ default:
+ pr_err("Unsupported page size(0x%x)!", nand->ecc.size);
+ }
+}
+
+/**
+ * config oob layout
+ *
+ * @param chip nand chip info structure
+ * @return 0 when successfully completed
+ * -EINVAL when ECC bytes exceed OOB size
+ */
+static int nand_config_oob_layout(struct nand_chip *nand)
+{
+ int i, ecc_start_offset;
+ struct mtd_info *mtd = nand_to_mtd(nand);
+
+ /* Calculate byte count for ECC */
+ eccoob.eccbytes = mtd->writesize / nand->ecc.size * nand->ecc.bytes;
+
+ if (mtd->oobsize < eccoob.eccbytes) {
+ pr_err("Spare area(%d) too small for BCH%d\n", nand->ecc.bytes,
+ nand->ecc.strength / 8);
+ pr_err("page_sz: %d\n", nand->ecc.size);
+ pr_err("oob_sz: %d\n", nand->ecc.bytes);
+ return -EINVAL;
+ }
+
+ /* Update OOB layout */
+ ecc_start_offset = mtd->oobsize - eccoob.eccbytes;
+ memset(eccoob.eccpos, 0, sizeof(eccoob.eccpos));
+ for (i = 0; i < eccoob.eccbytes; ++i)
+ eccoob.eccpos[i] = i + ecc_start_offset;
+
+ /* Unused spare area
+ * OOB[0] is bad block marker.
+ * Extra two byte is reserved as
+ * erase marker just right before ECC code.
+ */
+ eccoob.oobavail = nand->ecc.bytes - eccoob.eccbytes - 2;
+ eccoob.oobfree[0].offset = 2;
+ eccoob.oobfree[0].length =
+ mtd->oobsize - eccoob.eccbytes - eccoob.oobfree[0].offset - 1;
+
+ return 0;
+}
+
+static int ca_nand_probe(struct udevice *dev)
+{
+ struct ca_nand_info *ca_nand = dev_get_priv(dev);
+ struct nand_chip *nand = &ca_nand->nand_chip;
+ struct nand_drv *info = &ca_nand->nand_ctrl;
+ struct fdt_nand *config = &info->config;
+ struct mtd_info *our_mtd;
+ int ret;
+
+ if (fdt_decode_nand(dev, info)) {
+ printf("Could not decode nand-flash in device tree\n");
+ return -1;
+ }
+ if (!config->enabled)
+ return -1;
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = &eccoob;
+
+ nand->cmdfunc = ca_nand_command;
+ nand->read_byte = read_byte;
+ nand->read_buf = read_buf;
+ nand->ecc.read_page = nand_read_page_hwecc;
+ nand->ecc.write_page = nand_write_page_hwecc;
+ nand->ecc.read_page_raw = nand_read_page_raw;
+ nand->ecc.write_page_raw = nand_write_page_raw;
+ nand->ecc.read_oob = nand_read_oob;
+ nand->ecc.write_oob = nand_write_oob;
+ nand->ecc.strength = config->nand_ecc_strength;
+ nand->select_chip = nand_select_chip;
+ nand->dev_ready = nand_dev_ready;
+ nand_set_controller_data(nand, &ca_nand->nand_ctrl);
+
+ /* Disable subpage writes as we do not provide ecc->hwctl */
+ nand->options |= NAND_NO_SUBPAGE_WRITE | NAND_SKIP_BBTSCAN;
+
+ /* Configure flash type as P-NAND */
+ clrsetbits_le32(&info->reg->flash_type, FLASH_PIN,
+ FLASH_TYPE_4K | FLASH_SIZE_436OOB);
+ config->width = FLASH_WIDTH;
+
+ our_mtd = nand_to_mtd(nand);
+ ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
+ if (ret)
+ return ret;
+
+ nand->ecc.size = BCH_DATA_UNIT;
+ nand->ecc.bytes = BCH_GF_PARAM_M * (nand->ecc.strength / 8);
+
+ /* Reconfig flash type according to ONFI */
+ nand_config_flash_type(nand);
+
+ ret = set_bus_width_page_size(our_mtd);
+ if (ret)
+ return ret;
+
+ /* Set the bad block position */
+ nand->badblockpos =
+ our_mtd->writesize >
+ 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+ /* Arrange OOB layout */
+ ret = nand_config_oob_layout(nand);
+ if (ret)
+ return ret;
+
+ /* Init DMA descriptor ring */
+ ret = init_nand_dma(nand);
+ if (ret)
+ return ret;
+
+ ret = nand_scan_tail(our_mtd);
+ if (ret)
+ return ret;
+
+ ret = nand_register(0, our_mtd);
+ if (ret) {
+ dev_err(dev, "Failed to register MTD: %d\n", ret);
+ return ret;
+ }
+
+ ret = set_bus_width_page_size(our_mtd);
+ if (ret)
+ return ret;
+
+ printf("P-NAND : %s\n", our_mtd->name);
+ printf("Chip Size: %lldMB\n", nand->chipsize / (1024 * 1024));
+ printf("Block Size: %dKB\n", our_mtd->erasesize / 1024);
+ printf("Page Size: %dB\n", our_mtd->writesize);
+ printf("OOB Size: %dB\n", our_mtd->oobsize);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(cortina_nand) = {
+ .name = "CA-PNAND",
+ .id = UCLASS_MTD,
+ .of_match = cortina_nand_dt_ids,
+ .probe = ca_nand_probe,
+ .priv_auto = sizeof(struct ca_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
+ DM_DRIVER_GET(cortina_nand), &dev);
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize %s. (error %d)\n", dev->name, ret);
+}
diff --git a/drivers/mtd/nand/raw/cortina_nand.h b/drivers/mtd/nand/raw/cortina_nand.h
new file mode 100644
index 00000000000..1e3e3bfd055
--- /dev/null
+++ b/drivers/mtd/nand/raw/cortina_nand.h
@@ -0,0 +1,293 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2020 Cortina Access Inc..
+ */
+
+/* Cortina NAND definition */
+#define NAND_BASE_ADDR 0xE0000000
+#define BCH_GF_PARAM_M 14
+#define BCH_DATA_UNIT 1024
+#define FLASH_SHORT_DELAY 100
+#define FLASH_LONG_DELAY 1000
+#define FLASH_WIDTH 16
+#define BBT_PAGE_MASK 0xffffff3f
+#define WRITE_SIZE_512 512
+#define WRITE_SIZE_2048 2048
+#define WRITE_SIZE_4096 4096
+#define WRITE_SIZE_8192 8192
+#define ECC_STRENGTH_8 8
+#define ECC_STRENGTH_16 16
+#define ECC_STRENGTH_24 24
+#define ECC_STRENGTH_40 40
+#define EMPTY_PAGE 0xff
+#define ADDR1_MASK0 0x00ffffff
+#define ADDR2_MASK0 0xff000000
+#define ADDR1_MASK1 0xffff
+#define ADDR1_MASK2 0xff
+#define OOB_MASK 0xff
+#define EXT_ADDR_MASK 0x8000000
+
+/* Status bits */
+#define NAND_STATUS_FAIL 0x01
+#define NAND_STATUS_FAIL_N1 0x02
+#define NAND_STATUS_TRUE_READY 0x20
+#define NAND_STATUS_READY 0x40
+#define NAND_STATUS_WP 0x80
+
+/* Bit field in FLAS_TYPE */
+#define FLASH_PIN BIT(15)
+#define FLASH_TYPE_512 0x4000
+#define FLASH_TYPE_2K 0x5000
+#define FLASH_TYPE_4K 0x6000
+#define FLASH_TYPE_8K 0x7000
+#define FLASH_SIZE_CONFIGURABLEOOB (0x0 << 9)
+#define FLASH_SIZE_400OOB (0x1 << 9)
+#define FLASH_SIZE_436OOB (0x2 << 9)
+#define FLASH_SIZE_640OOB (0x3 << 9)
+
+/* Bit field in FLASH_STATUS */
+#define NFLASH_READY BIT(26)
+
+/* Bit field in FLASH_NF_ACCESS */
+#define NFLASH_ENABLE_ALTERNATIVE (0x0 << 15)
+#define AUTO_RESET BIT(16)
+#define DISABLE_AUTO_RESET (0x0 << 16)
+#define NFLASH_REG_WIDTH_RESERVED (0x3 << 10)
+#define NFLASH_REG_WIDTH_32 (0x2 << 10)
+#define NFLASH_REG_WIDTH_16 (0x1 << 10)
+#define NFLASH_REG_WIDTH_8 (0x0 << 10)
+
+/* Bit field in FLASH_NF_COUNT */
+#define REG_CMD_COUNT_EMPTY 0x3
+#define REG_CMD_COUNT_3TOGO 0x2
+#define REG_CMD_COUNT_2TOGO 0x1
+#define REG_CMD_COUNT_1TOGO 0x0
+#define REG_ADDR_COUNT_EMPTY (0x7 << 4)
+#define REG_ADDR_COUNT_5 (0x4 << 4)
+#define REG_ADDR_COUNT_4 (0x3 << 4)
+#define REG_ADDR_COUNT_3 (0x2 << 4)
+#define REG_ADDR_COUNT_2 (0x1 << 4)
+#define REG_ADDR_COUNT_1 (0x0 << 4)
+#define REG_DATA_COUNT_EMPTY (0x3fff << 8)
+#define REG_DATA_COUNT_512_DATA (0x1FF << 8)
+#define REG_DATA_COUNT_2k_DATA (0x7FF << 8)
+#define REG_DATA_COUNT_4k_DATA (0xFFF << 8)
+#define REG_DATA_COUNT_DATA_1 (0x0 << 8)
+#define REG_DATA_COUNT_DATA_2 (0x1 << 8)
+#define REG_DATA_COUNT_DATA_3 (0x2 << 8)
+#define REG_DATA_COUNT_DATA_4 (0x3 << 8)
+#define REG_DATA_COUNT_DATA_5 (0x4 << 8)
+#define REG_DATA_COUNT_DATA_6 (0x5 << 8)
+#define REG_DATA_COUNT_DATA_7 (0x6 << 8)
+#define REG_DATA_COUNT_DATA_8 (0x7 << 8)
+#define REG_OOB_COUNT_EMPTY (0x3ff << 22)
+
+/* Bit field in FLASH_FLASH_ACCESS_START */
+#define NFLASH_GO BIT(0)
+#define NFLASH_FIFO_REQ BIT(2)
+#define NFLASH_RD BIT(13)
+#define NFLASH_WT (BIT(12) | BIT(13))
+
+/* Bit field in FLASH_NF_ECC_RESET */
+#define RESET_NFLASH_RESET BIT(2)
+#define RESET_NFLASH_FIFO BIT(1)
+#define RESET_NFLASH_ECC BIT(0)
+#define ECC_RESET_ALL \
+ RESET_NFLASH_RESET | RESET_NFLASH_FIFO | RESET_NFLASH_ECC
+
+/* Bit field in FLASH_NF_ECC_CONTROL */
+#define ENABLE_ECC_GENERATION BIT(8)
+#define DISABLE_ECC_GENERATION (0 << 8)
+
+/* Flash FIFO control */
+#define FIFO_READ 2
+#define FIFO_WRITE 3
+
+/* NFLASH INTERRUPT */
+#define REGIRQ_CLEAR BIT(0)
+#define F_ADDR_ERR 2
+
+/* BCH ECC field definition */
+#define BCH_COMPARE BIT(0)
+#define BCH_ENABLE BIT(8)
+#define BCH_DISABLE (0 << 8)
+#define BCH_DECODE BIT(1)
+#define BCH_ENCODE (0 << 1)
+#define BCH_DECO_DONE BIT(30)
+#define BCH_GEN_DONE BIT(31)
+#define BCH_UNCORRECTABLE 0x3
+#define BCH_CORRECTABLE_ERR 0x2
+#define BCH_NO_ERR 0x1
+#define BCH_BUSY 0x0
+#define BCH_ERR_MASK 0x3
+#define BCH_ERR_NUM_MASK 0x3F
+#define BCH_ERR_LOC_MASK 0x3FFF
+#define BCH_CORRECT_LOC_MASK 0x7
+#define BCH_ERR_CAP_8 (0x0 << 9)
+#define BCH_ERR_CAP_16 (0x1 << 9)
+#define BCH_ERR_CAP_24 (0x2 << 9)
+#define BCH_ERR_CAP_40 (0x3 << 9)
+
+#define BCH_GF_PARAM_M 14
+
+struct nand_ctlr {
+ /* Cortina NAND controller register */
+ u32 flash_id;
+ u32 flash_timeout;
+ u32 flash_status;
+ u32 flash_type;
+ u32 flash_flash_access_start;
+ u32 flash_flash_interrupt;
+ u32 flash_flash_mask;
+ u32 flash_fifo_control;
+ u32 flash_fifo_status;
+ u32 flash_fifo_address;
+ u32 flash_fifo_match_address;
+ u32 flash_fifo_data;
+ u32 flash_sf_access;
+ u32 flash_sf_ext_access;
+ u32 flash_sf_address;
+ u32 flash_sf_data;
+ u32 flash_sf_timing;
+ u32 resv[3];
+ u32 flash_pf_access; // offset 0x050
+ u32 flash_pf_timing;
+ u32 resv1[2];
+ u32 flash_nf_access; // offset 0x060
+ u32 flash_nf_count;
+ u32 flash_nf_command;
+ u32 flash_nf_address_1;
+ u32 flash_nf_address_2;
+ u32 flash_nf_data;
+ u32 flash_nf_timing;
+ u32 flash_nf_ecc_status;
+ u32 flash_nf_ecc_control;
+ u32 flash_nf_ecc_oob;
+ u32 flash_nf_ecc_gen0;
+ u32 resv3[15];
+ u32 flash_nf_ecc_reset; // offset 0x0c8
+ u32 flash_nf_bch_control;
+ u32 flash_nf_bch_status;
+ u32 flash_nf_bch_error_loc01;
+ u32 resv4[19];
+ u32 flash_nf_bch_oob0; // offset 0x124
+ u32 resv5[17];
+ u32 flash_nf_bch_gen0_0; // offset 0x16c
+};
+
+/* Definition for DMA bitfield */
+#define TX_DMA_ENABLE BIT(0)
+#define RX_DMA_ENABLE BIT(0)
+#define DMA_CHECK_OWNER BIT(1)
+#define OWN_DMA 0
+#define OWN_CPU 1
+
+#define CA_DMA_DEPTH 3
+#define CA_DMA_DESC_NUM (BIT(0) << CA_DMA_DEPTH)
+#define CA_DMA_Q_PTR_MASK 0x1fff
+
+struct dma_q_base_depth_t {
+ u32 depth : 4 ; /* bits 3:0 */
+ u32 base : 28 ; /* bits 31:4 */
+};
+
+struct tx_descriptor_t {
+ unsigned int buf_adr; /* Buff addr */
+ unsigned int buf_adr_hi : 8 ; /* bits 7:0 */
+ unsigned int buf_len : 16 ; /* bits 23:8 */
+ unsigned int sgm : 1 ; /* bits 24 */
+ unsigned int rsrvd : 6 ; /* bits 30:25 */
+ unsigned int own : 1 ; /* bits 31:31 */
+};
+
+struct rx_descriptor_t {
+ unsigned int buf_adr; /* Buff addr */
+ unsigned int buf_adr_hi : 8 ; /* bits 7:0 */
+ unsigned int buf_len : 16 ; /* bits 23:8 */
+ unsigned int rsrvd : 7 ; /* bits 30:24 */
+ unsigned int own : 1 ; /* bits 31:31 */
+};
+
+struct dma_global {
+ u32 dma_glb_dma_lso_ctrl;
+ u32 dma_glb_lso_interrupt;
+ u32 dma_glb_lso_intenable;
+ u32 dma_glb_dma_lso_vlan_tag_type0;
+ u32 dma_glb_dma_lso_vlan_tag_type1;
+ u32 dma_glb_dma_lso_axi_user_sel0;
+ u32 dma_glb_axi_user_pat0;
+ u32 dma_glb_axi_user_pat1;
+ u32 dma_glb_axi_user_pat2;
+ u32 dma_glb_axi_user_pat3;
+ u32 dma_glb_fast_reg_pe0;
+ u32 dma_glb_fast_reg_pe1;
+ u32 dma_glb_dma_lso_tx_fdes_addr0;
+ u32 dma_glb_dma_lso_tx_fdes_addr1;
+ u32 dma_glb_dma_lso_tx_cdes_addr0;
+ u32 dma_glb_dma_lso_tx_cdes_addr1;
+ u32 dma_glb_dma_lso_tx_des_word0;
+ u32 dma_glb_dma_lso_tx_des_word1;
+ u32 dma_glb_dma_lso_lso_para_word0;
+ u32 dma_glb_dma_lso_lso_para_word1;
+ u32 dma_glb_dma_lso_debug0;
+ u32 dma_glb_dma_lso_debug1;
+ u32 dma_glb_dma_lso_debug2;
+ u32 dma_glb_dma_lso_spare0;
+ u32 dma_glb_dma_lso_spare1;
+ u32 dma_glb_dma_ssp_rx_ctrl;
+ u32 dma_glb_dma_ssp_tx_ctrl;
+ u32 dma_glb_dma_ssp_axi_user_sel0;
+ u32 dma_glb_dma_ssp_axi_user_sel1;
+ u32 dma_glb_dma_ssp_rx_fdes_addr0;
+ u32 dma_glb_dma_ssp_rx_fdes_addr1;
+ u32 dma_glb_dma_ssp_rx_cdes_addr0;
+ u32 dma_glb_dma_ssp_rx_cdes_addr1;
+ u32 dma_glb_dma_ssp_rx_des_word0;
+ u32 dma_glb_dma_ssp_rx_des_word1;
+ u32 dma_glb_dma_ssp_tx_fdes_addr0;
+ u32 dma_glb_dma_ssp_tx_fdes_addr1;
+ u32 dma_glb_dma_ssp_tx_cdes_addr0;
+ u32 dma_glb_dma_ssp_tx_cdes_addr1;
+ u32 dma_glb_dma_ssp_tx_des_word0;
+ u32 dma_glb_dma_ssp_tx_des_word1;
+ u32 dma_glb_dma_ssp_debug0;
+ u32 dma_glb_dma_ssp_debug1;
+ u32 dma_glb_dma_ssp_debug2;
+ u32 dma_glb_dma_ssp_spare0;
+ u32 dma_glb_dma_ssp_spare1;
+};
+
+struct dma_ssp {
+ u32 dma_q_rxq_control;
+ u32 dma_q_rxq_base_depth;
+ u32 dma_q_rxq_base;
+ u32 dma_q_rxq_wptr;
+ u32 dma_q_rxq_rptr;
+ u32 dma_q_rxq_pktcnt;
+ u32 dma_q_txq_control;
+ u32 dma_q_txq_base_depth;
+ u32 dma_q_txq_base;
+ u32 dma_q_txq_wptr;
+ u32 dma_q_txq_rptr;
+ u32 dma_q_txq_pktcnt;
+ u32 dma_q_rxq_interrupt;
+ u32 dma_q_rxq_intenable;
+ u32 dma_q_txq_interrupt;
+ u32 dma_q_txq_intenable;
+ u32 dma_q_rxq_misc_interrupt;
+ u32 dma_q_rxq_misc_intenable;
+ u32 dma_q_txq_misc_interrupt;
+ u32 dma_q_txq_misc_intenable;
+ u32 dma_q_rxq_coal_interrupt;
+ u32 dma_q_rxq_coal_intenable;
+ u32 dma_q_txq_coal_interrupt;
+ u32 dma_q_txq_coal_intenable;
+ u32 dma_q_rxq_frag_buff_addr0;
+ u32 dma_q_rxq_frag_buff_addr1;
+ u32 dma_q_rxq_frag_buff_size;
+ u32 dma_q_txq_frag_buff_addr0;
+ u32 dma_q_txq_frag_buff_addr1;
+ u32 dma_q_txq_frag_buff_size;
+ u32 dma_q_dma_spare_0;
+ u32 dma_q_dma_spare_1;
+};
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index 6553dde45c2..4dd3f73ead2 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -615,6 +615,7 @@ static int mtk_gpiochip_register(struct udevice *parent)
if (!drv)
return -ENOENT;
+ ret = -ENOENT;
dev_for_each_subnode(node, parent)
if (ofnode_read_bool(node, "gpio-controller")) {
ret = 0;
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index 7d51510d1b8..583fd3ddcde 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -91,6 +91,21 @@ config DM_PMIC_FAN53555
The driver implements read/write operations for use with the FAN53555
regulator driver and binds the regulator driver to its node.
+config DM_PMIC_MP5416
+ bool "Enable Driver Model for PMIC MP5416"
+ depends on DM_PMIC
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MP5416. The driver implements read/write operations.
+
+config SPL_DM_PMIC_MP5416
+ bool "Enable Driver Model for PMIC MP5416 in SPL stage"
+ depends on DM_PMIC
+ help
+ This config enables implementation of driver-model pmic uclass
+ features for PMIC MP5416. The driver implements read/write
+ operations.
+
config DM_PMIC_PCA9450
bool "Enable Driver Model for PMIC PCA9450"
depends on DM_PMIC
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 9cd6c375c06..2b2a6ddb565 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o
obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o
obj-$(CONFIG_$(SPL_)DM_PMIC_BD71837) += bd71837.o
+obj-$(CONFIG_$(SPL_)DM_PMIC_MP5416) += mp5416.o
obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o
obj-$(CONFIG_$(SPL_)DM_PMIC_PCA9450) += pca9450.o
obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o
diff --git a/drivers/power/pmic/mp5416.c b/drivers/power/pmic/mp5416.c
new file mode 100644
index 00000000000..458c4df688c
--- /dev/null
+++ b/drivers/power/pmic/mp5416.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Gateworks Corporation
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/mp5416.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct pmic_child_info pmic_children_info[] = {
+ /* buck */
+ { .prefix = "b", .driver = MP6416_REGULATOR_DRIVER },
+ /* ldo */
+ { .prefix = "l", .driver = MP6416_REGULATOR_DRIVER },
+ { },
+};
+
+static int mp5416_reg_count(struct udevice *dev)
+{
+ return MP5416_NUM_OF_REGS - 1;
+}
+
+static int mp5416_write(struct udevice *dev, uint reg, const uint8_t *buff,
+ int len)
+{
+ if (dm_i2c_write(dev, reg, buff, len)) {
+ pr_err("write error to device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mp5416_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ if (dm_i2c_read(dev, reg, buff, len)) {
+ pr_err("read error from device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mp5416_bind(struct udevice *dev)
+{
+ int children;
+ ofnode regulators_node;
+
+ debug("%s %s\n", __func__, dev->name);
+ regulators_node = dev_read_subnode(dev, "regulators");
+ if (!ofnode_valid(regulators_node)) {
+ debug("%s: %s regulators subnode not found!\n", __func__,
+ dev->name);
+ return -ENXIO;
+ }
+
+ debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+ children = pmic_bind_children(dev, regulators_node, pmic_children_info);
+ if (!children)
+ debug("%s: %s - no child found\n", __func__, dev->name);
+
+ /* Always return success for this device */
+ return 0;
+}
+
+static int mp5416_probe(struct udevice *dev)
+{
+ debug("%s %s\n", __func__, dev->name);
+
+ return 0;
+}
+
+static struct dm_pmic_ops mp5416_ops = {
+ .reg_count = mp5416_reg_count,
+ .read = mp5416_read,
+ .write = mp5416_write,
+};
+
+static const struct udevice_id mp5416_ids[] = {
+ { .compatible = "mps,mp5416", },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_mp5416) = {
+ .name = "mp5416 pmic",
+ .id = UCLASS_PMIC,
+ .of_match = mp5416_ids,
+ .bind = mp5416_bind,
+ .probe = mp5416_probe,
+ .ops = &mp5416_ops,
+};
diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c
index 1a829b5a4cf..259e4e3e678 100644
--- a/drivers/qe/qe.c
+++ b/drivers/qe/qe.c
@@ -288,7 +288,6 @@ void u_qe_init(void)
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
if (!mmc) {
- free(addr);
printf("\nMMC cannot find device for ucode\n");
} else {
printf("\nMMC read: dev # %u, block # %u, count %u ...\n",
diff --git a/drivers/ram/aspeed/Kconfig b/drivers/ram/aspeed/Kconfig
index 020c9131882..049b9dc2492 100644
--- a/drivers/ram/aspeed/Kconfig
+++ b/drivers/ram/aspeed/Kconfig
@@ -1,4 +1,5 @@
if RAM || SPL_RAM
+
config ASPEED_DDR4_DUALX8
bool "Enable Dual X8 DDR4 die"
depends on DM && OF_CONTROL && ARCH_ASPEED
@@ -7,4 +8,62 @@ config ASPEED_DDR4_DUALX8
Say Y if dual X8 DDR4 die is used on the board. The aspeed ddr sdram
controller needs to know if the memory chip mounted on the board is dual
x8 die or not. Or it may get the wrong size of the memory space.
-endif
+
+if ASPEED_AST2600
+
+choice
+ prompt "DDR4 target date rate"
+ default ASPEED_DDR4_1600
+
+config ASPEED_DDR4_400
+ bool "DDR4 targets at 400Mbps"
+ depends on DM && OF_CONTROL && ARCH_ASPEED
+ help
+ select DDR4 target data rate at 400M
+
+config ASPEED_DDR4_800
+ bool "DDR4 targets at 800Mbps"
+ depends on DM && OF_CONTROL && ARCH_ASPEED
+ help
+ select DDR4 target data rate at 800M
+
+config ASPEED_DDR4_1333
+ bool "DDR4 targets at 1333Mbps"
+ depends on DM && OF_CONTROL && ARCH_ASPEED
+ help
+ select DDR4 target data rate at 1333M
+
+config ASPEED_DDR4_1600
+ bool "DDR4 targets at 1600Mbps"
+ depends on DM && OF_CONTROL && ARCH_ASPEED
+ help
+ select DDR4 target data rate at 1600M
+endchoice
+
+config ASPEED_BYPASS_SELFTEST
+ bool "bypass self test during DRAM initialization"
+ default n
+ help
+ Say Y here to bypass DRAM self test to speed up the boot time
+
+config ASPEED_ECC
+ bool "aspeed SDRAM error correcting code"
+ depends on DM && OF_CONTROL && ARCH_ASPEED
+ default n
+ help
+ enable SDRAM ECC function
+
+if ASPEED_ECC
+config ASPEED_ECC_SIZE
+ int "ECC size: 0=driver auto-caluated"
+ depends on ASPEED_ECC
+ default 0
+ help
+ SDRAM size with the error correcting code enabled. The unit is
+ in Megabytes. Noted that only the 8/9 of the configured size
+ can be used by the system. The remaining 1/9 will be used by
+ the ECC engine. If the size is set to 0, the sdram driver will
+ calculate the SDRAM size and set the whole range be ECC enabled.
+endif # end of ASPEED_ECC
+endif # end of ASPEED_AST2600
+endif # end of RAM || SPL_RAM
diff --git a/drivers/ram/aspeed/Makefile b/drivers/ram/aspeed/Makefile
index af604f8a4b0..7ac10af1c22 100644
--- a/drivers/ram/aspeed/Makefile
+++ b/drivers/ram/aspeed/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0+
#
-obj-$(CONFIG_ASPEED_AST2500) += sdram_ast2500.o \ No newline at end of file
+obj-$(CONFIG_ASPEED_AST2500) += sdram_ast2500.o
+obj-$(CONFIG_ASPEED_AST2600) += sdram_ast2600.o
diff --git a/drivers/ram/aspeed/sdram_ast2600.c b/drivers/ram/aspeed/sdram_ast2600.c
new file mode 100644
index 00000000000..fd4235225a4
--- /dev/null
+++ b/drivers/ram/aspeed/sdram_ast2600.c
@@ -0,0 +1,1061 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) ASPEED Technology Inc.
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <ram.h>
+#include <regmap.h>
+#include <reset.h>
+#include <asm/io.h>
+#include <asm/arch/scu_ast2600.h>
+#include <asm/arch/sdram_ast2600.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <dt-bindings/clock/ast2600-clock.h>
+
+#define DDR_PHY_TBL_CHG_ADDR 0xaeeddeea
+#define DDR_PHY_TBL_END 0xaeededed
+
+#if defined(CONFIG_ASPEED_DDR4_800)
+u32 ast2600_sdramphy_config[165] = {
+ 0x1e6e0100, // start address
+ 0x00000000, // phyr000
+ 0x0c002062, // phyr004
+ 0x1a7a0063, // phyr008
+ 0x5a7a0063, // phyr00c
+ 0x1a7a0063, // phyr010
+ 0x1a7a0063, // phyr014
+ 0x20000000, // phyr018
+ 0x20000000, // phyr01c
+ 0x20000000, // phyr020
+ 0x20000000, // phyr024
+ 0x00000008, // phyr028
+ 0x00000000, // phyr02c
+ 0x00077600, // phyr030
+ 0x00000000, // phyr034
+ 0x00000000, // phyr038
+ 0x20000000, // phyr03c
+ 0x50506000, // phyr040
+ 0x50505050, // phyr044
+ 0x00002f07, // phyr048
+ 0x00003080, // phyr04c
+ 0x04000000, // phyr050
+ 0x00000200, // phyr054
+ 0x03140201, // phyr058
+ 0x04800000, // phyr05c
+ 0x0800044e, // phyr060
+ 0x00000000, // phyr064
+ 0x00180008, // phyr068
+ 0x00e00400, // phyr06c
+ 0x00140206, // phyr070
+ 0x1d4c0000, // phyr074
+ 0x493e0107, // phyr078
+ 0x08060404, // phyr07c
+ 0x90000a00, // phyr080
+ 0x06420618, // phyr084
+ 0x00001002, // phyr088
+ 0x05701016, // phyr08c
+ 0x10000000, // phyr090
+ 0xaeeddeea, // change address
+ 0x1e6e019c, // new address
+ 0x20202020, // phyr09c
+ 0x20202020, // phyr0a0
+ 0x00002020, // phyr0a4
+ 0x00002020, // phyr0a8
+ 0x00000001, // phyr0ac
+ 0xaeeddeea, // change address
+ 0x1e6e01cc, // new address
+ 0x01010101, // phyr0cc
+ 0x01010101, // phyr0d0
+ 0x80808080, // phyr0d4
+ 0x80808080, // phyr0d8
+ 0xaeeddeea, // change address
+ 0x1e6e0288, // new address
+ 0x80808080, // phyr188
+ 0x80808080, // phyr18c
+ 0x80808080, // phyr190
+ 0x80808080, // phyr194
+ 0xaeeddeea, // change address
+ 0x1e6e02f8, // new address
+ 0x90909090, // phyr1f8
+ 0x88888888, // phyr1fc
+ 0xaeeddeea, // change address
+ 0x1e6e0300, // new address
+ 0x00000000, // phyr200
+ 0xaeeddeea, // change address
+ 0x1e6e0194, // new address
+ 0x80118260, // phyr094
+ 0xaeeddeea, // change address
+ 0x1e6e019c, // new address
+ 0x20202020, // phyr09c
+ 0x20202020, // phyr0a0
+ 0x00002020, // phyr0a4
+ 0x80000000, // phyr0a8
+ 0x00000001, // phyr0ac
+ 0xaeeddeea, // change address
+ 0x1e6e0318, // new address
+ 0x09222719, // phyr218
+ 0x00aa4403, // phyr21c
+ 0xaeeddeea, // change address
+ 0x1e6e0198, // new address
+ 0x08060000, // phyr098
+ 0xaeeddeea, // change address
+ 0x1e6e01b0, // new address
+ 0x00000000, // phyr0b0
+ 0x00000000, // phyr0b4
+ 0x00000000, // phyr0b8
+ 0x00000000, // phyr0bc
+ 0x00000000, // phyr0c0
+ 0x00000000, // phyr0c4
+ 0x000aff2c, // phyr0c8
+ 0xaeeddeea, // change address
+ 0x1e6e01dc, // new address
+ 0x00080000, // phyr0dc
+ 0x00000000, // phyr0e0
+ 0xaa55aa55, // phyr0e4
+ 0x55aa55aa, // phyr0e8
+ 0xaaaa5555, // phyr0ec
+ 0x5555aaaa, // phyr0f0
+ 0xaa55aa55, // phyr0f4
+ 0x55aa55aa, // phyr0f8
+ 0xaaaa5555, // phyr0fc
+ 0x5555aaaa, // phyr100
+ 0xaa55aa55, // phyr104
+ 0x55aa55aa, // phyr108
+ 0xaaaa5555, // phyr10c
+ 0x5555aaaa, // phyr110
+ 0xaa55aa55, // phyr114
+ 0x55aa55aa, // phyr118
+ 0xaaaa5555, // phyr11c
+ 0x5555aaaa, // phyr120
+ 0x20202020, // phyr124
+ 0x20202020, // phyr128
+ 0x20202020, // phyr12c
+ 0x20202020, // phyr130
+ 0x20202020, // phyr134
+ 0x20202020, // phyr138
+ 0x20202020, // phyr13c
+ 0x20202020, // phyr140
+ 0x20202020, // phyr144
+ 0x20202020, // phyr148
+ 0x20202020, // phyr14c
+ 0x20202020, // phyr150
+ 0x20202020, // phyr154
+ 0x20202020, // phyr158
+ 0x20202020, // phyr15c
+ 0x20202020, // phyr160
+ 0x20202020, // phyr164
+ 0x20202020, // phyr168
+ 0x20202020, // phyr16c
+ 0x20202020, // phyr170
+ 0xaeeddeea, // change address
+ 0x1e6e0298, // new address
+ 0x20200800, // phyr198
+ 0x20202020, // phyr19c
+ 0x20202020, // phyr1a0
+ 0x20202020, // phyr1a4
+ 0x20202020, // phyr1a8
+ 0x20202020, // phyr1ac
+ 0x20202020, // phyr1b0
+ 0x20202020, // phyr1b4
+ 0x20202020, // phyr1b8
+ 0x20202020, // phyr1bc
+ 0x20202020, // phyr1c0
+ 0x20202020, // phyr1c4
+ 0x20202020, // phyr1c8
+ 0x20202020, // phyr1cc
+ 0x20202020, // phyr1d0
+ 0x20202020, // phyr1d4
+ 0x20202020, // phyr1d8
+ 0x20202020, // phyr1dc
+ 0x20202020, // phyr1e0
+ 0x20202020, // phyr1e4
+ 0x00002020, // phyr1e8
+ 0xaeeddeea, // change address
+ 0x1e6e0304, // new address
+ 0x00000800, // phyr204
+ 0xaeeddeea, // change address
+ 0x1e6e027c, // new address
+ 0x4e400000, // phyr17c
+ 0x59595959, // phyr180
+ 0x40404040, // phyr184
+ 0xaeeddeea, // change address
+ 0x1e6e02f4, // new address
+ 0x00000059, // phyr1f4
+ 0xaeededed, // end
+};
+#else
+u32 ast2600_sdramphy_config[165] = {
+ 0x1e6e0100, // start address
+ 0x00000000, // phyr000
+ 0x0c002062, // phyr004
+ 0x1a7a0063, // phyr008
+ 0x5a7a0063, // phyr00c
+ 0x1a7a0063, // phyr010
+ 0x1a7a0063, // phyr014
+ 0x20000000, // phyr018
+ 0x20000000, // phyr01c
+ 0x20000000, // phyr020
+ 0x20000000, // phyr024
+ 0x00000008, // phyr028
+ 0x00000000, // phyr02c
+ 0x00077600, // phyr030
+ 0x00000000, // phyr034
+ 0x00000000, // phyr038
+ 0x20000000, // phyr03c
+ 0x50506000, // phyr040
+ 0x50505050, // phyr044
+ 0x00002f07, // phyr048
+ 0x00003080, // phyr04c
+ 0x04000000, // phyr050
+ 0x00000200, // phyr054
+ 0x03140501, // phyr058-rtt:40
+ 0x04800000, // phyr05c
+ 0x0800044e, // phyr060
+ 0x00000000, // phyr064
+ 0x00180008, // phyr068
+ 0x00e00400, // phyr06c
+ 0x00140206, // phyr070
+ 0x1d4c0000, // phyr074
+ 0x493e0107, // phyr078
+ 0x08060404, // phyr07c
+ 0x90000a00, // phyr080
+ 0x06420c30, // phyr084
+ 0x00001002, // phyr088
+ 0x05701016, // phyr08c
+ 0x10000000, // phyr090
+ 0xaeeddeea, // change address
+ 0x1e6e019c, // new address
+ 0x20202020, // phyr09c
+ 0x20202020, // phyr0a0
+ 0x00002020, // phyr0a4
+ 0x00002020, // phyr0a8
+ 0x00000001, // phyr0ac
+ 0xaeeddeea, // change address
+ 0x1e6e01cc, // new address
+ 0x01010101, // phyr0cc
+ 0x01010101, // phyr0d0
+ 0x80808080, // phyr0d4
+ 0x80808080, // phyr0d8
+ 0xaeeddeea, // change address
+ 0x1e6e0288, // new address
+ 0x80808080, // phyr188
+ 0x80808080, // phyr18c
+ 0x80808080, // phyr190
+ 0x80808080, // phyr194
+ 0xaeeddeea, // change address
+ 0x1e6e02f8, // new address
+ 0x90909090, // phyr1f8
+ 0x88888888, // phyr1fc
+ 0xaeeddeea, // change address
+ 0x1e6e0300, // new address
+ 0x00000000, // phyr200
+ 0xaeeddeea, // change address
+ 0x1e6e0194, // new address
+ 0x801112e0, // phyr094 - bit12=1,15=0,- write window is ok
+ 0xaeeddeea, // change address
+ 0x1e6e019c, // new address
+ 0x20202020, // phyr09c
+ 0x20202020, // phyr0a0
+ 0x00002020, // phyr0a4
+ 0x80000000, // phyr0a8
+ 0x00000001, // phyr0ac
+ 0xaeeddeea, // change address
+ 0x1e6e0318, // new address
+ 0x09222719, // phyr218
+ 0x00aa4403, // phyr21c
+ 0xaeeddeea, // change address
+ 0x1e6e0198, // new address
+ 0x08060000, // phyr098
+ 0xaeeddeea, // change address
+ 0x1e6e01b0, // new address
+ 0x00000000, // phyr0b0
+ 0x00000000, // phyr0b4
+ 0x00000000, // phyr0b8
+ 0x00000000, // phyr0bc
+ 0x00000000, // phyr0c0 - ori
+ 0x00000000, // phyr0c4
+ 0x000aff2c, // phyr0c8
+ 0xaeeddeea, // change address
+ 0x1e6e01dc, // new address
+ 0x00080000, // phyr0dc
+ 0x00000000, // phyr0e0
+ 0xaa55aa55, // phyr0e4
+ 0x55aa55aa, // phyr0e8
+ 0xaaaa5555, // phyr0ec
+ 0x5555aaaa, // phyr0f0
+ 0xaa55aa55, // phyr0f4
+ 0x55aa55aa, // phyr0f8
+ 0xaaaa5555, // phyr0fc
+ 0x5555aaaa, // phyr100
+ 0xaa55aa55, // phyr104
+ 0x55aa55aa, // phyr108
+ 0xaaaa5555, // phyr10c
+ 0x5555aaaa, // phyr110
+ 0xaa55aa55, // phyr114
+ 0x55aa55aa, // phyr118
+ 0xaaaa5555, // phyr11c
+ 0x5555aaaa, // phyr120
+ 0x20202020, // phyr124
+ 0x20202020, // phyr128
+ 0x20202020, // phyr12c
+ 0x20202020, // phyr130
+ 0x20202020, // phyr134
+ 0x20202020, // phyr138
+ 0x20202020, // phyr13c
+ 0x20202020, // phyr140
+ 0x20202020, // phyr144
+ 0x20202020, // phyr148
+ 0x20202020, // phyr14c
+ 0x20202020, // phyr150
+ 0x20202020, // phyr154
+ 0x20202020, // phyr158
+ 0x20202020, // phyr15c
+ 0x20202020, // phyr160
+ 0x20202020, // phyr164
+ 0x20202020, // phyr168
+ 0x20202020, // phyr16c
+ 0x20202020, // phyr170
+ 0xaeeddeea, // change address
+ 0x1e6e0298, // new address
+ 0x20200800, // phyr198
+ 0x20202020, // phyr19c
+ 0x20202020, // phyr1a0
+ 0x20202020, // phyr1a4
+ 0x20202020, // phyr1a8
+ 0x20202020, // phyr1ac
+ 0x20202020, // phyr1b0
+ 0x20202020, // phyr1b4
+ 0x20202020, // phyr1b8
+ 0x20202020, // phyr1bc
+ 0x20202020, // phyr1c0
+ 0x20202020, // phyr1c4
+ 0x20202020, // phyr1c8
+ 0x20202020, // phyr1cc
+ 0x20202020, // phyr1d0
+ 0x20202020, // phyr1d4
+ 0x20202020, // phyr1d8
+ 0x20202020, // phyr1dc
+ 0x20202020, // phyr1e0
+ 0x20202020, // phyr1e4
+ 0x00002020, // phyr1e8
+ 0xaeeddeea, // change address
+ 0x1e6e0304, // new address
+ 0x00000800, // phyr204
+ 0xaeeddeea, // change address
+ 0x1e6e027c, // new address
+ 0x4e400000, // phyr17c
+ 0x59595959, // phyr180
+ 0x40404040, // phyr184
+ 0xaeeddeea, // change address
+ 0x1e6e02f4, // new address
+ 0x00000059, // phyr1f4
+ 0xaeededed, // end
+};
+#endif
+
+/* MPLL configuration */
+#define SCU_MPLL_FREQ_400M 0x0008405F
+#define SCU_MPLL_EXT_400M 0x0000002F
+#define SCU_MPLL_FREQ_333M 0x00488299
+#define SCU_MPLL_EXT_333M 0x0000014C
+#define SCU_MPLL_FREQ_200M 0x0078007F
+#define SCU_MPLL_EXT_200M 0x0000003F
+#define SCU_MPLL_FREQ_100M 0x0078003F
+#define SCU_MPLL_EXT_100M 0x0000001F
+
+#if defined(CONFIG_ASPEED_DDR4_1600)
+#define SCU_MPLL_FREQ_CFG SCU_MPLL_FREQ_400M
+#define SCU_MPLL_EXT_CFG SCU_MPLL_EXT_400M
+#elif defined(CONFIG_ASPEED_DDR4_1333)
+#define SCU_MPLL_FREQ_CFG SCU_MPLL_FREQ_333M
+#define SCU_MPLL_EXT_CFG SCU_MPLL_EXT_333M
+#elif defined(CONFIG_ASPEED_DDR4_800)
+#define SCU_MPLL_FREQ_CFG SCU_MPLL_FREQ_200M
+#define SCU_MPLL_EXT_CFG SCU_MPLL_EXT_200M
+#elif defined(CONFIG_ASPEED_DDR4_400)
+#define SCU_MPLL_FREQ_CFG SCU_MPLL_FREQ_100M
+#define SCU_MPLL_EXT_CFG SCU_MPLL_EXT_100M
+#else
+#error "undefined DDR4 target rate\n"
+#endif
+
+/*
+ * AC timing and SDRAM mode register setting
+ * for real chip are derived from the model GDDR4-1600
+ */
+#define DDR4_MR01_MODE 0x03010510
+#define DDR4_MR23_MODE 0x00000000
+#define DDR4_MR45_MODE 0x04000000
+#define DDR4_MR6_MODE 0x00000400
+#define DDR4_TRFC_1600 0x467299f1
+#define DDR4_TRFC_1333 0x3a5f80c9
+#define DDR4_TRFC_800 0x23394c78
+#define DDR4_TRFC_400 0x111c263c
+
+#if defined(CONFIG_ASPEED_DDR4_1600)
+#define DDR4_TRFC DDR4_TRFC_1600
+#define DDR4_PHY_TRAIN_TRFC 0xc30
+#elif defined(CONFIG_ASPEED_DDR4_1333)
+#define DDR4_TRFC DDR4_TRFC_1333
+#define DDR4_PHY_TRAIN_TRFC 0xa25
+#elif defined(CONFIG_ASPEED_DDR4_800)
+#define DDR4_TRFC DDR4_TRFC_800
+#define DDR4_PHY_TRAIN_TRFC 0x618
+#elif defined(CONFIG_ASPEED_DDR4_400)
+#define DDR4_TRFC DDR4_TRFC_400
+#define DDR4_PHY_TRAIN_TRFC 0x30c
+#else
+#error "undefined tRFC setting"
+#endif
+
+/* supported SDRAM size */
+#define SDRAM_SIZE_1KB (1024U)
+#define SDRAM_SIZE_1MB (SDRAM_SIZE_1KB * SDRAM_SIZE_1KB)
+#define SDRAM_MIN_SIZE (256 * SDRAM_SIZE_1MB)
+#define SDRAM_MAX_SIZE (2048 * SDRAM_SIZE_1MB)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const u32 ddr4_ac_timing[4] = {
+ 0x040e0307, 0x0f4711f1, 0x0e060304, 0x00001240 };
+static const u32 ddr_max_grant_params[4] = {
+ 0x44444444, 0x44444444, 0x44444444, 0x44444444 };
+
+struct dram_info {
+ struct ram_info info;
+ struct clk ddr_clk;
+ struct ast2600_sdrammc_regs *regs;
+ struct ast2600_scu *scu;
+ struct ast2600_ddr_phy *phy;
+ void __iomem *phy_setting;
+ void __iomem *phy_status;
+ ulong clock_rate;
+};
+
+static void ast2600_sdramphy_kick_training(struct dram_info *info)
+{
+ u32 data;
+ struct ast2600_sdrammc_regs *regs = info->regs;
+
+ writel(SDRAM_PHYCTRL0_NRST, &regs->phy_ctrl[0]);
+ udelay(5);
+ writel(SDRAM_PHYCTRL0_NRST | SDRAM_PHYCTRL0_INIT, &regs->phy_ctrl[0]);
+ udelay(1000);
+
+ while (1) {
+ data = readl(&regs->phy_ctrl[0]) & SDRAM_PHYCTRL0_INIT;
+ if (~data)
+ break;
+ }
+}
+
+/**
+ * @brief load DDR-PHY configurations table to the PHY registers
+ * @param[in] p_tbl - pointer to the configuration table
+ * @param[in] info - pointer to the DRAM info struct
+ *
+ * There are two sets of MRS (Mode Registers) configuration in ast2600 memory
+ * system: one is in the SDRAM MC (memory controller) which is used in run
+ * time, and the other is in the DDR-PHY IP which is used during DDR-PHY
+ * training.
+ */
+static void ast2600_sdramphy_init(u32 *p_tbl, struct dram_info *info)
+{
+ u32 reg_base = (u32)info->phy_setting;
+ u32 addr = p_tbl[0];
+ u32 data;
+ int i = 1;
+
+ writel(0, &info->regs->phy_ctrl[0]);
+ udelay(10);
+
+ while (1) {
+ if (addr < reg_base) {
+ debug("invalid DDR-PHY addr: 0x%08x\n", addr);
+ break;
+ }
+ data = p_tbl[i++];
+
+ if (data == DDR_PHY_TBL_END) {
+ break;
+ } else if (data == DDR_PHY_TBL_CHG_ADDR) {
+ addr = p_tbl[i++];
+ } else {
+ writel(data, addr);
+ addr += 4;
+ }
+ }
+
+ data = readl(info->phy_setting + 0x84) & ~GENMASK(16, 0);
+ data |= DDR4_PHY_TRAIN_TRFC;
+ writel(data, info->phy_setting + 0x84);
+}
+
+static int ast2600_sdramphy_check_status(struct dram_info *info)
+{
+ u32 value, tmp;
+ u32 reg_base = (u32)info->phy_status;
+ int need_retrain = 0;
+
+ debug("\nSDRAM PHY training report:\n");
+
+ /* training status */
+ value = readl(reg_base + 0x00);
+ debug("rO_DDRPHY_reg offset 0x00 = 0x%08x\n", value);
+
+ if (value & BIT(3))
+ debug("\tinitial PVT calibration fail\n");
+
+ if (value & BIT(5))
+ debug("\truntime calibration fail\n");
+
+ /* PU & PD */
+ value = readl(reg_base + 0x30);
+ debug("rO_DDRPHY_reg offset 0x30 = 0x%08x\n", value);
+ debug(" PU = 0x%02x\n", value & 0xff);
+ debug(" PD = 0x%02x\n", (value >> 16) & 0xff);
+
+ /* read eye window */
+ value = readl(reg_base + 0x68);
+ if (0 == (value & GENMASK(7, 0)))
+ need_retrain = 1;
+
+ debug("rO_DDRPHY_reg offset 0x68 = 0x%08x\n", value);
+ debug(" rising edge of read data eye training pass window\n");
+ tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 255;
+ debug(" B0:%d%%\n", tmp);
+ tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 255;
+ debug(" B1:%d%%\n", tmp);
+
+ value = readl(reg_base + 0xC8);
+ debug("rO_DDRPHY_reg offset 0xC8 = 0x%08x\n", value);
+ debug(" falling edge of read data eye training pass window\n");
+ tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 255;
+ debug(" B0:%d%%\n", tmp);
+ tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 255;
+ debug(" B1:%d%%\n", tmp);
+
+ /* write eye window */
+ value = readl(reg_base + 0x7c);
+ if (0 == (value & GENMASK(7, 0)))
+ need_retrain = 1;
+
+ debug("rO_DDRPHY_reg offset 0x7C = 0x%08x\n", value);
+ debug(" rising edge of write data eye training pass window\n");
+ tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 255;
+ debug(" B0:%d%%\n", tmp);
+ tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 255;
+ debug(" B1:%d%%\n", tmp);
+
+ /* read Vref training result */
+ value = readl(reg_base + 0x88);
+ debug("rO_DDRPHY_reg offset 0x88 = 0x%08x\n", value);
+ debug(" read Vref training result\n");
+ tmp = (((value & GENMASK(7, 0)) >> 0) * 100) / 127;
+ debug(" B0:%d%%\n", tmp);
+ tmp = (((value & GENMASK(15, 8)) >> 8) * 100) / 127;
+ debug(" B1:%d%%\n", tmp);
+
+ /* write Vref training result */
+ value = readl(reg_base + 0x90);
+ debug("rO_DDRPHY_reg offset 0x90 = 0x%08x\n", value);
+
+ /* gate train */
+ value = readl(reg_base + 0x50);
+ if ((0 == (value & GENMASK(15, 0))) ||
+ (0 == (value & GENMASK(31, 16)))) {
+ need_retrain = 1;
+ }
+
+ debug("rO_DDRPHY_reg offset 0x50 = 0x%08x\n", value);
+
+ return need_retrain;
+}
+
+#ifndef CONFIG_ASPEED_BYPASS_SELFTEST
+#define MC_TEST_PATTERN_N 8
+static u32 as2600_sdrammc_test_pattern[MC_TEST_PATTERN_N] = {
+ 0xcc33cc33, 0xff00ff00, 0xaa55aa55, 0x88778877,
+ 0x92cc4d6e, 0x543d3cde, 0xf1e843c7, 0x7c61d253 };
+
+#define TIMEOUT_DRAM 5000000
+int ast2600_sdrammc_dg_test(struct dram_info *info, unsigned int datagen, u32 mode)
+{
+ unsigned int data;
+ unsigned int timeout = 0;
+ struct ast2600_sdrammc_regs *regs = info->regs;
+
+ writel(0, &regs->ecc_test_ctrl);
+
+ if (mode == 0)
+ writel(0x00000085 | (datagen << 3), &regs->ecc_test_ctrl);
+ else
+ writel(0x000000C1 | (datagen << 3), &regs->ecc_test_ctrl);
+
+ do {
+ data = readl(&regs->ecc_test_ctrl) & GENMASK(13, 12);
+
+ if (data & BIT(13))
+ return 0;
+
+ if (++timeout > TIMEOUT_DRAM) {
+ debug("Timeout!!\n");
+ writel(0, &regs->ecc_test_ctrl);
+ return -1;
+ }
+ } while (!data);
+
+ writel(0, &regs->ecc_test_ctrl);
+
+ return 0;
+}
+
+int ast2600_sdrammc_cbr_test(struct dram_info *info)
+{
+ u32 i;
+ struct ast2600_sdrammc_regs *regs = info->regs;
+
+ clrsetbits_le32(&regs->test_addr, GENMASK(30, 4), 0x7ffff0);
+
+ /* single */
+ for (i = 0; i < 8; i++)
+ if (ast2600_sdrammc_dg_test(info, i, 0))
+ return -1;
+
+ /* burst */
+ for (i = 0; i < 8; i++)
+ if (ast2600_sdrammc_dg_test(info, i, i))
+ return -1;
+
+ return 0;
+}
+
+static int ast2600_sdrammc_test(struct dram_info *info)
+{
+ struct ast2600_sdrammc_regs *regs = info->regs;
+
+ u32 pass_cnt = 0;
+ u32 fail_cnt = 0;
+ u32 target_cnt = 2;
+ u32 test_cnt = 0;
+ u32 pattern;
+ u32 i = 0;
+ bool finish = false;
+
+ debug("sdram mc test:\n");
+ while (!finish) {
+ pattern = as2600_sdrammc_test_pattern[i++];
+ i = i % MC_TEST_PATTERN_N;
+ debug(" pattern = %08X : ", pattern);
+ writel(pattern, &regs->test_init_val);
+
+ if (ast2600_sdrammc_cbr_test(info)) {
+ debug("fail\n");
+ fail_cnt++;
+ } else {
+ debug("pass\n");
+ pass_cnt++;
+ }
+
+ if (++test_cnt == target_cnt)
+ finish = true;
+ }
+ debug("statistics: pass/fail/total:%d/%d/%d\n", pass_cnt, fail_cnt,
+ target_cnt);
+
+ return fail_cnt;
+}
+#endif
+
+/*
+ * scu500[14:13]
+ * 2b'00: VGA memory size = 16MB
+ * 2b'01: VGA memory size = 16MB
+ * 2b'10: VGA memory size = 32MB
+ * 2b'11: VGA memory size = 64MB
+ *
+ * mcr04[3:2]
+ * 2b'00: VGA memory size = 8MB
+ * 2b'01: VGA memory size = 16MB
+ * 2b'10: VGA memory size = 32MB
+ * 2b'11: VGA memory size = 64MB
+ */
+static size_t ast2600_sdrammc_get_vga_mem_size(struct dram_info *info)
+{
+ u32 vga_hwconf;
+ size_t vga_mem_size_base = 8 * 1024 * 1024;
+
+ vga_hwconf =
+ (readl(&info->scu->hwstrap1) & SCU_HWSTRAP1_VGA_MEM_MASK) >>
+ SCU_HWSTRAP1_VGA_MEM_SHIFT;
+
+ if (vga_hwconf == 0) {
+ vga_hwconf = 1;
+ writel(vga_hwconf << SCU_HWSTRAP1_VGA_MEM_SHIFT,
+ &info->scu->hwstrap1);
+ }
+
+ clrsetbits_le32(&info->regs->config, SDRAM_CONF_VGA_SIZE_MASK,
+ ((vga_hwconf << SDRAM_CONF_VGA_SIZE_SHIFT) &
+ SDRAM_CONF_VGA_SIZE_MASK));
+
+ /* no need to reserve VGA memory if efuse[VGA disable] is set */
+ if (readl(&info->scu->efuse) & SCU_EFUSE_DIS_VGA)
+ return 0;
+
+ return vga_mem_size_base << vga_hwconf;
+}
+
+/*
+ * Find out RAM size and save it in dram_info
+ *
+ * The procedure is taken from Aspeed SDK
+ */
+static void ast2600_sdrammc_calc_size(struct dram_info *info)
+{
+ /* The controller supports 256/512/1024/2048 MB ram */
+ size_t ram_size = SDRAM_MIN_SIZE;
+ const int write_test_offset = 0x100000;
+ u32 test_pattern = 0xdeadbeef;
+ u32 cap_param = SDRAM_CONF_CAP_2048M;
+ u32 refresh_timing_param = DDR4_TRFC;
+ const u32 write_addr_base = CONFIG_SYS_SDRAM_BASE + write_test_offset;
+
+ for (ram_size = SDRAM_MAX_SIZE; ram_size > SDRAM_MIN_SIZE;
+ ram_size >>= 1) {
+ writel(test_pattern, write_addr_base + (ram_size >> 1));
+ test_pattern = (test_pattern >> 4) | (test_pattern << 28);
+ }
+
+ /* One last write to overwrite all wrapped values */
+ writel(test_pattern, write_addr_base);
+
+ /* Reset the pattern and see which value was really written */
+ test_pattern = 0xdeadbeef;
+ for (ram_size = SDRAM_MAX_SIZE; ram_size > SDRAM_MIN_SIZE;
+ ram_size >>= 1) {
+ if (readl(write_addr_base + (ram_size >> 1)) == test_pattern)
+ break;
+
+ --cap_param;
+ refresh_timing_param >>= 8;
+ test_pattern = (test_pattern >> 4) | (test_pattern << 28);
+ }
+
+ clrsetbits_le32(&info->regs->ac_timing[1],
+ (SDRAM_AC_TRFC_MASK << SDRAM_AC_TRFC_SHIFT),
+ ((refresh_timing_param & SDRAM_AC_TRFC_MASK)
+ << SDRAM_AC_TRFC_SHIFT));
+
+ info->info.base = CONFIG_SYS_SDRAM_BASE;
+ info->info.size = ram_size - ast2600_sdrammc_get_vga_mem_size(info);
+
+ clrsetbits_le32(&info->regs->config, SDRAM_CONF_CAP_MASK,
+ ((cap_param << SDRAM_CONF_CAP_SHIFT) & SDRAM_CONF_CAP_MASK));
+}
+
+static int ast2600_sdrammc_init_ddr4(struct dram_info *info)
+{
+ const u32 power_ctrl = MCR34_CKE_EN | MCR34_AUTOPWRDN_EN |
+ MCR34_MREQ_BYPASS_DIS | MCR34_RESETN_DIS |
+ MCR34_ODT_EN | MCR34_ODT_AUTO_ON |
+ (0x1 << MCR34_ODT_EXT_SHIFT);
+
+ /* init SDRAM-PHY only on real chip */
+ ast2600_sdramphy_init(ast2600_sdramphy_config, info);
+ writel((MCR34_CKE_EN | MCR34_MREQI_DIS | MCR34_RESETN_DIS),
+ &info->regs->power_ctrl);
+ udelay(5);
+ ast2600_sdramphy_kick_training(info);
+ udelay(500);
+ writel(SDRAM_RESET_DLL_ZQCL_EN, &info->regs->refresh_timing);
+
+ writel(MCR30_SET_MR(3), &info->regs->mode_setting_control);
+ writel(MCR30_SET_MR(6), &info->regs->mode_setting_control);
+ writel(MCR30_SET_MR(5), &info->regs->mode_setting_control);
+ writel(MCR30_SET_MR(4), &info->regs->mode_setting_control);
+ writel(MCR30_SET_MR(2), &info->regs->mode_setting_control);
+ writel(MCR30_SET_MR(1), &info->regs->mode_setting_control);
+ writel(MCR30_SET_MR(0) | MCR30_RESET_DLL_DELAY_EN,
+ &info->regs->mode_setting_control);
+
+ writel(SDRAM_REFRESH_EN | SDRAM_RESET_DLL_ZQCL_EN |
+ (0x5f << SDRAM_REFRESH_PERIOD_SHIFT),
+ &info->regs->refresh_timing);
+
+ /* wait self-refresh idle */
+ while (readl(&info->regs->power_ctrl) &
+ MCR34_SELF_REFRESH_STATUS_MASK)
+ ;
+
+ writel(SDRAM_REFRESH_EN | SDRAM_LOW_PRI_REFRESH_EN |
+ SDRAM_REFRESH_ZQCS_EN |
+ (0x5f << SDRAM_REFRESH_PERIOD_SHIFT) |
+ (0x42aa << SDRAM_REFRESH_PERIOD_ZQCS_SHIFT),
+ &info->regs->refresh_timing);
+
+ writel(power_ctrl, &info->regs->power_ctrl);
+ udelay(500);
+
+ return 0;
+}
+
+static void ast2600_sdrammc_unlock(struct dram_info *info)
+{
+ writel(SDRAM_UNLOCK_KEY, &info->regs->protection_key);
+ while (!readl(&info->regs->protection_key))
+ ;
+}
+
+static void ast2600_sdrammc_lock(struct dram_info *info)
+{
+ writel(~SDRAM_UNLOCK_KEY, &info->regs->protection_key);
+ while (readl(&info->regs->protection_key))
+ ;
+}
+
+static void ast2600_sdrammc_common_init(struct ast2600_sdrammc_regs *regs)
+{
+ int i;
+
+ writel(MCR34_MREQI_DIS | MCR34_RESETN_DIS, &regs->power_ctrl);
+ writel(SDRAM_VIDEO_UNLOCK_KEY, &regs->gm_protection_key);
+ writel(0x10 << MCR38_RW_MAX_GRANT_CNT_RQ_SHIFT,
+ &regs->arbitration_ctrl);
+ writel(0xFFBBFFF4, &regs->req_limit_mask);
+
+ for (i = 0; i < ARRAY_SIZE(ddr_max_grant_params); ++i)
+ writel(ddr_max_grant_params[i], &regs->max_grant_len[i]);
+
+ writel(MCR50_RESET_ALL_INTR, &regs->intr_ctrl);
+
+ writel(0x07FFFFFF, &regs->ecc_range_ctrl);
+
+ writel(0, &regs->ecc_test_ctrl);
+ writel(0x80000001, &regs->test_addr);
+ writel(0, &regs->test_fail_dq_bit);
+ writel(0, &regs->test_init_val);
+
+ writel(0xFFFFFFFF, &regs->req_input_ctrl);
+ writel(0, &regs->req_high_pri_ctrl);
+
+ udelay(600);
+
+#ifdef CONFIG_ASPEED_DDR4_DUALX8
+ writel(0x37, &regs->config);
+#else
+ writel(0x17, &regs->config);
+#endif
+
+ /* load controller setting */
+ for (i = 0; i < ARRAY_SIZE(ddr4_ac_timing); ++i)
+ writel(ddr4_ac_timing[i], &regs->ac_timing[i]);
+
+ writel(DDR4_MR01_MODE, &regs->mr01_mode_setting);
+ writel(DDR4_MR23_MODE, &regs->mr23_mode_setting);
+ writel(DDR4_MR45_MODE, &regs->mr45_mode_setting);
+ writel(DDR4_MR6_MODE, &regs->mr6_mode_setting);
+}
+
+/*
+ * Update size info according to the ECC HW setting
+ *
+ * Assume SDRAM has been initialized by SPL or the host. To get the RAM size, we
+ * don't need to calculate the ECC size again but read from MCR04 and derive the
+ * size from its value.
+ */
+static void ast2600_sdrammc_update_size(struct dram_info *info)
+{
+ struct ast2600_sdrammc_regs *regs = info->regs;
+ u32 conf = readl(&regs->config);
+ u32 cap_param;
+ size_t ram_size = SDRAM_MAX_SIZE;
+ size_t hw_size;
+
+ cap_param = (conf & SDRAM_CONF_CAP_MASK) >> SDRAM_CONF_CAP_SHIFT;
+ switch (cap_param) {
+ case SDRAM_CONF_CAP_2048M:
+ ram_size = 2048 * SDRAM_SIZE_1MB;
+ break;
+ case SDRAM_CONF_CAP_1024M:
+ ram_size = 1024 * SDRAM_SIZE_1MB;
+ break;
+ case SDRAM_CONF_CAP_512M:
+ ram_size = 512 * SDRAM_SIZE_1MB;
+ break;
+ case SDRAM_CONF_CAP_256M:
+ ram_size = 256 * SDRAM_SIZE_1MB;
+ break;
+ }
+
+ info->info.base = CONFIG_SYS_SDRAM_BASE;
+ info->info.size = ram_size - ast2600_sdrammc_get_vga_mem_size(info);
+
+ if (0 == (conf & SDRAM_CONF_ECC_SETUP))
+ return;
+
+ hw_size = readl(&regs->ecc_range_ctrl) & SDRAM_ECC_RANGE_ADDR_MASK;
+ hw_size += (1 << SDRAM_ECC_RANGE_ADDR_SHIFT);
+
+ info->info.size = hw_size;
+}
+
+#ifdef CONFIG_ASPEED_ECC
+static void ast2600_sdrammc_ecc_enable(struct dram_info *info)
+{
+ struct ast2600_sdrammc_regs *regs = info->regs;
+ size_t conf_size;
+ u32 reg;
+
+ conf_size = CONFIG_ASPEED_ECC_SIZE * SDRAM_SIZE_1MB;
+ if (conf_size > info->info.size) {
+ printf("warning: ECC configured %dMB but actual size is %dMB\n",
+ CONFIG_ASPEED_ECC_SIZE,
+ info->info.size / SDRAM_SIZE_1MB);
+ conf_size = info->info.size;
+ } else if (conf_size == 0) {
+ conf_size = info->info.size;
+ }
+
+ info->info.size = (((conf_size / 9) * 8) >> 20) << 20;
+ writel(((info->info.size >> 20) - 1) << 20, &regs->ecc_range_ctrl);
+ reg = readl(&regs->config) | SDRAM_CONF_ECC_SETUP;
+ writel(reg, &regs->config);
+
+ writel(0, &regs->test_init_val);
+ writel(0x80000001, &regs->test_addr);
+ writel(0x221, &regs->ecc_test_ctrl);
+ while (0 == (readl(&regs->ecc_test_ctrl) & BIT(12)))
+ ;
+ writel(0, &regs->ecc_test_ctrl);
+ writel(BIT(31), &regs->intr_ctrl);
+ writel(0, &regs->intr_ctrl);
+}
+#endif
+
+static int ast2600_sdrammc_probe(struct udevice *dev)
+{
+ int ret;
+ u32 reg;
+ struct dram_info *priv = (struct dram_info *)dev_get_priv(dev);
+ struct ast2600_sdrammc_regs *regs = priv->regs;
+ struct udevice *clk_dev;
+
+ /* find SCU base address from clock device */
+ ret = uclass_get_device_by_driver(UCLASS_CLK,
+ DM_DRIVER_GET(aspeed_ast2600_scu), &clk_dev);
+ if (ret) {
+ debug("clock device not defined\n");
+ return ret;
+ }
+
+ priv->scu = devfdt_get_addr_ptr(clk_dev);
+ if (IS_ERR(priv->scu)) {
+ debug("%s(): can't get SCU\n", __func__);
+ return PTR_ERR(priv->scu);
+ }
+
+ if (readl(&priv->scu->dram_hdshk) & SCU_DRAM_HDSHK_RDY) {
+ printf("already initialized, ");
+ ast2600_sdrammc_update_size(priv);
+ return 0;
+ }
+
+ reg = readl(&priv->scu->mpll);
+ reg &= ~(SCU_PLL_BYPASS | SCU_PLL_DIV_MASK |
+ SCU_PLL_DENUM_MASK | SCU_PLL_NUM_MASK);
+ reg |= (SCU_PLL_RST | SCU_PLL_OFF | SCU_MPLL_FREQ_CFG);
+ writel(reg, &priv->scu->mpll);
+ writel(SCU_MPLL_EXT_CFG, &priv->scu->mpll_ext);
+ udelay(100);
+ reg &= ~(SCU_PLL_RST | SCU_PLL_OFF);
+ writel(reg, &priv->scu->mpll);
+
+ while ((readl(&priv->scu->mpll_ext) & BIT(31)) == 0)
+ ;
+
+ ast2600_sdrammc_unlock(priv);
+ ast2600_sdrammc_common_init(regs);
+L_ast2600_sdramphy_train:
+ ast2600_sdrammc_init_ddr4(priv);
+
+ /* make sure DDR-PHY is ready before access */
+ do {
+ reg = readl(priv->phy_status) & BIT(1);
+ } while (reg == 0);
+
+ if (ast2600_sdramphy_check_status(priv) != 0) {
+ printf("DDR4 PHY training fail, retrain\n");
+ goto L_ast2600_sdramphy_train;
+ }
+
+ ast2600_sdrammc_calc_size(priv);
+
+#ifndef CONFIG_ASPEED_BYPASS_SELFTEST
+ if (ast2600_sdrammc_test(priv) != 0) {
+ printf("%s: DDR4 init fail\n", __func__);
+ return -EINVAL;
+ }
+#endif
+
+#ifdef CONFIG_ASPEED_ECC
+ ast2600_sdrammc_ecc_enable(priv);
+#endif
+
+ writel(readl(&priv->scu->dram_hdshk) | SCU_DRAM_HDSHK_RDY,
+ &priv->scu->dram_hdshk);
+
+ clrbits_le32(&regs->intr_ctrl, MCR50_RESET_ALL_INTR);
+ ast2600_sdrammc_lock(priv);
+ return 0;
+}
+
+static int ast2600_sdrammc_of_to_plat(struct udevice *dev)
+{
+ struct dram_info *priv = dev_get_priv(dev);
+
+ priv->regs = (void *)(uintptr_t)devfdt_get_addr_index(dev, 0);
+ priv->phy_setting = (void *)(uintptr_t)devfdt_get_addr_index(dev, 1);
+ priv->phy_status = (void *)(uintptr_t)devfdt_get_addr_index(dev, 2);
+
+ priv->clock_rate = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+ "clock-frequency", 0);
+ if (!priv->clock_rate) {
+ debug("DDR Clock Rate not defined\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ast2600_sdrammc_get_info(struct udevice *dev, struct ram_info *info)
+{
+ struct dram_info *priv = dev_get_priv(dev);
+
+ *info = priv->info;
+
+ return 0;
+}
+
+static struct ram_ops ast2600_sdrammc_ops = {
+ .get_info = ast2600_sdrammc_get_info,
+};
+
+static const struct udevice_id ast2600_sdrammc_ids[] = {
+ { .compatible = "aspeed,ast2600-sdrammc" },
+ { }
+};
+
+U_BOOT_DRIVER(sdrammc_ast2600) = {
+ .name = "aspeed_ast2600_sdrammc",
+ .id = UCLASS_RAM,
+ .of_match = ast2600_sdrammc_ids,
+ .ops = &ast2600_sdrammc_ops,
+ .of_to_plat = ast2600_sdrammc_of_to_plat,
+ .probe = ast2600_sdrammc_probe,
+ .priv_auto = sizeof(struct dram_info),
+};
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 33c2736554e..f5b3f8826fb 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -81,6 +81,15 @@ config RESET_AST2500
Say Y if you want to control reset signals of different peripherals
through System Control Unit (SCU).
+config RESET_AST2600
+ bool "Reset controller driver for AST2600 SoCs"
+ depends on DM_RESET
+ default y if ASPEED_AST2600
+ help
+ Support for reset controller on AST2600 SoC.
+ Say Y if you want to control reset signals of different peripherals
+ through System Control Unit (SCU).
+
config RESET_ROCKCHIP
bool "Reset controller driver for Rockchip SoCs"
depends on DM_RESET && ARCH_ROCKCHIP && CLK
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index fa52aa33291..8a0f5280761 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_AST2500) += reset-ast2500.o
+obj-$(CONFIG_RESET_AST2600) += reset-ast2600.o
obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
diff --git a/drivers/reset/reset-ast2600.c b/drivers/reset/reset-ast2600.c
new file mode 100644
index 00000000000..f64adaf74e8
--- /dev/null
+++ b/drivers/reset/reset-ast2600.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020 ASPEED Technology Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <misc.h>
+#include <reset.h>
+#include <reset-uclass.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <asm/arch/scu_ast2600.h>
+
+struct ast2600_reset_priv {
+ struct ast2600_scu *scu;
+};
+
+static int ast2600_reset_request(struct reset_ctl *reset_ctl)
+{
+ debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl,
+ reset_ctl->dev, reset_ctl->id);
+
+ return 0;
+}
+
+static int ast2600_reset_free(struct reset_ctl *reset_ctl)
+{
+ debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl,
+ reset_ctl->dev, reset_ctl->id);
+
+ return 0;
+}
+
+static int ast2600_reset_assert(struct reset_ctl *reset_ctl)
+{
+ struct ast2600_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+ struct ast2600_scu *scu = priv->scu;
+
+ debug("%s: reset_ctl->id: %lu\n", __func__, reset_ctl->id);
+
+ if (reset_ctl->id < 32)
+ writel(BIT(reset_ctl->id), scu->modrst_ctrl1);
+ else
+ writel(BIT(reset_ctl->id - 32), scu->modrst_ctrl2);
+
+ return 0;
+}
+
+static int ast2600_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ struct ast2600_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+ struct ast2600_scu *scu = priv->scu;
+
+ debug("%s: reset_ctl->id: %lu\n", __func__, reset_ctl->id);
+
+ if (reset_ctl->id < 32)
+ writel(BIT(reset_ctl->id), scu->modrst_clr1);
+ else
+ writel(BIT(reset_ctl->id - 32), scu->modrst_clr2);
+
+ return 0;
+}
+
+static int ast2600_reset_probe(struct udevice *dev)
+{
+ int rc;
+ struct ast2600_reset_priv *priv = dev_get_priv(dev);
+ struct udevice *scu_dev;
+
+ /* get SCU base from clock device */
+ rc = uclass_get_device_by_driver(UCLASS_CLK,
+ DM_DRIVER_GET(aspeed_ast2600_scu), &scu_dev);
+ if (rc) {
+ debug("%s: clock device not found, rc=%d\n", __func__, rc);
+ return rc;
+ }
+
+ priv->scu = devfdt_get_addr_ptr(scu_dev);
+ if (IS_ERR_OR_NULL(priv->scu)) {
+ debug("%s: invalid SCU base pointer\n", __func__);
+ return PTR_ERR(priv->scu);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id ast2600_reset_ids[] = {
+ { .compatible = "aspeed,ast2600-reset" },
+ { }
+};
+
+struct reset_ops ast2600_reset_ops = {
+ .request = ast2600_reset_request,
+ .rfree = ast2600_reset_free,
+ .rst_assert = ast2600_reset_assert,
+ .rst_deassert = ast2600_reset_deassert,
+};
+
+U_BOOT_DRIVER(ast2600_reset) = {
+ .name = "ast2600_reset",
+ .id = UCLASS_RESET,
+ .of_match = ast2600_reset_ids,
+ .probe = ast2600_reset_probe,
+ .ops = &ast2600_reset_ops,
+ .priv_auto = sizeof(struct ast2600_reset_priv),
+};
diff --git a/drivers/sysreset/sysreset_ast.c b/drivers/sysreset/sysreset_ast.c
index ee941c77706..d747ed00a7f 100644
--- a/drivers/sysreset/sysreset_ast.c
+++ b/drivers/sysreset/sysreset_ast.c
@@ -12,6 +12,7 @@
#include <asm/io.h>
#include <asm/arch/wdt.h>
#include <linux/err.h>
+#include <hang.h>
static int ast_sysreset_request(struct udevice *dev, enum sysreset_t type)
{
@@ -33,11 +34,15 @@ static int ast_sysreset_request(struct udevice *dev, enum sysreset_t type)
return -EPROTONOSUPPORT;
}
+#if !defined(CONFIG_SPL_BUILD)
ret = wdt_expire_now(wdt, reset_mode);
if (ret) {
debug("Sysreset failed: %d", ret);
return ret;
}
+#else
+ hang();
+#endif
return -EINPROGRESS;
}
diff --git a/drivers/timer/mtk_timer.c b/drivers/timer/mtk_timer.c
index 448a76a7e1f..f6b97f868c7 100644
--- a/drivers/timer/mtk_timer.c
+++ b/drivers/timer/mtk_timer.c
@@ -61,6 +61,16 @@ static int mtk_timer_probe(struct udevice *dev)
if (!uc_priv->clock_rate)
return -EINVAL;
+ /*
+ * Initialize the timer:
+ * 1. set clock source to system clock with clock divider setting to 1
+ * 2. set timer mode to free running
+ * 3. reset timer counter to 0 then enable the timer
+ */
+ writel(GPT4_CLK_SYS | GPT4_CLK_DIV1, priv->base + MTK_GPT4_CLK);
+ writel(GPT4_FREERUN | GPT4_CLEAR | GPT4_ENABLE,
+ priv->base + MTK_GPT4_CTRL);
+
return 0;
}
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 4532a40e458..d5edabf3eff 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -86,6 +86,15 @@ config WDT_ASPEED
It currently does not support Boot Flash Addressing Mode Detection or
Second Boot.
+config WDT_AST2600
+ bool "Aspeed AST2600 watchdog timer support"
+ depends on WDT
+ default y if ASPEED_AST2600
+ help
+ Select this to enable watchdog timer for Aspeed ast2500/ast2400 devices.
+ The watchdog timer is stopped when initialized. It performs reset, either
+ full SoC reset or CPU or just some peripherals, based on the flags.
+
config WDT_AT91
bool "AT91 watchdog timer support"
depends on WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 01b8231f2bf..cbb6d8407cc 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_$(SPL_TPL_)WDT) += wdt-uclass.o
obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
+obj-$(CONFIG_WDT_AST2600) += ast2600_wdt.o
obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o
obj-$(CONFIG_WDT_CORTINA) += cortina_wdt.o
obj-$(CONFIG_WDT_ORION) += orion_wdt.o
diff --git a/drivers/watchdog/ast2600_wdt.c b/drivers/watchdog/ast2600_wdt.c
new file mode 100644
index 00000000000..bc9842089be
--- /dev/null
+++ b/drivers/watchdog/ast2600_wdt.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020 Aspeed Technology, Inc
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <wdt.h>
+#include <asm/io.h>
+#include <asm/arch/wdt_ast2600.h>
+#include <linux/err.h>
+
+struct ast2600_wdt_priv {
+ struct ast2600_wdt *regs;
+};
+
+static int ast2600_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
+{
+ struct ast2600_wdt_priv *priv = dev_get_priv(dev);
+ struct ast2600_wdt *wdt = priv->regs;
+
+ /* WDT counts in the 1MHz frequency, namely 1us */
+ writel((u32)(timeout_ms * 1000), &wdt->counter_reload_val);
+ writel(WDT_COUNTER_RESTART_VAL, &wdt->counter_restart);
+ writel(WDT_CTRL_EN | WDT_CTRL_RESET, &wdt->ctrl);
+
+ return 0;
+}
+
+static int ast2600_wdt_stop(struct udevice *dev)
+{
+ struct ast2600_wdt_priv *priv = dev_get_priv(dev);
+ struct ast2600_wdt *wdt = priv->regs;
+
+ clrbits_le32(&wdt->ctrl, WDT_CTRL_EN);
+
+ writel(WDT_RESET_MASK1_DEFAULT, &wdt->reset_mask1);
+ writel(WDT_RESET_MASK2_DEFAULT, &wdt->reset_mask2);
+
+ return 0;
+}
+
+static int ast2600_wdt_reset(struct udevice *dev)
+{
+ struct ast2600_wdt_priv *priv = dev_get_priv(dev);
+ struct ast2600_wdt *wdt = priv->regs;
+
+ writel(WDT_COUNTER_RESTART_VAL, &wdt->counter_restart);
+
+ return 0;
+}
+
+static int ast2600_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+ int ret;
+ struct ast2600_wdt_priv *priv = dev_get_priv(dev);
+ struct ast2600_wdt *wdt = priv->regs;
+
+ ret = ast2600_wdt_start(dev, 1, flags);
+ if (ret)
+ return ret;
+
+ while (readl(&wdt->ctrl) & WDT_CTRL_EN)
+ ;
+
+ return ast2600_wdt_stop(dev);
+}
+
+static int ast2600_wdt_of_to_plat(struct udevice *dev)
+{
+ struct ast2600_wdt_priv *priv = dev_get_priv(dev);
+
+ priv->regs = dev_read_addr_ptr(dev);
+ if (!priv->regs)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct wdt_ops ast2600_wdt_ops = {
+ .start = ast2600_wdt_start,
+ .reset = ast2600_wdt_reset,
+ .stop = ast2600_wdt_stop,
+ .expire_now = ast2600_wdt_expire_now,
+};
+
+static const struct udevice_id ast2600_wdt_ids[] = {
+ { .compatible = "aspeed,ast2600-wdt" },
+ { }
+};
+
+static int ast2600_wdt_probe(struct udevice *dev)
+{
+ debug("%s() wdt%u\n", __func__, dev_seq(dev));
+ ast2600_wdt_stop(dev);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(ast2600_wdt) = {
+ .name = "ast2600_wdt",
+ .id = UCLASS_WDT,
+ .of_match = ast2600_wdt_ids,
+ .probe = ast2600_wdt_probe,
+ .priv_auto = sizeof(struct ast2600_wdt_priv),
+ .of_to_plat = ast2600_wdt_of_to_plat,
+ .ops = &ast2600_wdt_ops,
+};