diff --git a/arch/arm/boot/dts/meson8b_odroidc.dts b/arch/arm/boot/dts/meson8b_odroidc.dts index 0230b34877ec..f17c3341700a 100644 --- a/arch/arm/boot/dts/meson8b_odroidc.dts +++ b/arch/arm/boot/dts/meson8b_odroidc.dts @@ -334,7 +334,7 @@ vcck_dvfs { dvfs_id = <1>; /** must be value of (1 << n) */ - table_count = <12>; /** must be correct count for dvfs_table */ + table_count = <13>; /** must be correct count for dvfs_table */ change-frequent-only; dvfs_table = < /* NOTE: frequent in this table must be ascending order */ @@ -349,8 +349,9 @@ 816000 850000 850000 1008000 875000 875000 1200000 925000 925000 - 1320000 1000000 1000000 - 1488000 1075000 1075000 + 1320000 1100000 1100000 + 1488000 1100000 1100000 + 1536000 1140000 1140000 >; }; }; @@ -596,6 +597,7 @@ cpufreq-meson{ compatible = "amlogic,cpufreq-meson"; + syspll_fixed; status = "okay"; }; diff --git a/arch/arm/configs/odroidc_defconfig b/arch/arm/configs/odroidc_defconfig index 52685e02304d..81e1590b8a3a 100644 --- a/arch/arm/configs/odroidc_defconfig +++ b/arch/arm/configs/odroidc_defconfig @@ -494,6 +494,7 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_ARM_EXYNOS5250_CPUFREQ is not set # CONFIG_ARM_KIRKWOOD_CPUFREQ is not set CONFIG_AMLOGIC_MESON_CPUFREQ=y +CONFIG_FIX_SYSPLL=y # CONFIG_CPU_IDLE is not set # CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set diff --git a/arch/arm/mach-meson8b/clock.c b/arch/arm/mach-meson8b/clock.c old mode 100644 new mode 100755 index 50e93913677e..4b062afb20c3 --- a/arch/arm/mach-meson8b/clock.c +++ b/arch/arm/mach-meson8b/clock.c @@ -55,12 +55,13 @@ extern struct arm_delay_ops arm_delay_ops; static DEFINE_SPINLOCK(clockfw_lock); static DEFINE_MUTEX(clock_ops_lock); +static int measure_cpu_clock = 0; /**************** SYS PLL**************************/ #define SYS_PLL_TABLE_MIN 24000000 #define SYS_PLL_TABLE_MAX 2112000000 -#define CPU_FREQ_LIMIT 1488000000 +#define CPU_FREQ_LIMIT 1536000000 struct sys_pll_s { unsigned int freq; @@ -339,10 +340,11 @@ long clk_round_rate_sys(struct clk *clk, unsigned long rate) dst = setup_a9_clk_min; else if(dst > setup_a9_clk_max) dst = setup_a9_clk_max; - - idx = ((dst - SYS_PLL_TABLE_MIN) / 1000000) / 24; - //printk("sys round rate: %d -- %d\n",rate,sys_pll_settings[idx][0]); - rate = sys_pll_settings[idx][0] * 1000000; + if ((rate != 1250000000)) { + idx = ((dst - SYS_PLL_TABLE_MIN) / 1000000) / 24; + //printk("sys round rate: %ld -- %d\n",rate,sys_pll_settings[idx][0]); + rate = sys_pll_settings[idx][0] * 1000000; + } return rate; } @@ -744,8 +746,11 @@ static unsigned long clk_get_rate_a9(struct clk * clkdev) parent_clk = clk_get_rate_xtal(NULL); else if(pll_sel == 1) parent_clk = clk_get_rate_sys(clkdev->parent); - else + else if (pll_sel == 2) { + clk = 1250000000; // from MPLL / 2 + } else { printk(KERN_INFO "Error : A9 parent pll selection incorrect!\n"); + } if(parent_clk > 0){ unsigned int N = (aml_read_reg32(P_HHI_SYS_CPU_CLK_CNTL1) >> 20) & 0x3FF; unsigned int div = 1; @@ -792,11 +797,14 @@ static int _clk_set_rate_cpu(struct clk *clk, unsigned long cpu, unsigned long g unsigned long parent = 0; unsigned long oldcpu = clk_get_rate_a9(clk); unsigned int cpu_clk_cntl = aml_read_reg32(P_HHI_SYS_CPU_CLK_CNTL); + int test_n = 0; // if ((cpu_clk_cntl & 3) == 1) { { + unsigned long real_cpu; parent = clk_get_rate_sys(clk->parent); // CPU switch to xtal + aml_write_reg32(P_HHI_SYS_CPU_CLK_CNTL, cpu_clk_cntl & ~(1 << 7)); if (oldcpu <= cpu) { // when increasing frequency, lpj has already been adjusted @@ -805,7 +813,19 @@ static int _clk_set_rate_cpu(struct clk *clk, unsigned long cpu, unsigned long g // when decreasing frequency, lpj has not yet been adjusted udelay_scaled(10, oldcpu / 1000000, 24 /*clk_get_rate_xtal*/); } - set_sys_pll(clk->parent, cpu); + + aml_set_reg32_bits(P_HHI_SYS_CPU_CLK_CNTL, 1, 0, 2); // path select to syspll + if (cpu == 1250000000) { + aml_set_reg32_bits(P_HHI_MPLL_CNTL6, 1, 27, 1); + aml_set_reg32_bits(P_HHI_SYS_CPU_CLK_CNTL, 2, 0, 2); // select to mpll + aml_set_reg32_bits(P_HHI_SYS_CPU_CLK_CNTL, 0, 2, 2); // cancel external od + udelay_scaled(500, oldcpu / 1000000, 24 /*clk_get_rate_xtal*/); + printk(KERN_DEBUG"CTS_CPU_CLK %4ld --> %4ld (MHz)\n", + clk->rate / 1000000, cpu / 1000000); + clk->parent->rate = cpu; + } else { + set_sys_pll(clk->parent, cpu); + } // Read CBUS for short delay, then CPU switch to sys pll cpu_clk_cntl = aml_read_reg32(P_HHI_SYS_CPU_CLK_CNTL); @@ -818,6 +838,16 @@ static int _clk_set_rate_cpu(struct clk *clk, unsigned long cpu, unsigned long g udelay_scaled(100, oldcpu / 1000000, cpu / 1000000); } + if (measure_cpu_clock) { + while (test_n < 5) { + real_cpu = clk_util_clk_msr(18) << 4; + if ((real_cpu < cpu && (cpu - real_cpu) > 48000000) || + (real_cpu > cpu && (real_cpu - cpu) > 48000000)) { + pr_info("hope to set cpu clk as %ld, real value is %ld, time %d\n", cpu, real_cpu, test_n); + } + test_n++; + } + } // CPU switch to sys pll //cpu_clk_cntl = aml_read_reg32(P_HHI_SYS_CPU_CLK_CNTL); //aml_set_reg32_mask(P_HHI_SYS_CPU_CLK_CNTL, (1 << 7)); @@ -1173,7 +1203,12 @@ SETPLL: aml_set_reg32_bits(P_HHI_SYS_CPU_CLK_CNTL, 3, 2, 2); else aml_set_reg32_bits(P_HHI_SYS_CPU_CLK_CNTL, 0, 2, 2); - aml_write_reg32(P_HHI_SYS_PLL_CNTL, cpu_clk_cntl | (1 << 29)); + //aml_write_reg32(P_HHI_SYS_PLL_CNTL, cpu_clk_cntl | (1 << 29)); + if((cpu_clk_cntl & 0x3fff) != (curr_cntl & 0x3fff)) { + //dest M,N is equal to curr_cntl, So, we neednot reset the pll, just change the OD. + aml_write_reg32(P_HHI_SYS_PLL_CNTL, cpu_clk_cntl | (1 << 29)); + } + if(only_once == 99){ only_once = 1; aml_write_reg32(P_HHI_SYS_PLL_CNTL2, M8_SYS_PLL_CNTL_2); @@ -1684,9 +1719,25 @@ static ssize_t freq_limit_show(struct class *cla, struct class_attribute *attr, return sprintf(buf, "%d\n", freq_limit); } +static ssize_t check_clock_store(struct class *cla, struct class_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + measure_cpu_clock = input; + return count; +} +static ssize_t check_clock_show(struct class *cla, struct class_attribute *attr, char *buf) +{ + printk("%u\n", measure_cpu_clock); + return sprintf(buf, "%d\n", measure_cpu_clock); +} static struct class_attribute freq_limit_class_attrs[] = { __ATTR(limit, S_IRUGO|S_IWUSR|S_IWGRP, freq_limit_show, freq_limit_store), + __ATTR(check_clock, S_IRUGO|S_IWUSR|S_IWGRP, check_clock_show, check_clock_store), __ATTR_NULL, }; diff --git a/arch/arm/mach-meson8b/include/mach/cpufreq_table.h b/arch/arm/mach-meson8b/include/mach/cpufreq_table.h old mode 100644 new mode 100755 index 4a9bf923ebee..37e1f6a77eae --- a/arch/arm/mach-meson8b/include/mach/cpufreq_table.h +++ b/arch/arm/mach-meson8b/include/mach/cpufreq_table.h @@ -21,3 +21,21 @@ static struct cpufreq_frequency_table meson_freq_table[]= {14 , 1488000 }, {15 , CPUFREQ_TABLE_END}, }; + +#ifdef CONFIG_FIX_SYSPLL +static struct cpufreq_frequency_table meson_freq_table_fix_syspll[]= +{ + {0 , 96000 }, + {1 , 192000 }, + {2 , 384000 }, + {3 , 768000 }, +#if 1 + {4 , 1250000 }, + {5 , 1536000 }, + {6 , CPUFREQ_TABLE_END}, +#else + {4 , 1536000 }, + {5 , CPUFREQ_TABLE_END}, +#endif +}; +#endif diff --git a/drivers/amlogic/cpufreq/Kconfig b/drivers/amlogic/cpufreq/Kconfig index 3b755a790887..f15df78b0540 100644 --- a/drivers/amlogic/cpufreq/Kconfig +++ b/drivers/amlogic/cpufreq/Kconfig @@ -3,3 +3,9 @@ config AMLOGIC_MESON_CPUFREQ depends on PLAT_MESON help This adds the CPUFreq driver for Meson series CPU + +config FIX_SYSPLL + bool "Meson CPU Fix syspll mode support" + depends on ARCH_MESON8B + help + This add fix sys pll mode support for M8baby diff --git a/drivers/amlogic/cpufreq/meson-cpufreq.c b/drivers/amlogic/cpufreq/meson-cpufreq.c index 7850dab2f131..c2799e4cbda9 100755 --- a/drivers/amlogic/cpufreq/meson-cpufreq.c +++ b/drivers/amlogic/cpufreq/meson-cpufreq.c @@ -42,6 +42,9 @@ struct meson_cpufreq { }; static struct meson_cpufreq cpufreq; +#ifdef CONFIG_FIX_SYSPLL +static int fix_syspll = 0; +#endif static DEFINE_MUTEX(meson_cpufreq_mutex); @@ -100,6 +103,11 @@ static int meson_cpufreq_target_locked(struct cpufreq_policy *policy, uint cpu = policy ? policy->cpu : 0; int ret = -EINVAL; unsigned int freqInt = 0; +#ifdef CONFIG_FIX_SYSPLL + struct cpufreq_frequency_table *freq_table = NULL; + unsigned int freq_new, index; +#endif + if (cpu > (NR_CPUS - 1)) { printk(KERN_ERR"cpu %d set target freq error\n",cpu); return ret; @@ -123,7 +131,23 @@ static int meson_cpufreq_target_locked(struct cpufreq_policy *policy, } } - +#ifdef CONFIG_FIX_SYSPLL + /* + * CPU frequent should only select from aviliable frequent table + * if under fix syspll mode + */ + if (fix_syspll) { + freq_table = cpufreq_frequency_get_table(policy->cpu); + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, + CPUFREQ_RELATION_H, &index); + if(ret >= 0) { + freq_new = freq_table[index].frequency; + target_freq = freq_new; + } else { + printk(KERN_ERR"input frequent :%d cannot found in frequent table, ret:%d\n", target_freq, ret); + } + } +#endif freqs.old = clk_get_rate(cpufreq.armclk) / 1000; freqs.new = clk_round_rate(cpufreq.armclk, target_freq * 1000) / 1000; @@ -226,9 +250,18 @@ static int meson_cpufreq_init(struct cpufreq_policy *policy) cpufreq_frequency_table_get_attr(freq_table, policy->cpu); } else { +#endif +#ifdef CONFIG_FIX_SYSPLL + if (fix_syspll) { // select fix pll table if syspll_fixed is enabled in dts + cpufreq_frequency_table_get_attr(meson_freq_table_fix_syspll, + policy->cpu); + } else { #endif cpufreq_frequency_table_get_attr(meson_freq_table, policy->cpu); +#ifdef CONFIG_FIX_SYSPLL + } +#endif #if 0 } #endif @@ -348,7 +381,16 @@ static int __init meson_cpufreq_probe(struct platform_device *pdev) #ifdef CONFIG_USE_OF const void *prop; - +#ifdef CONFIG_FIX_SYSPLL + int err = 0; + if (pdev->dev.of_node) { + err = of_property_read_bool(pdev->dev.of_node, "syspll_fixed"); + if (err) { + printk("%s:SYSPLL request to be fixed\n", __func__); + fix_syspll = 1; + } + } +#endif if (pdev->dev.of_node) { prop = of_get_property(pdev->dev.of_node, "voltage_control", NULL); if(prop)