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

1268 lines
36 KiB
C

/*
*********************************************************************************************************
* AR100 SYSTEM
* AR100 Software System Develop Kits
* clock control unit module
*
* (c) Copyright 2012-2016, Sunny China
* All Rights Reserved
*
* File : mclk.c
* By : Superm
* Version : v1.0
* Date : 2016-7-13
* Descript: module clock management
* Update : date auther ver notes
* 2016-7-13 8:43:10 Superm 1.0 Create this file.
*********************************************************************************************************
*/
#include "ccu_i.h"
#include "hal_prcm.h"
#include "errno.h"
#include "aw_io.h"
#include "aw_common.h"
#include "errno.h"
#include "compiler_attributes.h"
#include "notifier.h"
#include "delay.h"
static u32 ccu_log2(u32 m)
{
u32 n = 0;
while (m >= 2) {
m = m >> 1;
n++;
}
return n;
}
void ccu_iosc_freq_update(void)
{
if (ccu_get_mclk_src(CCU_MOD_CLK_APBS2) == CCU_SYS_CLK_IOSC) {
notifier_notify(&apbs2_notifier_head, CCU_CLK_CLKCHG_REQ, iosc_freq / ccu_get_mclk_div(CCU_MOD_CLK_APBS2));
notifier_notify(&apbs2_notifier_head, CCU_CLK_CLKCHG_DONE, iosc_freq / ccu_get_mclk_div(CCU_MOD_CLK_APBS2));
}
}
/*
*********************************************************************************************************
* SET SOURCE OF MODULE CLOCK
*
* Description: set the source of a specific module clock.
*
* Arguments : mclk : the module clock ID which we want to set source.
* sclk : the source clock ID whick we want to set as source.
*
* Returns : OK if set source succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_set_mclk_src(u32 mclk, u32 sclk)
{
switch (mclk) {
case CCU_MOD_CLK_CPUS: {
switch (sclk) {
case CCU_SYS_CLK_HOSC: {
ccu_reg_addr->cpus_clk_cfg.src_sel = 0;
return OK;
}
case CCU_SYS_CLK_LOSC: {
ccu_reg_addr->cpus_clk_cfg.src_sel = 1;
return OK;
}
case CCU_SYS_CLK_IOSC: {
ccu_reg_addr->cpus_clk_cfg.src_sel = 2;
return OK;
}
case CCU_SYS_CLK_AUDIO1_2X: {
ccu_reg_addr->cpus_clk_cfg.src_sel = 3;
return OK;
}
case CCU_SYS_CLK_AUDIO0_DIV2: {
ccu_reg_addr->cpus_clk_cfg.src_sel = 4;
return OK;
}
default: {
/* invalid source id for module clock */
return -EINVAL;
}
}
}
case CCU_MOD_CLK_APBS2: {
u32 src_freq;
u32 src_sel;
u32 factor_m;
switch (sclk) {
case CCU_SYS_CLK_HOSC: {
src_freq = CCU_HOSC_FREQ;
src_sel = 0;
factor_m = 0;
break;
}
case CCU_SYS_CLK_LOSC: {
src_freq = losc_freq;
src_sel = 1;
factor_m = 0;
break;
}
case CCU_SYS_CLK_IOSC: {
src_freq = iosc_freq;
src_sel = 2;
factor_m = 0;
break;
}
/* apbs max freq is 200MHz */
case CCU_SYS_CLK_PERI_2X: {
src_freq = CCU_PERIPH0_FREQ / 3;
src_sel = 3;
factor_m = 2;
break;
}
case CCU_SYS_CLK_AUDIO0_DIV2: {
src_freq = CCU_PERIPH0_FREQ / 3;
src_sel = 4;
factor_m = 2;
return -ENOTSUP;
}
default: {
return -EINVAL;
}
}
notifier_notify(&apbs2_notifier_head, CCU_CLK_CLKCHG_REQ,
src_freq / ccu_get_mclk_div(CCU_MOD_CLK_APBS2));
ccu_reg_addr->apbs2_cfg.factor_m = factor_m;
udelay(2);
ccu_reg_addr->apbs2_cfg.src_sel = src_sel;
notifier_notify(&apbs2_notifier_head, CCU_CLK_CLKCHG_DONE,
src_freq / ccu_get_mclk_div(CCU_MOD_CLK_APBS2));
return OK;
}
case CCU_MOD_CLK_C0: {
switch (sclk) {
case CCU_SYS_CLK_HOSC: {
writel(((readl(CCU_CPU_AXI_CFG_REG) & (~(0x3 << 24))) |
(0x0 << 24)),
CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_SYS_CLK_LOSC: {
writel(((readl(CCU_CPU_AXI_CFG_REG) & (~(0x3 << 24))) |
(0x1 << 24)),
CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_SYS_CLK_IOSC: {
writel(((readl(CCU_CPU_AXI_CFG_REG) & (~(0x3 << 24))) |
(0x2 << 24)),
CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_SYS_CLK_CPUX: {
writel(((readl(CCU_CPU_AXI_CFG_REG) & (~(0x3 << 24))) |
(0x3 << 24)),
CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PERI_1X: {
writel(((readl(CCU_CPU_AXI_CFG_REG) & (~(0x3 << 24))) |
(0x4 << 24)),
CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PERI_2X: {
writel(((readl(CCU_CPU_AXI_CFG_REG) & (~(0x3 << 24))) |
(0x5 << 24)),
CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PERI_800M: {
writel(((readl(CCU_CPU_AXI_CFG_REG) & (~(0x3 << 24))) |
(0x6 << 24)),
CCU_CPU_AXI_CFG_REG);
return OK;
}
default: {
return -EINVAL;
}
}
}
case CCU_MOD_CLK_PSI:
case CCU_MOD_CLK_AHB1:
case CCU_MOD_CLK_AHB2: {
switch (sclk) {
case CCU_SYS_CLK_HOSC: {
writel(((readl(CCU_PSI_AHB1_AHB2_CFG_REG) &
(~(0x3 << 24))) |
(0x0 << 24)),
CCU_PSI_AHB1_AHB2_CFG_REG);
return OK;
}
case CCU_SYS_CLK_LOSC: {
writel(((readl(CCU_PSI_AHB1_AHB2_CFG_REG) &
(~(0x3 << 24))) |
(0x1 << 24)),
CCU_PSI_AHB1_AHB2_CFG_REG);
return OK;
}
case CCU_SYS_CLK_IOSC: {
writel(((readl(CCU_PSI_AHB1_AHB2_CFG_REG) &
(~(0x3 << 24))) |
(0x2 << 24)),
CCU_PSI_AHB1_AHB2_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PERI_1X: {
writel(((readl(CCU_PSI_AHB1_AHB2_CFG_REG) &
(~(0x3 << 24))) |
(0x3 << 24)),
CCU_PSI_AHB1_AHB2_CFG_REG);
return OK;
}
default: {
return -EINVAL;
}
}
}
case CCU_MOD_CLK_APB1: {
switch (sclk) {
case CCU_SYS_CLK_HOSC: {
writel(((readl(CCU_APB1_CFG_REG) & (~(0x3 << 24))) |
(0x0 << 24)),
CCU_APB1_CFG_REG);
return OK;
}
case CCU_SYS_CLK_LOSC: {
writel(((readl(CCU_APB1_CFG_REG) & (~(0x3 << 24))) |
(0x1 << 24)),
CCU_APB1_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PSI: {
writel(((readl(CCU_APB1_CFG_REG) & (~(0x3 << 24))) |
(0x2 << 24)),
CCU_APB1_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PERI_1X: {
writel(((readl(CCU_APB1_CFG_REG) & (~(0x3 << 24))) |
(0x3 << 24)),
CCU_APB1_CFG_REG);
return OK;
}
default: {
return -EINVAL;
}
}
}
case CCU_MOD_CLK_APB2: {
switch (sclk) {
case CCU_SYS_CLK_HOSC: {
writel(((readl(CCU_APB2_CFG_REG) & (~(0x3 << 24))) |
(0x0 << 24)),
CCU_APB2_CFG_REG);
return OK;
}
case CCU_SYS_CLK_LOSC: {
writel(((readl(CCU_APB2_CFG_REG) & (~(0x3 << 24))) |
(0x1 << 24)),
CCU_APB2_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PSI: {
writel(((readl(CCU_APB2_CFG_REG) & (~(0x3 << 24))) |
(0x2 << 24)),
CCU_APB2_CFG_REG);
return OK;
}
case CCU_SYS_CLK_PERI_1X: {
writel(((readl(CCU_APB2_CFG_REG) & (~(0x3 << 24))) |
(0x3 << 24)),
CCU_APB2_CFG_REG);
return OK;
}
default: {
return -EINVAL;
}
}
}
case CCU_MOD_CLK_R_CIR_SP: {
switch (sclk) {
case CCU_SYS_CLK_LOSC: {
ccu_reg_addr->r_ir_clk.src_sel = 0;
return OK;
}
case CCU_SYS_CLK_HOSC: {
ccu_reg_addr->r_ir_clk.src_sel = 1;
return OK;
}
default: {
return -EINVAL;
}
}
}
case CCU_MOD_CLK_R_AC_ADC: {
switch (sclk) {
case CCU_SYS_CLK_AUDIO0_DIV5:
ccu_reg_addr->r_ac_adc.src_sel = 0;
return OK;
case CCU_SYS_CLK_AUDIO0_DIV2:
ccu_reg_addr->r_ac_adc.src_sel = 1;
return OK;
case CCU_SYS_CLK_AUDIO1_1X:
ccu_reg_addr->r_ac_adc.src_sel = 2;
return OK;
case CCU_SYS_CLK_AUDIO1_4X:
ccu_reg_addr->r_ac_adc.src_sel = 3;
return OK;
default:
/* invalid source id for module clock */
return -EINVAL;
}
}
case CCU_MOD_CLK_R_AC_DAC: {
switch (sclk) {
case CCU_SYS_CLK_AUDIO0_DIV5:
ccu_reg_addr->r_ac_dac.src_sel = 0;
return OK;
case CCU_SYS_CLK_AUDIO0_DIV2:
ccu_reg_addr->r_ac_dac.src_sel = 1;
return OK;
case CCU_SYS_CLK_AUDIO1_1X:
ccu_reg_addr->r_ac_dac.src_sel = 2;
return OK;
case CCU_SYS_CLK_AUDIO1_4X:
ccu_reg_addr->r_ac_dac.src_sel = 3;
return OK;
default:
/* invalid source id for module clock */
return -EINVAL;
}
}
case CCU_MOD_CLK_R_DMIC: {
switch (sclk) {
case CCU_SYS_CLK_AUDIO0_DIV5:
ccu_reg_addr->r_dmic.src_sel = 0;
return OK;
case CCU_SYS_CLK_AUDIO0_DIV2:
ccu_reg_addr->r_dmic.src_sel = 1;
return OK;
case CCU_SYS_CLK_AUDIO1_1X:
ccu_reg_addr->r_dmic.src_sel = 2;
return OK;
case CCU_SYS_CLK_AUDIO1_4X:
ccu_reg_addr->r_dmic.src_sel = 3;
return OK;
default:
/* invalid source id for module clock */
return -EINVAL;
}
}
case CCU_MOD_CLK_R_I2S0: {
switch (sclk) {
case CCU_SYS_CLK_AUDIO0_DIV5:
ccu_reg_addr->r_i2s0.src_sel = 0;
return OK;
case CCU_SYS_CLK_AUDIO0_DIV2:
ccu_reg_addr->r_i2s0.src_sel = 1;
return OK;
case CCU_SYS_CLK_AUDIO1_1X:
ccu_reg_addr->r_i2s0.src_sel = 2;
return OK;
case CCU_SYS_CLK_AUDIO1_4X:
ccu_reg_addr->r_i2s0.src_sel = 3;
return OK;
default:
/* invalid source id for module clock */
return -EINVAL;
}
}
case CCU_MOD_CLK_R_I2S0_ASRC: {
switch (sclk) {
case CCU_SYS_CLK_AUDIO0_DIV5:
ccu_reg_addr->r_i2s0_asrc.src_sel = 0;
return OK;
case CCU_SYS_CLK_AUDIO0_DIV2:
ccu_reg_addr->r_i2s0_asrc.src_sel = 1;
return OK;
case CCU_SYS_CLK_AUDIO1_1X:
ccu_reg_addr->r_i2s0_asrc.src_sel = 2;
return OK;
case CCU_SYS_CLK_AUDIO1_4X:
ccu_reg_addr->r_i2s0_asrc.src_sel = 3;
return OK;
default:
/* invalid source id for module clock */
return -EINVAL;
}
}
case CCU_MOD_CLK_R_I2S1: {
switch (sclk) {
case CCU_SYS_CLK_AUDIO0_DIV5:
ccu_reg_addr->r_i2s1.src_sel = 0;
return OK;
case CCU_SYS_CLK_AUDIO0_DIV2:
ccu_reg_addr->r_i2s1.src_sel = 1;
return OK;
case CCU_SYS_CLK_AUDIO1_1X:
ccu_reg_addr->r_i2s1.src_sel = 2;
return OK;
case CCU_SYS_CLK_AUDIO1_4X:
ccu_reg_addr->r_i2s1.src_sel = 3;
return OK;
default:
/* invalid source id for module clock */
return -EINVAL;
}
}
case CCU_MOD_CLK_R_LPSD: {
switch (sclk) {
case CCU_SYS_CLK_AUDIO0_DIV5:
ccu_reg_addr->r_lpsd.src_sel = 0;
return OK;
case CCU_SYS_CLK_AUDIO0_DIV2:
ccu_reg_addr->r_lpsd.src_sel = 1;
return OK;
case CCU_SYS_CLK_AUDIO1_1X:
ccu_reg_addr->r_lpsd.src_sel = 2;
return OK;
case CCU_SYS_CLK_AUDIO1_4X:
ccu_reg_addr->r_lpsd.src_sel = 3;
return OK;
default:
/* invalid source id for module clock */
return -EINVAL;
}
}
default: {
pr_warning("invaid module clock id (%d) when set source\n", mclk);
return -EINVAL;
}
}
/* un-reached */
}
/*
*********************************************************************************************************
* GET SOURCE OF MODULE CLOCK
*
* Description: get the source of a specific module clock.
*
* Arguments : mclk : the module clock ID which we want to get source.
*
* Returns : the source clock ID of source clock.
*********************************************************************************************************
*/
s32 ccu_get_mclk_src(u32 mclk)
{
switch (mclk) {
case CCU_MOD_CLK_CPUS:
{
switch (ccu_reg_addr->cpus_clk_cfg.src_sel) {
case 0:
{
return CCU_SYS_CLK_HOSC;
}
case 1:
{
return CCU_SYS_CLK_LOSC;
}
case 2:
{
return CCU_SYS_CLK_IOSC;
}
case 3:
{
return CCU_SYS_CLK_PERI_2X;
}
case 4:
{
return CCU_SYS_CLK_AUDIO0_DIV2;
}
default:
{
/* invalid source id for module clock */
return CCU_SYS_CLK_NONE;
}
}
}
case CCU_MOD_CLK_C0:
{
switch ((readl(CCU_CPU_AXI_CFG_REG) >> 24) & 0x3) {
case 0:
{
return CCU_SYS_CLK_HOSC;
}
case 1:
{
return CCU_SYS_CLK_LOSC;
}
case 2:
{
return CCU_SYS_CLK_IOSC;
}
case 3:
{
return CCU_SYS_CLK_CPUX;
}
case 4:
{
return CCU_SYS_CLK_PERI_1X;
}
case 5:
{
return CCU_SYS_CLK_PERI_2X;
}
case 6:
{
return CCU_SYS_CLK_PERI_800M;
}
default:
{
return -EINVAL;
}
}
}
case CCU_MOD_CLK_PSI:
case CCU_MOD_CLK_AHB1:
case CCU_MOD_CLK_AHB2:
{
switch ((readl(CCU_PSI_AHB1_AHB2_CFG_REG) >> 24) & 0x3) {
case 0:
{
return CCU_SYS_CLK_HOSC;
}
case 1:
{
return CCU_SYS_CLK_LOSC;
}
case 2:
{
return CCU_SYS_CLK_IOSC;
}
case 3:
{
return CCU_SYS_CLK_PERI_1X;
}
default:
{
return -EINVAL;
}
}
}
case CCU_MOD_CLK_APB1:
{
switch ((readl(CCU_APB1_CFG_REG) >> 24) & 0x3) {
case 0:
{
return CCU_SYS_CLK_HOSC;
}
case 1:
{
return CCU_SYS_CLK_LOSC;
}
case 2:
{
return CCU_SYS_CLK_PSI;
}
case 3:
{
return CCU_SYS_CLK_PERI_1X;
}
default:
{
return -EINVAL;
}
}
}
case CCU_MOD_CLK_APB2:
{
switch ((readl(CCU_APB2_CFG_REG) >> 24) & 0x3) {
case 0:
{
return CCU_SYS_CLK_HOSC;
}
case 1:
{
return CCU_SYS_CLK_LOSC;
}
case 2:
{
return CCU_SYS_CLK_PSI;
}
case 3:
{
return CCU_SYS_CLK_PERI_1X;
}
default:
{
return -EINVAL;
}
}
}
case CCU_MOD_CLK_AHBS:
{
return CCU_SYS_CLK_CPUS;
}
case CCU_MOD_CLK_APBS1:
{
switch (ccu_reg_addr->apbs1_cfg.src_sel) {
case 0:
{
return CCU_SYS_CLK_HOSC;
}
case 1:
{
return CCU_SYS_CLK_LOSC;
}
case 2:
{
return CCU_SYS_CLK_IOSC;
}
case 3:
{
return CCU_SYS_CLK_PERI_2X;
}
case 4:
{
return CCU_SYS_CLK_AUDIO0_DIV2;
}
default:
{
return CCU_SYS_CLK_NONE;
}
}
return CCU_MOD_CLK_AHBS;
}
case CCU_MOD_CLK_APBS2:
{
switch (ccu_reg_addr->apbs2_cfg.src_sel) {
case 0:
{
return CCU_SYS_CLK_HOSC;
}
case 1:
{
return CCU_SYS_CLK_LOSC;
}
case 2:
{
return CCU_SYS_CLK_IOSC;
}
case 3:
{
return CCU_SYS_CLK_PERI_2X;
}
case 4:
{
return CCU_SYS_CLK_AUDIO0_DIV2;
}
default:
{
return CCU_SYS_CLK_NONE;
}
}
}
case CCU_MOD_CLK_R_ONEWIRE:
case CCU_MOD_CLK_R_TIMER0_1:
case CCU_MOD_CLK_R_CIR:
case CCU_MOD_CLK_R_PIO:
{
return CCU_SYS_CLK_APB1;
}
case CCU_MOD_CLK_R_RSB:
case CCU_MOD_CLK_R_TWI:
case CCU_MOD_CLK_R_UART:
{
return CCU_SYS_CLK_APB2;
}
case CCU_MOD_CLK_R_CIR_SP:
{
switch (ccu_reg_addr->r_ir_clk.src_sel) {
case 0:
{
return CCU_SYS_CLK_LOSC;
}
case 1:
{
return CCU_SYS_CLK_HOSC;
}
default:
{
return CCU_SYS_CLK_NONE;
}
}
}
default:
{
pr_warning("invaid module clock id (%d) when set source\n",
mclk);
return CCU_SYS_CLK_NONE;
}
}
/* un-reached */
}
s32 calc_clk_audio_ratio_nm(u32 div, u32 *n, u32 *m)
{
if (div < 33) {
*n = 0;
} else if (div < 65) {
*n = 1;
} else if (div < 129) {
*n = 2;
} else if (div < 257) {
*n = 3;
} else {
*n = 0;
*m = 0;
printf("audio_ratio: %x\n", div);
return -EINVAL;
}
*m = div / (1 << (*n)) - 1;
return OK;
}
s32 calc_clk_ratio_nm(u32 div, u32 *n, u32 *m)
{
if (div < 17) {
*n = 0;
*m = div;
} else if (div < 33) {
*n = 1;
*m = (div + 1) / 2;
} else if (div < 65) {
*n = 2;
*m = (div + 3) / 4;
} else if (div < 129) {
*n = 3;
*m = (div + 7) / 8;
} else {
pr_err("icd %x\n", div);
return -EINVAL;
}
return OK;
}
/*
*********************************************************************************************************
* SET DIVIDER OF MODULE CLOCK
*
* Description: set the divider of a specific module clock.
*
* Arguments : mclk : the module clock ID which we want to set divider.
* div : the divider whick we want to set as source.
*
* Returns : OK if set divider succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_set_mclk_div(u32 mclk, u32 div)
{
switch (mclk) {
case CCU_MOD_CLK_CPUS:
{
ccu_reg_addr->cpus_clk_cfg.factor_n =
ccu_log2(div);
return OK;
}
case CCU_MOD_CLK_APBS1:
{
ccu_reg_addr->apbs1_cfg.factor_m = div - 1;
return OK;
}
case CCU_MOD_CLK_APBS2:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->apbs2_cfg.factor_n = ccu_log2(factor_n);
ccu_reg_addr->apbs2_cfg.factor_m = factor_m - 1;
return OK;
}
case CCU_MOD_CLK_R_CIR_SP:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_ir_clk.factor_n = ccu_log2(factor_n);
ccu_reg_addr->r_ir_clk.factor_m = factor_m - 1;
return OK;
}
case CCU_MOD_CLK_AXI:
{
volatile u32 value;
value = readl(CCU_CPU_AXI_CFG_REG);
value &= (~(0x3));
value |= ((div - 1) & 0x3);
writel(value, CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_MOD_CLK_CPU_APB:
{
volatile u32 value;
value = readl(CCU_CPU_AXI_CFG_REG);
value &= (~(0x3 << 8));
value |= ((div - 1) & 0x3) << 8;
writel(value, CCU_CPU_AXI_CFG_REG);
return OK;
}
case CCU_MOD_CLK_PSI:
case CCU_MOD_CLK_AHB1:
case CCU_MOD_CLK_AHB2:
{
volatile u32 value;
u32 factor_n;
u32 factor_m;
if (calc_clk_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
value = readl(CCU_PSI_AHB1_AHB2_CFG_REG);
value &= (~(0x3ff << 0));
value |= (ccu_log2(factor_n) << 8) | (factor_m - 1);
writel(value, CCU_PSI_AHB1_AHB2_CFG_REG);
return OK;
}
case CCU_MOD_CLK_APB1:
{
volatile u32 value;
u32 factor_n;
u32 factor_m;
if (calc_clk_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
value = readl(CCU_APB1_CFG_REG);
value &= (~(0x3ff << 0));
value |= (ccu_log2(factor_n) << 8) | (factor_m - 1);
writel(value, CCU_APB1_CFG_REG);
return OK;
}
case CCU_MOD_CLK_APB2:
{
volatile u32 value;
u32 factor_n;
u32 factor_m;
if (calc_clk_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
value = readl(CCU_APB2_CFG_REG);
value &= (~(0x3ff << 0));
value |= (ccu_log2(factor_n) << 8) | (factor_m - 1);
writel(value, CCU_APB2_CFG_REG);
return OK;
}
case CCU_MOD_CLK_R_LPSD:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_audio_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_lpsd.factor_n = factor_n;
ccu_reg_addr->r_lpsd.factor_m0 = factor_m;
return OK;
}
case CCU_MOD_CLK_R_AC_ADC:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_audio_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_ac_adc.factor_n = factor_n;
ccu_reg_addr->r_ac_adc.factor_m = factor_m;
return OK;
}
case CCU_MOD_CLK_R_AC_DAC:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_audio_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_ac_dac.factor_n = factor_n;
ccu_reg_addr->r_ac_dac.factor_m = factor_m;
return OK;
}
case CCU_MOD_CLK_R_I2S0:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_audio_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_i2s0.factor_n = factor_n;
ccu_reg_addr->r_i2s0.factor_m = factor_m;
return OK;
}
case CCU_MOD_CLK_R_I2S0_ASRC:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_audio_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_i2s0_asrc.factor_n = factor_n;
ccu_reg_addr->r_i2s0_asrc.factor_m = factor_m;
return OK;
}
case CCU_MOD_CLK_R_I2S1:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_audio_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_i2s1.factor_n = factor_n;
ccu_reg_addr->r_i2s1.factor_m = factor_m;
return OK;
}
case CCU_MOD_CLK_R_DMIC:
{
u32 factor_n;
u32 factor_m;
if (calc_clk_audio_ratio_nm(div, &factor_n, &factor_m) != OK) {
return -EFAULT;
}
ccu_reg_addr->r_dmic.factor_n = factor_n;
ccu_reg_addr->r_dmic.factor_m = factor_m;
return OK;
}
default:
{
pr_warning("invaid module clock id (%d) when set divider\n",
mclk);
return -EINVAL;
}
}
/* un-reached */
}
/*
*********************************************************************************************************
* GET DIVIDER OF MODULE CLOCK
*
* Description: get the divider of a specific module clock.
*
* Arguments : mclk : the module clock ID which we want to get divider.
*
* Returns : the divider of the specific module clock.
*********************************************************************************************************
*/
s32 ccu_get_mclk_div(u32 mclk)
{
switch (mclk) {
case CCU_MOD_CLK_CPUS: {
return 1 << (ccu_reg_addr->cpus_clk_cfg.factor_n);
}
case CCU_MOD_CLK_AHBS: {
return 1;
}
case CCU_MOD_CLK_APBS1: {
return (ccu_reg_addr->apbs1_cfg.factor_m + 1);
}
case CCU_MOD_CLK_APBS2: {
return 1 << (ccu_reg_addr->apbs2_cfg.factor_n);
}
case CCU_MOD_CLK_R_RTC:
case CCU_MOD_CLK_R_CPUSCFG:
case CCU_MOD_CLK_R_PRCM:
case CCU_MOD_CLK_R_TIMER0_1:
case CCU_MOD_CLK_R_WDG:
case CCU_MOD_CLK_R_TWD:
case CCU_MOD_CLK_R_PWM:
case CCU_MOD_CLK_R_INTC:
case CCU_MOD_CLK_R_PIO:
case CCU_MOD_CLK_R_CIR:
case CCU_MOD_CLK_R_ONEWIRE:
case CCU_MOD_CLK_R_TWI:
case CCU_MOD_CLK_R_UART:
case CCU_MOD_CLK_R_RSB: {
return 1;
}
case CCU_MOD_CLK_R_AC_ADC: {
return (ccu_reg_addr->r_ac_adc.factor_m + 1) *
ccu_reg_addr->r_ac_adc.factor_n;
}
case CCU_MOD_CLK_R_AC_DAC: {
return (ccu_reg_addr->r_ac_dac.factor_m + 1) *
ccu_reg_addr->r_ac_dac.factor_n;
}
case CCU_MOD_CLK_R_DMIC: {
return (ccu_reg_addr->r_dmic.factor_m + 1) *
ccu_reg_addr->r_dmic.factor_n;
}
case CCU_MOD_CLK_R_I2S0: {
return (ccu_reg_addr->r_i2s0.factor_m + 1) *
ccu_reg_addr->r_i2s0.factor_n;
}
case CCU_MOD_CLK_R_I2S0_ASRC: {
return (ccu_reg_addr->r_i2s0_asrc.factor_m + 1) *
ccu_reg_addr->r_i2s0_asrc.factor_n;
}
case CCU_MOD_CLK_R_I2S1: {
return (ccu_reg_addr->r_i2s1.factor_m + 1) *
ccu_reg_addr->r_i2s1.factor_n;
}
case CCU_MOD_CLK_R_CIR_SP: {
return (1 << (ccu_reg_addr->r_ir_clk.factor_n)) *
(ccu_reg_addr->r_ir_clk.factor_m + 1);
}
case CCU_MOD_CLK_AXI: {
volatile u32 value;
value = readl(CCU_CPU_AXI_CFG_REG);
return ((value & 0x3) + 1);
}
case CCU_MOD_CLK_CPU_APB: {
volatile u32 value;
value = readl(CCU_CPU_AXI_CFG_REG);
return (((value >> 8) & 0x3) + 1);
}
default: {
pr_warning("invaid module clock id (%d) when get divider\n",
mclk);
return -EINVAL;
}
}
/* un-reached */
}
/*
*********************************************************************************************************
* SET ON-OFF STATUS OF MODULE CLOCK
*
* Description: set the on-off status of a specific module clock.
*
* Arguments : mclk : the module clock ID which we want to set on-off status.
* onoff : the on-off status which we want to set, the detail please
* refer to the clock status of on-off.
*
* Returns : OK if set module clock on-off status succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_set_mclk_onoff(u32 mclk, s32 onoff)
{
switch (mclk) {
case CCU_MOD_CLK_MSGBOX0:
writel(((readl(CCU_MSGBOX_BGR_REG) & (~(0x1 << 0))) |
(onoff << 0)),
CCU_MSGBOX_BGR_REG);
break;
case CCU_MOD_CLK_MSGBOX1:
writel(((readl(CCU_MSGBOX_BGR_REG) & (~(0x1 << 1))) |
(onoff << 1)),
CCU_MSGBOX_BGR_REG);
break;
case CCU_MOD_CLK_MSGBOXR:
writel(((readl(CCU_R_MSGBOX_BGR_REG) & (~(0x1 << 0))) |
(onoff << 0)),
CCU_R_MSGBOX_BGR_REG);
break;
case CCU_MOD_CLK_R_DMA:
/* bus gating */
writel(((readl(CCU_R_DMA_BGR_REG) & (~(0x1 << 0))) |
(onoff << 0)),
CCU_R_DMA_BGR_REG);
/* mclk gating */
writel(((readl(CCU_MBUS_MASTER_CLK_REG) & (~(0x1 << 3))) |
(onoff << 3)),
CCU_MBUS_MASTER_CLK_REG);
return OK;
case CCU_MOD_CLK_R_TWI: {
ccu_reg_addr->r_twi.gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_UART: {
ccu_reg_addr->r_uart.gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_TIMER0_1: {
ccu_reg_addr->r_timer.gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_TWD: {
ccu_reg_addr->r_twd.gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_PWM: {
ccu_reg_addr->r_pwm.gate = onoff;
return OK;
}
/* case CCU_MOD_CLK_R_ONEWIRE: { */
/* ccu_reg_addr->r_owc.gate = onoff; */
/* return OK; */
/* } */
case CCU_MOD_CLK_R_RTC: {
ccu_reg_addr->r_rtc.gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_RSB: {
ccu_reg_addr->r_rsb.gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_CIR: {
ccu_reg_addr->r_ir.gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_CIR_SP: {
ccu_reg_addr->r_ir_clk.sclk_gate = onoff;
return OK;
}
case CCU_MOD_CLK_SPINLOCK: {
writel(((readl(CCU_SPINLOCK_BGR_REG) & (~(0x1 << 0))) |
(onoff << 0)),
CCU_SPINLOCK_BGR_REG);
return OK;
}
case CCU_MOD_CLK_MSGBOX: {
writel(((readl(CCU_MSGBOX_BGR_REG) & (~(0x1 << 0))) |
(onoff << 0)),
CCU_MSGBOX_BGR_REG);
return OK;
}
case CCU_MOD_CLK_R_AC_ADC: {
ccu_reg_addr->r_ac_adc.sclk_gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_AC_DAC: {
ccu_reg_addr->r_ac_dac.sclk_gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_DMIC: {
ccu_reg_addr->r_dmic.sclk_gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_I2S0: {
ccu_reg_addr->r_i2s0.sclk_gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_I2S0_ASRC: {
ccu_reg_addr->r_i2s0_asrc.sclk_gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_I2S1: {
ccu_reg_addr->r_i2s1.sclk_gate = onoff;
return OK;
}
case CCU_MOD_CLK_R_LPSD: {
ccu_reg_addr->r_lpsd.sclk_gate = onoff;
return OK;
}
default: {
pr_warning("invaid module clock id (%d) when set divider\n",
mclk);
return -EINVAL;
}
}
/* un-reached */
return OK;
}
/*
*********************************************************************************************************
* REGISTER MODULE CB
*
* Description: register call-back for module clock, when the source frequency
* of the module clock changed, it will use this call-back to notify
* module driver.
*
* Arguments : mclk : the module clock ID which we want to register call-back.
* pcb : the call-back which we want to register.
*
* Returns : OK if register call-back succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_reg_mclk_cb(__maybe_unused u32 mclk, __pNotifier_t pcb)
{
/*
* we just support apb module clock notifier now.
* by sunny at 2012-11-23 14:36:14.
*/
/* insert call-back to apb_notifier_head. */
return notifier_insert(&apbs2_notifier_head, pcb);
}
/*
*********************************************************************************************************
* UNREGISTER MODULE CB
*
* Description: unregister call-back for module clock.
*
* Arguments : mclk : the module clock ID which we want to unregister call-back.
* pcb : the call-back which we want to unregister.
*
* Returns : OK if unregister call-back succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_unreg_mclk_cb(__maybe_unused u32 mclk, __pNotifier_t pcb)
{
/* delete call-back to apb_notifier_head. */
return notifier_delete(&apbs2_notifier_head, pcb);
}
s32 ccu_set_cpus_src(u32 sclk)
{
u32 src_freq;
u32 src_sel;
switch (sclk) {
case CCU_SYS_CLK_HOSC:
{
src_freq = losc_freq;
src_sel = 0;
break;
}
case CCU_SYS_CLK_LOSC:
{
src_freq = CCU_HOSC_FREQ;
src_sel = 1;
break;
}
case CCU_SYS_CLK_IOSC:
{
src_freq = iosc_freq;
src_sel = 2;
break;
}
case CCU_SYS_CLK_PLL3:
{
src_freq = CCU_CPUS_POST_DIV;
src_sel = 3;
break;
}
default:
{
return -EINVAL;
}
}
/* set cpus clock source */
ccu_reg_addr->cpus_clk_cfg.src_sel = src_sel;
return OK;
}