imx8m: add clk support for i.MX8MM
Introduce clk implementation for i.MX8MM, including pll configuration,
ccm configuration. Mostly will be done clk dm driver,
but such as DRAM part, we still use non clk dm driver, because we
have limited sram.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
diff --git a/arch/arm/mach-imx/imx8m/clock_imx8mm.c b/arch/arm/mach-imx/imx8m/clock_imx8mm.c
new file mode 100644
index 0000000..ee44ba7
--- /dev/null
+++ b/arch/arm/mach-imx/imx8m/clock_imx8mm.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018-2019 NXP
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <common.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <clk-uclass.h>
+#include <dt-bindings/clock/imx8mm-clock.h>
+#include <div64.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct anamix_pll *ana_pll = (struct anamix_pll *)ANATOP_BASE_ADDR;
+
+void enable_ocotp_clk(unsigned char enable)
+{
+ struct clk *clkp;
+ int ret;
+
+ ret = clk_get_by_id(IMX8MM_CLK_OCOTP_ROOT, &clkp);
+ if (ret) {
+ printf("%s: err: %d\n", __func__, ret);
+ return;
+ }
+
+ enable ? clk_enable(clkp) : clk_disable(clkp);
+}
+
+int enable_i2c_clk(unsigned char enable, unsigned i2c_num)
+{
+ struct clk *clkp;
+ int ret;
+
+ ret = clk_get_by_id(IMX8MM_CLK_I2C1_ROOT + i2c_num, &clkp);
+ if (ret) {
+ printf("%s: err: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return enable ? clk_enable(clkp) : clk_disable(clkp);
+}
+
+#ifdef CONFIG_SPL_BUILD
+static struct imx_int_pll_rate_table imx8mm_fracpll_tbl[] = {
+ PLL_1443X_RATE(800000000U, 300, 9, 0, 0),
+ PLL_1443X_RATE(750000000U, 250, 8, 0, 0),
+ PLL_1443X_RATE(650000000U, 325, 3, 2, 0),
+ PLL_1443X_RATE(600000000U, 300, 3, 2, 0),
+ PLL_1443X_RATE(594000000U, 99, 1, 2, 0),
+ PLL_1443X_RATE(400000000U, 300, 9, 1, 0),
+ PLL_1443X_RATE(266666667U, 400, 9, 2, 0),
+ PLL_1443X_RATE(167000000U, 334, 3, 4, 0),
+ PLL_1443X_RATE(100000000U, 300, 9, 3, 0),
+};
+
+int fracpll_configure(enum pll_clocks pll, u32 freq)
+{
+ int i;
+ u32 tmp, div_val;
+ void *pll_base;
+ struct imx_int_pll_rate_table *rate;
+
+ for (i = 0; i < ARRAY_SIZE(imx8mm_fracpll_tbl); i++) {
+ if (freq == imx8mm_fracpll_tbl[i].rate)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(imx8mm_fracpll_tbl)) {
+ printf("No matched freq table %u\n", freq);
+ return -EINVAL;
+ }
+
+ rate = &imx8mm_fracpll_tbl[i];
+
+ switch (pll) {
+ case ANATOP_DRAM_PLL:
+ setbits_le32(GPC_BASE_ADDR + 0xEC, 1 << 7);
+ setbits_le32(GPC_BASE_ADDR + 0xF8, 1 << 5);
+ writel(SRC_DDR1_ENABLE_MASK, SRC_BASE_ADDR + 0x1004);
+
+ pll_base = &ana_pll->dram_pll_gnrl_ctl;
+ break;
+ case ANATOP_VIDEO_PLL:
+ pll_base = &ana_pll->video_pll1_gnrl_ctl;
+ break;
+ default:
+ return 0;
+ }
+ /* Bypass clock and set lock to pll output lock */
+ tmp = readl(pll_base);
+ tmp |= BYPASS_MASK;
+ writel(tmp, pll_base);
+
+ /* Enable RST */
+ tmp &= ~RST_MASK;
+ writel(tmp, pll_base);
+
+ div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
+ (rate->sdiv << SDIV_SHIFT);
+ writel(div_val, pll_base + 4);
+ writel(rate->kdiv << KDIV_SHIFT, pll_base + 8);
+
+ __udelay(100);
+
+ /* Disable RST */
+ tmp |= RST_MASK;
+ writel(tmp, pll_base);
+
+ /* Wait Lock*/
+ while (!(readl(pll_base) & LOCK_STATUS))
+ ;
+
+ /* Bypass */
+ tmp &= ~BYPASS_MASK;
+ writel(tmp, pll_base);
+
+ return 0;
+}
+
+void dram_pll_init(ulong pll_val)
+{
+ fracpll_configure(ANATOP_DRAM_PLL, pll_val);
+}
+
+static struct dram_bypass_clk_setting imx8mm_dram_bypass_tbl[] = {
+ DRAM_BYPASS_ROOT_CONFIG(MHZ(100), 2, CLK_ROOT_PRE_DIV1, 2,
+ CLK_ROOT_PRE_DIV2),
+ DRAM_BYPASS_ROOT_CONFIG(MHZ(250), 3, CLK_ROOT_PRE_DIV2, 2,
+ CLK_ROOT_PRE_DIV2),
+ DRAM_BYPASS_ROOT_CONFIG(MHZ(400), 1, CLK_ROOT_PRE_DIV2, 3,
+ CLK_ROOT_PRE_DIV2),
+};
+
+void dram_enable_bypass(ulong clk_val)
+{
+ int i;
+ struct dram_bypass_clk_setting *config;
+
+ for (i = 0; i < ARRAY_SIZE(imx8mm_dram_bypass_tbl); i++) {
+ if (clk_val == imx8mm_dram_bypass_tbl[i].clk)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(imx8mm_dram_bypass_tbl)) {
+ printf("No matched freq table %lu\n", clk_val);
+ return;
+ }
+
+ config = &imx8mm_dram_bypass_tbl[i];
+
+ clock_set_target_val(DRAM_ALT_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(config->alt_root_sel) |
+ CLK_ROOT_PRE_DIV(config->alt_pre_div));
+ clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(config->apb_root_sel) |
+ CLK_ROOT_PRE_DIV(config->apb_pre_div));
+ clock_set_target_val(DRAM_SEL_CFG, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(1));
+}
+
+void dram_disable_bypass(void)
+{
+ clock_set_target_val(DRAM_SEL_CFG, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(0));
+ clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(4) |
+ CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV5));
+}
+#endif
+
+void init_uart_clk(u32 index)
+{
+ /*
+ * set uart clock root
+ * 24M OSC
+ */
+ switch (index) {
+ case 0:
+ clock_enable(CCGR_UART1, 0);
+ clock_set_target_val(UART1_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(0));
+ clock_enable(CCGR_UART1, 1);
+ return;
+ case 1:
+ clock_enable(CCGR_UART2, 0);
+ clock_set_target_val(UART2_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(0));
+ clock_enable(CCGR_UART2, 1);
+ return;
+ case 2:
+ clock_enable(CCGR_UART3, 0);
+ clock_set_target_val(UART3_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(0));
+ clock_enable(CCGR_UART3, 1);
+ return;
+ case 3:
+ clock_enable(CCGR_UART4, 0);
+ clock_set_target_val(UART4_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(0));
+ clock_enable(CCGR_UART4, 1);
+ return;
+ default:
+ printf("Invalid uart index\n");
+ return;
+ }
+}
+
+void init_wdog_clk(void)
+{
+ clock_enable(CCGR_WDOG1, 0);
+ clock_enable(CCGR_WDOG2, 0);
+ clock_enable(CCGR_WDOG3, 0);
+ clock_set_target_val(WDOG_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(0));
+ clock_enable(CCGR_WDOG1, 1);
+ clock_enable(CCGR_WDOG2, 1);
+ clock_enable(CCGR_WDOG3, 1);
+}
+
+int clock_init(void)
+{
+ u32 val_cfg0;
+
+ /*
+ * The gate is not exported to clk tree, so configure them here.
+ * According to ANAMIX SPEC
+ * sys pll1 fixed at 800MHz
+ * sys pll2 fixed at 1GHz
+ * Here we only enable the outputs.
+ */
+ val_cfg0 = readl(&ana_pll->sys_pll1_gnrl_ctl);
+ val_cfg0 |= INTPLL_CLKE_MASK | INTPLL_DIV2_CLKE_MASK |
+ INTPLL_DIV3_CLKE_MASK | INTPLL_DIV4_CLKE_MASK |
+ INTPLL_DIV5_CLKE_MASK | INTPLL_DIV6_CLKE_MASK |
+ INTPLL_DIV8_CLKE_MASK | INTPLL_DIV10_CLKE_MASK |
+ INTPLL_DIV20_CLKE_MASK;
+ writel(val_cfg0, &ana_pll->sys_pll1_gnrl_ctl);
+
+ val_cfg0 = readl(&ana_pll->sys_pll2_gnrl_ctl);
+ val_cfg0 |= INTPLL_CLKE_MASK | INTPLL_DIV2_CLKE_MASK |
+ INTPLL_DIV3_CLKE_MASK | INTPLL_DIV4_CLKE_MASK |
+ INTPLL_DIV5_CLKE_MASK | INTPLL_DIV6_CLKE_MASK |
+ INTPLL_DIV8_CLKE_MASK | INTPLL_DIV10_CLKE_MASK |
+ INTPLL_DIV20_CLKE_MASK;
+ writel(val_cfg0, &ana_pll->sys_pll2_gnrl_ctl);
+
+ /* config GIC to sys_pll2_100m */
+ clock_enable(CCGR_GIC, 0);
+ clock_set_target_val(GIC_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(3));
+ clock_enable(CCGR_GIC, 1);
+
+ clock_set_target_val(NAND_USDHC_BUS_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(1));
+
+ clock_enable(CCGR_DDR1, 0);
+ clock_set_target_val(DRAM_ALT_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(1));
+ clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON |
+ CLK_ROOT_SOURCE_SEL(1));
+ clock_enable(CCGR_DDR1, 1);
+
+ init_wdog_clk();
+
+ clock_enable(CCGR_TEMP_SENSOR, 1);
+
+ clock_enable(CCGR_SEC_DEBUG, 1);
+
+ return 0;
+};
+
+u32 imx_get_uartclk(void)
+{
+ return 24000000U;
+}
+
+u32 mxc_get_clock(enum mxc_clock clk)
+{
+ struct clk *clkp;
+ int ret;
+
+ switch (clk) {
+ case MXC_IPG_CLK:
+ ret = clk_get_by_id(IMX8MM_CLK_IPG_ROOT, &clkp);
+ if (ret)
+ return 0;
+ return clk_get_rate(clkp);
+ case MXC_ARM_CLK:
+ ret = clk_get_by_id(IMX8MM_CLK_A53_DIV, &clkp);
+ if (ret)
+ return 0;
+ return clk_get_rate(clkp);
+ default:
+ printf("%s: %d not supported\n", __func__, clk);
+ }
+
+ return 0;
+}