rt-thread/bsp/allwinner/libraries/sunxi-hal/hal/source/prcm/prcm-sun50iw11/ccu.c

232 lines
6.3 KiB
C

/*
*********************************************************************************************************
* AR100 SYSTEM
* AR100 Software System Develop Kits
* clock control unit module
*
* (c) Copyright 2012-2016, Sunny China
* All Rights Reserved
*
* File : ccu.c
* By : Sunny
* Version : v1.0
* Date : 2012-5-7
* Descript: clock control unit module.
* Update : date auther ver notes
* 2012-5-7 8:43:10 Sunny 1.0 Create this file.
*********************************************************************************************************
*/
#include "ccu_i.h"
#include "platform.h"
#include "cpucfg_regs.h"
#include "hal_prcm.h"
#include "compiler_attributes.h"
#include "aw_io.h"
#define DO_NOT_CALIBRATION
/* ccu module registers base address */
struct ccu_reg_list *ccu_reg_addr;
struct ccu_pll_c0_cpux_reg0000 *ccu_pll_c0_cpux_reg_addr;
struct ccu_pll_ddr0_reg0010 *ccu_pll_ddr0_reg_addr;
struct ccu_pll_periph_reg0010 *ccu_pll_periph0_reg_addr;
struct ccu_pll_audio0_reg0020 *ccu_pll_audio0_reg_addr;
/* struct ccu_pll_periph1_reg0028 *ccu_pll_periph1_reg_addr; */
/* apb clock change notifier list */
struct notifier *apbs2_notifier_head;
u32 iosc_freq = 16000000;
u32 losc_freq = 32768;
#ifndef DO_NOT_CALIBRATION
static u32 filter_channel[10] = {0};
static u32 filter_count;
#endif
void dcxo_cali_start(u32 __maybe_unused *bk)
{
#ifndef DO_NOT_CALIBRATION
u32 calibration_status, xo_ctrl;
calibration_status = readl(IOSC_CLK_AUTO_CALI);
xo_ctrl = readl(XO_CTRL);
writel(readl(XO_CTRL) | (0xa), XO_CTRL);
writel(0x7, IOSC_CLK_AUTO_CALI);
bk[0] = calibration_status;
bk[1] = xo_ctrl;
#endif
}
void dcxo_cali_end(__maybe_unused u32 *bk)
{
#ifndef DO_NOT_CALIBRATION
u32 calibration_status, xo_ctrl;
calibration_status = bk[0];
xo_ctrl = bk[1];
writel(xo_ctrl, XO_CTRL);
writel(calibration_status, IOSC_CLK_AUTO_CALI);
#endif
}
void osc_freq_init(void)
{
#ifndef DO_NOT_CALIBRATION
u32 count = 0;
u32 value, sum = 0;
u32 integer, decimal;
u32 dcxo_status_bk[2] = {0};
filter_count = 0;
dcxo_cali_start(dcxo_status_bk);
time_mdelay(50);
while (1) {
count++;
if (count > 20)
break;
value = readl(IOSC_CLK_AUTO_CALI);
time_mdelay(16);
integer = (value >> DCXO_CALI_INTEGER_OFFSET);
decimal = (value >> DCXO_CALI_DECIMAL_OFFSET) & 0xffff;
value = integer * losc_freq + (losc_freq * 65535 / decimal);
if (value > 24000000 || value < 8000000)
continue;
sum = value + sum;
filter_channel[filter_count] = value;
filter_count++;
if (filter_count == 5)
break;
}
if (filter_count == 0)
iosc_freq = 16000000;
else
iosc_freq = sum / filter_count;
dcxo_cali_end(dcxo_status_bk);
#endif
}
static u32 __maybe_unused filter_sliding(u32 *channel, u32 value, u32 max_count)
{
u32 *bk, *ch = channel + max_count - 1;
u32 sum = 0;
do {
bk = ch;
ch--;
*bk = *ch;
sum += *bk;
} while (ch >= (channel + 1));
*ch = value;
sum = sum + *ch;
return sum / max_count;
}
void osc_freq_filter(void)
{
#ifndef DO_NOT_CALIBRATION
u32 integer, decimal;
u32 value, sum;
time_mdelay(16);
value = readl(IOSC_CLK_AUTO_CALI);
integer = (value >> DCXO_CALI_INTEGER_OFFSET);
decimal = (value >> DCXO_CALI_DECIMAL_OFFSET) & 0xffff;
value = integer * losc_freq + (losc_freq * 65535 / decimal);
if (value > 24000000 || value < 8000000)
return ;
if (filter_count < 10) {
sum = iosc_freq * filter_count;
filter_channel[filter_count] = value;
sum = sum + value;
filter_count++;
iosc_freq = sum / filter_count;
} else {
iosc_freq = filter_sliding(&filter_channel[0], value, filter_count);
}
#endif
}
/*
*********************************************************************************************************
* INITIALIZE CCU
*
* Description: initialize clock control unit.
*
* Arguments : none.
*
* Returns : OK if initialize ccu succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_init(void)
{
/* initialize ccu register address */
ccu_reg_addr = (struct ccu_reg_list *)R_PRCM_REG_BASE;
ccu_pll_c0_cpux_reg_addr = (struct ccu_pll_c0_cpux_reg0000 *)CCU_PLL_C0_REG;
ccu_pll_ddr0_reg_addr = (struct ccu_pll_ddr0_reg0010 *)CCU_PLL_DDR0_REG;
ccu_pll_periph0_reg_addr = (struct ccu_pll_periph_reg0010 *)CCU_PLL_PERIPH0_REG;
ccu_pll_audio0_reg_addr = (struct ccu_pll_audio0_reg0020 *)CCU_PLL_AUDIO0_REG;
/* ccu_pll_periph1_reg_addr = (struct ccu_pll_periph1_reg0028 *)CCU_PLL_PERIPH1_REG; */
#ifndef CFG_FPGA_PLATFORM
/* setup cpus post div source to 200M(CCU_CPUS_POST_DIV) */
/* FIXME: board in fix */
/* u32 value;
value = (ccu_get_sclk_freq(CCU_SYS_CLK_PLL3)) / CCU_CPUS_POST_DIV;
if (value < 1) {
[>to avoid PLL5 freq less than CCU_CPUS_POST_DIV<]
value = 1;
}
ccu_reg_addr->cpus_clk_cfg.factor_m = value - 1;
[>set ar100 clock source to PLL5<]
ccu_set_mclk_src(CCU_MOD_CLK_CPUS, CCU_SYS_CLK_PLL3); */
#endif
/* initialize apb notifier list */
apbs2_notifier_head = NULL;
/* ccu initialize succeeded */
return OK;
}
/*
*********************************************************************************************************
* EXIT CCU
*
* Description: exit clock control unit.
*
* Arguments : none.
*
* Returns : OK if exit ccu succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_exit(void)
{
ccu_pll_c0_cpux_reg_addr = NULL;
ccu_reg_addr = NULL;
ccu_pll_periph0_reg_addr = NULL;
ccu_pll_audio0_reg_addr = NULL;
return OK;
}
void write_rtc_domain_reg(u32 reg, u32 value)
{
writel((unsigned long)value, (unsigned long)reg);
}
u32 read_rtc_domain_reg(u32 reg)
{
return readl((unsigned long)reg);
}