Files
xiuos/Ubiquitous/XiZi_AIoT/services/drivers/imx6q-sabrelite/clock/ccm_pll.c
T

222 lines
7.3 KiB
C

/*
* Copyright (c) 2011-2012, Freescale Semiconductor, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* o Neither the name of Freescale Semiconductor, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file ccm_pll.c
* @brief support imx6q soc ccm pll functions
* @version 3.0
* @author AIIT XUOS Lab
* @date 2023.09.08
*/
/*************************************************
File name: ccm_pll.c
Description: support imx6q soc ccm pll functions
Others:
History:
1. Date: 2023-08-28
Author: AIIT XUOS Lab
Modification:
1. Delete unnecessary functions;
2. Slim clock_gating_config to fit only uart and gpt(clock)
*************************************************/
#include "soc_memory_map.h"
#include <stdbool.h>
#include <stdint.h>
#include "regsccm.h"
#include "regsccmanalog.h"
#include "regsgpc.h"
#include "regsepit.h"
#include "regsgpt.h"
#include "ccm_pll.h"
////////////////////////////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////////////////////////////
const uint32_t PLL1_OUTPUT = 792000000;
const uint32_t PLL2_OUTPUT[] = { 528000000, 396000000, 352000000, 198000000, 594000000 };
const uint32_t PLL3_OUTPUT[] = { 480000000, 720000000, 540000000, 508235294, 454736842 };
const uint32_t PLL4_OUTPUT = 650000000;
const uint32_t PLL5_OUTPUT = 650000000;
////////////////////////////////////////////////////////////////////////////////
// Code
////////////////////////////////////////////////////////////////////////////////
uint32_t get_main_clock(main_clocks_t clock)
{
uint32_t ret_val = 0;
uint32_t pre_periph_clk_sel = HW_CCM_CBCMR.B.PRE_PERIPH_CLK_SEL;
switch (clock) {
case CPU_CLK:
ret_val = PLL1_OUTPUT;
break;
#if !defined(CHIP_MX6SL)
case AXI_CLK:
ret_val = PLL2_OUTPUT[pre_periph_clk_sel] / (HW_CCM_CBCDR.B.AXI_PODF + 1);
break;
case MMDC_CH0_AXI_CLK:
ret_val = PLL2_OUTPUT[pre_periph_clk_sel] / (HW_CCM_CBCDR.B.MMDC_CH0_AXI_PODF + 1);
break;
#endif
case AHB_CLK:
ret_val = PLL2_OUTPUT[pre_periph_clk_sel] / (HW_CCM_CBCDR.B.AHB_PODF + 1);
break;
case IPG_CLK:
ret_val = PLL2_OUTPUT[pre_periph_clk_sel] / (HW_CCM_CBCDR.B.AHB_PODF + 1) / (HW_CCM_CBCDR.B.IPG_PODF + 1);
break;
case IPG_PER_CLK:
ret_val = PLL2_OUTPUT[pre_periph_clk_sel] / (HW_CCM_CBCDR.B.AHB_PODF + 1) / (HW_CCM_CBCDR.B.IPG_PODF + 1) / (HW_CCM_CSCMR1.B.PERCLK_PODF + 1);
break;
#if !defined(CHIP_MX6SL)
case MMDC_CH1_AXI_CLK:
ret_val = PLL2_OUTPUT[pre_periph_clk_sel] / (HW_CCM_CBCDR.B.MMDC_CH1_AXI_PODF + 1);
break;
#endif
default:
break;
}
return ret_val;
}
uint32_t get_peri_clock(peri_clocks_t clock)
{
uint32_t ret_val = 0;
switch (clock) {
case UART1_MODULE_CLK:
case UART2_MODULE_CLK:
case UART3_MODULE_CLK:
case UART4_MODULE_CLK:
// UART source clock is a fixed PLL3 / 6
ret_val = PLL3_OUTPUT[0] / 6 / (HW_CCM_CSCDR1.B.UART_CLK_PODF + 1);
break;
// eCSPI clock:
// PLL3(480) -> /8 -> CSCDR2[ECSPI_CLK_PODF]
case SPI_CLK:
ret_val = PLL3_OUTPUT[0] / 8 / (HW_CCM_CSCDR2.B.ECSPI_CLK_PODF + 1);
break;
#if !defined(CHIP_MX6SL)
case RAWNAND_CLK:
ret_val = PLL3_OUTPUT[0] / (HW_CCM_CS2CDR.B.ENFC_CLK_PRED + 1) / (HW_CCM_CS2CDR.B.ENFC_CLK_PODF + 1);
break;
case CAN_CLK:
// For i.mx6dq/sdl CAN source clock is a fixed PLL3 / 8
ret_val = PLL3_OUTPUT[0] / 8 / (HW_CCM_CSCMR2.B.CAN_CLK_PODF + 1);
break;
#endif
default:
break;
}
return ret_val;
}
void ccm_set_lpm_wakeup_source(uint32_t irq_id, bool doEnable)
{
uint32_t reg_offset = 0;
uint32_t bit_offset = 0;
uint32_t gpc_imr = 0;
// calculate the offset of the register handling that interrupt ID
// ID starts at 32, so for instance ID=89 is handled by IMR2 because
// the integer part of the division is reg_offset = 2
reg_offset = (irq_id / 32);
// and the rest of the previous division is used to calculate the bit
// offset in the register, so for ID=89 this is bit_offset = 25
bit_offset = irq_id - 32 * reg_offset;
// get the current value of the corresponding GPC_IMRx register
gpc_imr = *((volatile uint32_t*)(HW_GPC_IMR1_ADDR + (reg_offset - 1) * 4));
if (doEnable) {
// clear the corresponding bit to unmask the interrupt source
gpc_imr &= ~(1 << bit_offset);
// write the new mask
*((volatile uint32_t*)(HW_GPC_IMR1_ADDR + (reg_offset - 1) * 4)) = (gpc_imr);
} else {
// set the corresponding bit to mask the interrupt source
gpc_imr |= (1 << bit_offset);
// write the new mask
*((volatile uint32_t*)(HW_GPC_IMR1_ADDR + (reg_offset - 1) * 4)) = (gpc_imr);
}
}
/*!
* Set/unset clock gating for a peripheral.
* @param ccm_ccgrx Address of the clock gating register: CCM_CCGR1,...
* @param cgx_offset Offset of the clock gating field: CG(x).
* @param gating_mode Clock gating mode: CLOCK_ON or CLOCK_OFF.
*/
void ccm_ccgr_config(uint32_t ccm_ccgrx, uint32_t cgx_offset, uint32_t gating_mode)
{
if (gating_mode == CLOCK_ON) {
*(volatile uint32_t*)(ccm_ccgrx) |= cgx_offset;
} else {
*(volatile uint32_t*)(ccm_ccgrx) &= ~cgx_offset;
}
}
void clock_gating_config(uint32_t base_address, uint32_t gating_mode)
{
uint32_t ccm_ccgrx = 0;
uint32_t cgx_offset = 0;
switch (base_address) {
case REGS_EPIT1_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(6);
break;
case REGS_EPIT2_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(7);
break;
default:
break;
}
// apply changes only if a valid address was found
if (ccm_ccgrx != 0) {
ccm_ccgr_config(ccm_ccgrx, cgx_offset, gating_mode);
}
}
////////////////////////////////////////////////////////////////////////////////
// End of file
////////////////////////////////////////////////////////////////////////////////