blob: 851e70d2f4f32c64e5685ff1cb247d625a1e2a7d [file] [log] [blame]
/****************************************************************************
* 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));
}