rt-thread-official/bsp/imx6ul/platform/cpu/ccm_pll2.c

342 lines
11 KiB
C
Raw Normal View History

2017-11-01 13:30:17 +08:00
/*
* 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.
*/
#include "sdk.h"
#include "registers/regsccm.h"
#include "registers/regsccmanalog.h"
#include "registers/regsuart.h"
#include "registers/regsepit.h"
#include "registers/regsspba.h"
#include "registers/regssdmaarm.h"
#include "registers/regsgpt.h"
#include "registers/regsi2c.h"
#include "registers/regsecspi.h"
#include "ccm_pll.h"
//#include "hardware.h"
//#include "soc_memory_map.h"
#define HW_ANADIG_REG_CORE (ANATOP_IPS_BASE_ADDR + 0x140)
#define HW_ANADIG_PLL_SYS_RW (ANATOP_IPS_BASE_ADDR + 0x000)
#define HW_ANADIG_REG_CORE_V_CORE_VALUE_mv(x) ((((x)-700)/25) << 0)
#define HW_ANADIG_REG_CORE_V_SOC_VALUE_mv(x) ((((x)-700)/25) << 18)
#define HW_ANADIG_REG_CORE_V_CORE_MSK 0x1F
#define HW_ANADIG_REG_CORE_V_SOC_MSK (0x1F << 18)
uint32_t g_arm_clk = 528000000;
const uint32_t PLL2_OUTPUT[] = { 528000000, 396000000, 352000000, 198000000 };
const uint32_t PLL3_OUTPUT[] = { 480000000, 720000000, 540000000, 508235294, 454736842 };
const uint32_t PLL4_OUTPUT = 650000000;
const uint32_t PLL5_OUTPUT = 650000000;
////////////////////////////////////////////////////////////////////////////////
// Code
////////////////////////////////////////////////////////////////////////////////
void set_soc_core_voltage(unsigned int v_core_mv, unsigned int v_soc_mv)
{
unsigned int val, val_v_core, val_v_soc;
val = reg32_read(HW_ANADIG_REG_CORE);
val &= ~HW_ANADIG_REG_CORE_V_CORE_MSK;
val &= ~HW_ANADIG_REG_CORE_V_SOC_MSK;
val_v_core = HW_ANADIG_REG_CORE_V_CORE_VALUE_mv(v_core_mv);
val_v_soc = HW_ANADIG_REG_CORE_V_SOC_VALUE_mv(v_soc_mv);
val |= val_v_core | val_v_soc;
reg32_write(HW_ANADIG_REG_CORE, val);
}
void setup_clk(void)
{
uint32_t div_select;
uint32_t temp;
uint32_t arm_clk = g_arm_clk/1000000;
switch(arm_clk)
{
case 400:
div_select = 33;
set_soc_core_voltage(1150, 1175);
return;
case 528:
div_select = 44;
set_soc_core_voltage(1250, 1250);
break;
case 756:
div_select = 63;
set_soc_core_voltage(1250, 1250);
printf("ARM Clock set to 756MHz\r\n");
break;
default:
return;
}
// first, make sure ARM_PODF is clear
HW_CCM_CACRR_WR(0);
// write the div_select value into HW_ANADIG_PLL_SYS_RW
// this will re-program the PLL to the new freq
temp = readl(HW_ANADIG_PLL_SYS_RW);
temp |= 0x10000;// set BYBASS
writel(temp, HW_ANADIG_PLL_SYS_RW);
temp = readl(HW_ANADIG_PLL_SYS_RW);
temp &= ~(0x0000007F);
temp |= div_select; // Update div
writel(temp, HW_ANADIG_PLL_SYS_RW);
/* Wait for PLL to lock */
while(!(readl(HW_ANADIG_PLL_SYS_RW) & 0x80000000));
/*disable BYPASS*/
temp = readl(HW_ANADIG_PLL_SYS_RW);
temp &= ~0x10000;
writel(temp, HW_ANADIG_PLL_SYS_RW);
}
void ccm_init(void)
{
HW_CCM_CCGR0_WR(0xffffffff);
HW_CCM_CCGR1_WR(0xffffffff); // EPIT, ESAI, GPT enabled by driver
HW_CCM_CCGR2_WR(0xffffffff); // I2C enabled by driver
HW_CCM_CCGR3_WR(0xffffffff);
HW_CCM_CCGR4_WR(0xffffffff); // GPMI, Perfmon enabled by driver
HW_CCM_CCGR5_WR(0xffffffff); // UART, SATA enabled by driver
HW_CCM_CCGR6_WR(0xffffffff);
/*
* Keep default settings at reset.
* pre_periph_clk_sel is by default at 0, so the selected output
* of PLL2 is the main output at 528MHz.
* => by default, ahb_podf divides by 4 => AHB_CLK@132MHz.
* => by default, ipg_podf divides by 2 => IPG_CLK@66MHz.
*/
HW_CCM_CBCDR.U = BF_CCM_CBCDR_AHB_PODF(3)
| BF_CCM_CBCDR_AXI_PODF(1)
| BF_CCM_CBCDR_IPG_PODF(1);
setup_clk();
/* Power up 480MHz PLL */
reg32_write_mask(HW_CCM_ANALOG_PLL_USB1_ADDR, 0x00001000, 0x00001000);
/* Enable 480MHz PLL */
reg32_write_mask(HW_CCM_ANALOG_PLL_USB1_ADDR, 0x00002000, 0x00002000);
reg32_write_mask(HW_CCM_CSCDR1_ADDR, 0x00000000, 0x0000003F);
}
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 = g_arm_clk;
break;
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;
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;
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:
case UART5_MODULE_CLK:
case UART6_MODULE_CLK:
case UART7_MODULE_CLK:
case UART8_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;
case SPI_CLK:
ret_val = PLL3_OUTPUT[0] / 8 / (HW_CCM_CSCDR2.B.ECSPI_CLK_PODF + 1);
break;
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;
default:
break;
}
return ret_val;
}
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_UART1_BASE:
ccm_ccgrx = HW_CCM_CCGR5_ADDR;
cgx_offset = CG(12);
break;
case REGS_UART2_BASE:
ccm_ccgrx = HW_CCM_CCGR0_ADDR;
cgx_offset = CG(14);
break;
case REGS_UART3_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(5);
break;
case REGS_UART4_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(12);
break;
case REGS_UART5_BASE:
ccm_ccgrx = HW_CCM_CCGR3_ADDR;
cgx_offset = CG(1);
break;
case REGS_UART6_BASE:
ccm_ccgrx = HW_CCM_CCGR3_ADDR;
cgx_offset = CG(3);
break;
case REGS_UART7_BASE:
ccm_ccgrx = HW_CCM_CCGR5_ADDR;
cgx_offset = CG(13);
break;
case REGS_UART8_BASE:
ccm_ccgrx = HW_CCM_CCGR6_ADDR;
cgx_offset = CG(7);
break;
case REGS_SPBA_BASE:
ccm_ccgrx = HW_CCM_CCGR5_ADDR;
cgx_offset = CG(6);
break;
case REGS_SDMAARM_BASE:
ccm_ccgrx = HW_CCM_CCGR5_ADDR;
cgx_offset = CG(3);
break;
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;
case REGS_GPT1_BASE:
case REGS_GPT2_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(10)|CG(11);
break;
case REGS_I2C1_BASE:
ccm_ccgrx = HW_CCM_CCGR2_ADDR;
cgx_offset = CG(3);
break;
case REGS_I2C2_BASE:
ccm_ccgrx = HW_CCM_CCGR2_ADDR;
cgx_offset = CG(4);
break;
case REGS_I2C3_BASE:
ccm_ccgrx = HW_CCM_CCGR2_ADDR;
cgx_offset = CG(5);
break;
case REGS_ECSPI1_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(0);
break;
case REGS_ECSPI2_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(1);
break;
case REGS_ECSPI3_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(2);
break;
case REGS_ECSPI4_BASE:
ccm_ccgrx = HW_CCM_CCGR1_ADDR;
cgx_offset = CG(3);
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
////////////////////////////////////////////////////////////////////////////////