| /**************************************************************************** |
| * arch/arm/src/mx8mp/mx8mp_ccm.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include "mx8mp_ccm.h" |
| #include "hardware/mx8mp_rdc.h" |
| |
| #include <debug.h> |
| #include "arm_internal.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const uint32_t CLK_ROOT_MAP[CLOCK_ROOT_MAP_SIZE][8] = |
| { |
| ARM_A53_CLK_MUX, |
| ARM_M7_CLK_MUX, |
| ML_CLK_MUX, |
| GPU3D_CORE_CLK_MUX, |
| GPU3D_SHADER_CLK_MUX, |
| GPU2D_CLK_MUX, |
| AUDIO_AXI_CLK_MUX, |
| HSIO_AXI_CLK_MUX, |
| MEDIA_ISP_CLK_MUX, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| MAIN_AXI_CLK_MUX, |
| ENET_AXI_CLK_MUX, |
| NAND_USDHC_BUS_CLK_MUX, |
| VPU_BUS_CLK_MUX, |
| MEDIA_AXI_CLK_MUX, |
| MEDIA_APB_CLK_MUX, |
| HDMI_APB_CLK_MUX, |
| HDMI_AXI_CLK_MUX, |
| GPU_AXI_CLK_MUX, |
| GPU_AHB_CLK_MUX, |
| NOC_CLK_MUX, |
| NOC_IO_CLK_MUX, |
| ML_AXI_CLK_MUX, |
| ML_AHB_CLK_MUX, |
| {}, |
| {}, |
| AHB_CLK_MUX, |
| IPG_CLK_MUX, |
| AUDIO_AHB_CLK_MUX, |
| {}, |
| {}, |
| {}, |
| MEDIA_DISP2_CLK_MUX, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| DRAM_SEL_CFG_MUX, |
| ARM_A53_CLK_ROOT_SEL_MUX, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| {}, |
| DRAM_ALT_CLK_MUX, |
| DRAM_APB_CLK_MUX, |
| VPU_G1_CLK_MUX, |
| VPU_G2_CLK_MUX, |
| CAN1_CLK_MUX, |
| CAN2_CLK_MUX, |
| MEMREPAIR_CLK_MUX, |
| PCIE_PHY_CLK_MUX, |
| PCIE_AUX_CLK_MUX, |
| I2C5_CLK_MUX, |
| I2C6_CLK_MUX, |
| SAI1_CLK_MUX, |
| SAI2_CLK_MUX, |
| SAI3_CLK_MUX, |
| {}, |
| SAI5_CLK_MUX, |
| SAI6_CLK_MUX, |
| ENET_QOS_CLK_MUX, |
| ENET_QOS_TIMER_CLK_MUX, |
| ENET_REF_CLK_MUX, |
| ENET_TIMER_CLK_MUX, |
| ENET_PHY_REF_CLK_MUX, |
| NAND_CLK_MUX, |
| QSPI_CLK_MUX, |
| USDHC1_CLK_MUX, |
| USDHC2_CLK_MUX, |
| I2C1_CLK_MUX, |
| I2C2_CLK_MUX, |
| I2C3_CLK_MUX, |
| I2C4_CLK_MUX, |
| UART1_CLK_MUX, |
| UART2_CLK_MUX, |
| UART3_CLK_MUX, |
| UART4_CLK_MUX, |
| {}, |
| {}, |
| GIC_CLK_MUX, |
| ECSPI1_CLK_MUX, |
| ECSPI2_CLK_MUX, |
| PWM1_CLK_MUX, |
| PWM2_CLK_MUX, |
| PWM3_CLK_MUX, |
| PWM4_CLK_MUX, |
| GPT1_CLK_MUX, |
| GPT2_CLK_MUX, |
| GPT3_CLK_MUX, |
| GPT4_CLK_MUX, |
| GPT5_CLK_MUX, |
| GPT6_CLK_MUX, |
| TRACE_CLK_MUX, |
| WDOG_CLK_MUX, |
| WRCLK_CLK_MUX, |
| IPP_DO_CLKO1_MUX, |
| IPP_DO_CLKO2_MUX, |
| HDMI_FDCC_TST_CLK_MUX, |
| HDMI_27M_CLK_MUX, |
| HDMI_REF_266M_CLK_MUX, |
| USDHC3_CLK_MUX, |
| MEDIA_CAM1_PIX_CLK_MUX, |
| MEDIA_MIPI_PHY1_REF_CLK_MUX, |
| MEDIA_DISP1_PIX_CLK_MUX, |
| MEDIA_CAM2_PIX_CLK_MUX, |
| MEDIA_LDB_CLK_MUX, |
| {}, |
| {}, |
| {}, |
| MEDIA_MIPI_TEST_BYTE_CLK_MUX, |
| ECSPI3_CLK_MUX, |
| PDM_CLK_MUX, |
| VPU_VC8000E_CLK_MUX, |
| SAI7_CLK_MUX, |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static uint32_t root_frequency(uint32_t freq_in, |
| uint32_t main_div, |
| uint32_t pre_div, |
| uint32_t scaler, |
| uint32_t dsm) |
| { |
| /* Reference manual chapter 5.1.5.4.4 SSCG and Fractional PLLs |
| * Fout = ((m + k /65536) * Fin) / (p * 2^s) |
| * with m -> main divider, p -> pre-divider, s -> post-scaler and k -> DSM |
| * => ((m * 65536 + k) * Fin) / (65536 * p * (1 << s)) |
| */ |
| |
| return (uint32_t)(((main_div * 65536llu + dsm) * freq_in) / |
| (65536llu * pre_div * (1 << scaler))); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| uint32_t mx8mp_ccm_get_root_clock(int clk_root_src) |
| { |
| uint32_t pll; |
| uint32_t dsm; |
| uint32_t final_div; |
| uint32_t fdiv0; |
| uint32_t main_div; |
| uint32_t pre_div; |
| uint32_t post_div; |
| uint32_t root_freq; |
| |
| dsm = 0; |
| final_div = 1; |
| |
| switch (clk_root_src) |
| { |
| case OSC_24M_REF_CLK: |
| return 24000000; |
| |
| case OSC_32K_REF_CLK: |
| return 32000; |
| |
| case ARM_PLL_CLK: |
| { |
| pll = CCM_ANALOG_ARM_PLL; |
| } |
| break; |
| |
| case DRAM_PLL1_CLK: |
| { |
| pll = CCM_ANALOG_DRAM_PLL; |
| dsm = (getreg32(pll + CCM_ANALOG_FDIV1) & CCM_FDIV1_DSM_MASK) |
| >> CCM_FDIV1_DSM_SHIFT; |
| } |
| break; |
| |
| case VPU_PLL_CLK: |
| { |
| pll = CCM_ANALOG_VPU_PLL; |
| } |
| break; |
| |
| case GPU_PLL_CLK: |
| { |
| pll = CCM_ANALOG_GPU_PLL; |
| } |
| break; |
| |
| case SYSTEM_PLL1_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV2_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 2; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV3_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 3; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV4_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 4; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV5_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 5; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV6_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 6; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV8_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 8; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV10_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 10; |
| } |
| break; |
| |
| case SYSTEM_PLL1_DIV20_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL1; |
| final_div = 20; |
| } |
| break; |
| |
| case SYSTEM_PLL2_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV2_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 2; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV3_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 3; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV4_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 4; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV5_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 5; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV6_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 6; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV8_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 8; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV10_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 10; |
| } |
| break; |
| |
| case SYSTEM_PLL2_DIV20_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL2; |
| final_div = 20; |
| break; |
| } |
| |
| case SYSTEM_PLL3_CLK: |
| { |
| pll = CCM_ANALOG_SYSTEM_PLL3; |
| } |
| break; |
| |
| case AUDIO_PLL1_CLK: |
| { |
| pll = CCM_ANALOG_AUDIO_PLL1; |
| dsm = (getreg32(pll + CCM_ANALOG_FDIV1) & CCM_FDIV1_DSM_MASK) |
| >> CCM_FDIV1_DSM_SHIFT; |
| } |
| break; |
| |
| case AUDIO_PLL2_CLK: |
| { |
| pll = CCM_ANALOG_AUDIO_PLL2; |
| dsm = (getreg32(pll + CCM_ANALOG_FDIV1) & CCM_FDIV1_DSM_MASK) |
| >> CCM_FDIV1_DSM_SHIFT; |
| } |
| break; |
| |
| case VIDEO_PLL_CLK: |
| { |
| pll = CCM_ANALOG_VIDEO_PLL1; |
| dsm = (getreg32(pll + CCM_ANALOG_FDIV1) & CCM_FDIV1_DSM_MASK) |
| >> CCM_FDIV1_DSM_SHIFT; |
| } |
| break; |
| |
| default: |
| return 0; |
| } |
| |
| fdiv0 = getreg32(pll + CCM_ANALOG_FDIV0); |
| main_div = (fdiv0 & CCM_FDIV0_MAIN_DIV_MASK) >> CCM_FDIV0_MAIN_DIV_SHIFT; |
| pre_div = (fdiv0 & CCM_FDIV0_PRE_DIV_MASK) >> CCM_FDIV0_PRE_DIV_SHIFT; |
| post_div = (fdiv0 & CCM_FDIV0_POST_DIV_MASK) >> CCM_FDIV0_POST_DIV_SHIFT; |
| root_freq = root_frequency(24000000, |
| main_div, |
| pre_div, |
| post_div, |
| dsm); |
| return root_freq / final_div; |
| } |
| |
| uint32_t mx8mp_ccm_get_clock(int clk_index) |
| { |
| uint32_t reg; |
| uint32_t is_enable; |
| uint32_t pre_podf; |
| uint32_t post_podf; |
| uint32_t mux; |
| uint32_t src_clk; |
| |
| reg = getreg32(CCM_CLK_ROOT_BASE + 128 * clk_index); |
| is_enable = reg & CCM_CLK_ROOT_ENABLE; |
| pre_podf = (reg & CCM_CLK_ROOT_PRE_PODF_MASK) |
| >> CCM_CLK_ROOT_PRE_PODF_SHIFT; |
| post_podf = (reg & CCM_CLK_ROOT_POST_PODF_MASK) |
| >> CCM_CLK_ROOT_POST_PODF_SHIFT; |
| mux = (reg & CCM_CLK_ROOT_MUX_MASK) |
| >> CCM_CLK_ROOT_MUX_SHIFT; |
| |
| if (!is_enable) |
| { |
| return 0; |
| } |
| |
| src_clk = mx8mp_ccm_get_root_clock(CLK_ROOT_MAP[clk_index][mux]); |
| return src_clk / (pre_podf + 1) / (post_podf + 1); |
| } |
| |
| int mx8mp_ccm_configure_clock(int clk_index, |
| int clk_root_src, |
| uint32_t pre_div, |
| uint32_t post_div) |
| { |
| uint32_t reg; |
| uint32_t value; |
| int mux = 8; |
| int i; |
| |
| for (i = 0; i < 8; ++i) |
| { |
| if (CLK_ROOT_MAP[clk_index][i] == clk_root_src) |
| { |
| mux = i; |
| break; |
| } |
| } |
| |
| if (mux == 8) |
| { |
| /* cannot find a mux for the desired clock root: |
| * skip the configuration |
| */ |
| |
| return -1; |
| } |
| |
| reg = CCM_CLK_ROOT_BASE + 128 * clk_index; |
| value = getreg32(reg) & CCM_CLK_ROOT_ENABLE; |
| value |= (((pre_div - 1) << CCM_CLK_ROOT_PRE_PODF_SHIFT) |
| & CCM_CLK_ROOT_PRE_PODF_MASK); |
| value |= (((post_div - 1) << CCM_CLK_ROOT_POST_PODF_SHIFT) |
| & CCM_CLK_ROOT_POST_PODF_MASK); |
| value |= ((mux << CCM_CLK_ROOT_MUX_SHIFT) |
| & CCM_CLK_ROOT_MUX_MASK); |
| putreg32(value, reg); |
| |
| return 0; |
| } |
| |
| void mx8mp_ccm_gate_clock(int gate_index, uint32_t value) |
| { |
| uint32_t reg = (CCM_CCGR_BASE + gate_index * 16); |
| putreg32(value, reg); |
| } |
| |
| void mx8mp_ccm_enable_clock(int clk_index) |
| { |
| uint32_t reg = CCM_CLK_ROOT_BASE + 128 * clk_index; |
| modreg32(CCM_CLK_ROOT_ENABLE, CCM_CLK_ROOT_ENABLE, reg); |
| } |
| |
| void mx8mp_ccm_gate_pll(int pll_index, uint32_t value) |
| { |
| uint32_t reg = (CCM_PLL_BASE + pll_index * 16); |
| putreg32(value, reg); |
| } |
| |
| void mx8mp_ccm_configure_pll(int pll_index, |
| uint32_t main_div, |
| uint32_t pre_div, |
| uint32_t post_div, |
| uint32_t dsm) |
| { |
| uint32_t reg_genctrl; |
| uint32_t reg_fdiv0; |
| uint32_t reg_fdiv1; |
| uint32_t genctrl; |
| uint32_t fdiv0; |
| |
| /* Associate pll_index (pll gating) to |
| * pll configuration register in CCM_ANALOG |
| */ |
| |
| switch (pll_index) |
| { |
| case ARM_PLL_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_ARM_PLL; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = 0; |
| } |
| break; |
| |
| case GPU_PLL_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_GPU_PLL; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = 0; |
| } |
| break; |
| |
| case VPU_PLL_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_VPU_PLL; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = 0; |
| } |
| break; |
| |
| case DRAM_PLL1_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_DRAM_PLL; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = 0; |
| } |
| break; |
| |
| case SYSTEM_PLL1_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_SYSTEM_PLL1; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = 0; |
| } |
| break; |
| |
| case SYSTEM_PLL2_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_SYSTEM_PLL2; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = 0; |
| } |
| break; |
| |
| case SYSTEM_PLL3_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_SYSTEM_PLL3; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = 0; |
| } |
| break; |
| |
| case AUDIO_PLL1_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_AUDIO_PLL1; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = reg_genctrl + CCM_ANALOG_FDIV1; |
| } |
| break; |
| |
| case AUDIO_PLL2_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_ARM_PLL; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = reg_genctrl + CCM_ANALOG_FDIV1; |
| } |
| break; |
| |
| case VIDEO_PLL_CLK: |
| { |
| reg_genctrl = CCM_ANALOG_ARM_PLL; |
| reg_fdiv0 = reg_genctrl + CCM_ANALOG_FDIV0; |
| reg_fdiv1 = reg_genctrl + CCM_ANALOG_FDIV1; |
| } |
| break; |
| |
| default: /* unknown PLL - skip configuration */ |
| return; |
| } |
| |
| /* Disable PLL, no bypass, clock ref on 24MHz */ |
| |
| genctrl = getreg32(reg_genctrl); |
| genctrl &= ~(CCM_PLL_RST | CCM_PLL_BYPASS | CCM_PLL_REF_CLK_SEL_MASK); |
| putreg32(genctrl, reg_genctrl); |
| |
| /* Apply dividers */ |
| |
| fdiv0 = 0; |
| fdiv0 |= ((main_div << CCM_FDIV0_MAIN_DIV_SHIFT) |
| & CCM_FDIV0_MAIN_DIV_MASK); |
| fdiv0 |= ((pre_div << CCM_FDIV0_PRE_DIV_SHIFT) |
| & CCM_FDIV0_PRE_DIV_MASK); |
| fdiv0 |= ((post_div << CCM_FDIV0_POST_DIV_SHIFT) |
| & CCM_FDIV0_POST_DIV_MASK); |
| putreg32(fdiv0, reg_fdiv0); |
| |
| if (reg_fdiv1 != 0) |
| { |
| uint32_t fdiv1 = 0; |
| fdiv1 |= ((dsm << CCM_FDIV1_DSM_SHIFT) |
| & CCM_FDIV1_DSM_MASK); |
| putreg32(fdiv1, reg_fdiv1); |
| } |
| |
| /* Enable PLL */ |
| |
| genctrl = getreg32(reg_genctrl); |
| genctrl |= (CCM_PLL_CLKE | CCM_PLL_RST); |
| putreg32(genctrl, reg_genctrl); |
| |
| /* Wait for PLL to stabilize */ |
| |
| while (!(getreg32(reg_genctrl) & CCM_PLL_LOCK)); |
| } |