From: wurui <wu...@actions-semi.com>

commit 54fa7f93a0ee978e0e944187808c08afc508b875 from
https://github.com/xapp-le/kernel.git

Change-Id: I4c31445b5e7b06590cd71250dd16f56716ec2ea2
---
 drivers/cpufreq/Kconfig.arm          |  11 +
 drivers/cpufreq/Makefile             |   3 +-
 drivers/cpufreq/cpufreq.c            |  99 +++++-
 drivers/cpufreq/owl-cpufreq-common.c |  53 +++
 drivers/cpufreq/s500-cpufreq.c       | 628 +++++++++++++++++++++++++++++++++++
 5 files changed, 791 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 drivers/cpufreq/Kconfig.arm
 mode change 100644 => 100755 drivers/cpufreq/Makefile
 mode change 100644 => 100755 drivers/cpufreq/cpufreq.c
 create mode 100755 drivers/cpufreq/owl-cpufreq-common.c
 create mode 100755 drivers/cpufreq/s500-cpufreq.c

diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
old mode 100644
new mode 100755
index 4f3dbc8..ef96c43
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -272,3 +272,14 @@ config ARM_PXA2xx_CPUFREQ
          This add the CPUFreq driver support for Intel PXA2xx SOCs.
 
          If in doubt, say N.
+
+config ARM_OWL_CPUFREQ
+       tristate "Actions OWL CPUFreq support"
+       depends on ARCH_OWL
+       ---help---
+         This adds the CPUFreq driver for the Actions OWL SoC. 
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called owl_cpufreq.
\ No newline at end of file
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
old mode 100644
new mode 100755
index cdce92a..cced82b
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -79,7 +79,8 @@ obj-$(CONFIG_ARM_SA1110_CPUFREQ)      += sa1110-cpufreq.o
 obj-$(CONFIG_ARM_SPEAR_CPUFREQ)                += spear-cpufreq.o
 obj-$(CONFIG_ARM_TEGRA_CPUFREQ)                += tegra-cpufreq.o
 obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
-
+obj-$(CONFIG_ARM_OWL_CPUFREQ)          += owl-cpufreq-common.o
+obj-$(CONFIG_ARM_OWL_CPUFREQ)          += s500-cpufreq.o
 
##################################################################################
 # PowerPC platform drivers
 obj-$(CONFIG_CPU_FREQ_CBE)             += ppc-cbe-cpufreq.o
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
old mode 100644
new mode 100755
index 8ae655c..614e9cd
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -17,6 +17,8 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+
+#include <mach/bootdev.h>
 #include <linux/cpu.h>
 #include <linux/cpufreq.h>
 #include <linux/delay.h>
@@ -29,6 +31,7 @@
 #include <linux/suspend.h>
 #include <linux/syscore_ops.h>
 #include <linux/tick.h>
+#include <linux/mm.h>
 #include <trace/events/power.h>
 
 /* Macros to iterate over lists */
@@ -494,6 +497,66 @@ out:
        return err;
 }
 
+static char procname[PAGE_SIZE];
+int proc_pid_cmdline_global(struct task_struct *task, char * buffer)
+{
+       int res = 0;
+       unsigned int len;
+       struct mm_struct *mm = get_task_mm(task);
+       if (!mm)
+               goto out;
+       if (!mm->arg_end)
+               goto out_mm;    /* Shh! No looking before we're done */
+
+       len = mm->arg_end - mm->arg_start;
+
+       if (len > PAGE_SIZE)
+               len = PAGE_SIZE;
+
+       res = access_process_vm(task, mm->arg_start, buffer, len, 0);
+
+       // If the nul at the end of args has been overwritten, then
+       // assume application is using setproctitle(3).
+       if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {
+               len = strnlen(buffer, res);
+               if (len < res) {
+                   res = len;
+               } else {
+                       len = mm->env_end - mm->env_start;
+                       if (len > PAGE_SIZE - res)
+                               len = PAGE_SIZE - res;
+                       res += access_process_vm(task, mm->env_start, 
buffer+res, len, 0);
+                       res = strnlen(buffer, res);
+               }
+       }
+out_mm:
+       mmput(mm);
+out:
+       return res;
+}
+
+static int is_game_mode(const char *str){
+       if ((strstr(str, "com.gameloft.android") != NULL 
+               || strstr(str, "com.ea.games") != NULL
+               || strstr(str, "pplive") != NULL
+               || strstr(str, "sohu") != NULL
+               || strstr(str, "xqiyi") != NULL
+               || strstr(str, ".pps.") != NULL
+               || strstr(str, "qqlive") != NULL
+               || strstr(str, "com.g5e") != NULL
+               || strstr(str, ".sega.") != NULL
+               || strstr(str, "com.konami") != NULL
+               || strstr(str, "com.chillingo") != NULL
+               || strstr(str, "com.capcom") != NULL
+               || strstr(str, "com.g2us") != NULL
+               || strstr(str, "com.glu") != NULL
+               || strstr(str, "com.disney") != NULL)
+               &&
+               (strcmp(str, "com.gameloft.android.ANMP.GloftA8HM")!=0))
+               return 1;
+       else
+               return 0;
+}
 /**
  * cpufreq_per_cpu_attr_read() / show_##file_name() -
  * print out cpufreq information
@@ -509,8 +572,20 @@ static ssize_t show_##file_name                            
\
        return sprintf(buf, "%u\n", policy->object);    \
 }
 
+static ssize_t show_cpuinfo_max_freq(struct cpufreq_policy *policy, char *buf)
+{
+       int isNFS = 0;  // determin if we're in NFS.
+
+       proc_pid_cmdline_global(current, procname);
+
+       if(is_game_mode(procname))
+               isNFS = 1;
+       
+       return sprintf(buf, "%u\n", 
isNFS?policy->cpuinfo.max_freq/2:policy->cpuinfo.max_freq);
+}
+
 show_one(cpuinfo_min_freq, cpuinfo.min_freq);
-show_one(cpuinfo_max_freq, cpuinfo.max_freq);
+//show_one(cpuinfo_max_freq, cpuinfo.max_freq);
 show_one(cpuinfo_transition_latency, cpuinfo.transition_latency);
 show_one(scaling_min_freq, min);
 show_one(scaling_max_freq, max);
@@ -565,9 +640,15 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy 
*policy,
                                        char *buf)
 {
        unsigned int cur_freq = __cpufreq_get(policy);
+       int isNFS = 0;  // determin if we're in NFS.
+
+       proc_pid_cmdline_global(current, procname);
+       if(is_game_mode(procname))
+               isNFS = 1;
+
        if (!cur_freq)
                return sprintf(buf, "<unknown>");
-       return sprintf(buf, "%u\n", cur_freq);
+       return sprintf(buf, "%u\n", isNFS?cur_freq/2:cur_freq);
 }
 
 /**
@@ -2416,6 +2497,11 @@ int cpufreq_register_driver(struct cpufreq_driver 
*driver_data)
        if (cpufreq_disabled())
                return -ENODEV;
 
+               
+       if (owl_get_boot_mode() == OWL_BOOT_MODE_UPGRADE) {
+               printk("upgrade process cpufreq disabled!!\n");
+               return -ENODEV;
+       }
        if (!driver_data || !driver_data->verify || !driver_data->init ||
            !(driver_data->setpolicy || driver_data->target_index ||
                    driver_data->target) ||
@@ -2530,6 +2616,15 @@ static int __init cpufreq_core_init(void)
        if (cpufreq_disabled())
                return -ENODEV;
 
+       
+       
+       
+       if (owl_get_boot_mode() == OWL_BOOT_MODE_UPGRADE) {
+               printk("upgrade process cpufreq disabled!!\n");
+               return -ENODEV;
+       }
+
+       
        cpufreq_global_kobject = kobject_create();
        BUG_ON(!cpufreq_global_kobject);
 
diff --git a/drivers/cpufreq/owl-cpufreq-common.c 
b/drivers/cpufreq/owl-cpufreq-common.c
new file mode 100755
index 0000000..b599dd1
--- /dev/null
+++ b/drivers/cpufreq/owl-cpufreq-common.c
@@ -0,0 +1,53 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#if 0
+#define CPUFREQ_DBG(format, ...) \
+       printk(KERN_NOTICE "owl cpufreq: " format, ## __VA_ARGS__)
+
+#else /* DEBUG */
+#define CPUFREQ_DBG(format, ...)
+#endif
+
+#define CPUFREQ_ERR(format, ...) \
+       printk(KERN_ERR "owl cpufreq: " format, ## __VA_ARGS__)
+
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <asm/cpu.h>
+
+#include <mach/module-owl.h>
+#include <mach/clkname.h>
+#include <mach/clkname_priv.h>
+#include <mach/cpu_map-owl.h>
+
+extern int owl_cpu0_cpufreq_driver_init(void);
+extern void owl_cpu0_cpufreq_driver_exit(void);
+
+static int cpu0_cpufreq_driver_init(void)
+{
+       return owl_cpu0_cpufreq_driver_init();
+}
+
+static void __exit cpu0_cpufreq_driver_exit(void)
+{
+       return cpu0_cpufreq_driver_exit();
+}
+
+late_initcall(cpu0_cpufreq_driver_init);
+module_exit(cpu0_cpufreq_driver_exit);
+
+MODULE_AUTHOR("Actions semi");
+MODULE_DESCRIPTION("owl CPU0 cpufreq driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/s500-cpufreq.c b/drivers/cpufreq/s500-cpufreq.c
new file mode 100755
index 0000000..67475f6
--- /dev/null
+++ b/drivers/cpufreq/s500-cpufreq.c
@@ -0,0 +1,628 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#if 0
+#define CPUFREQ_DBG(format, ...) \
+       printk(KERN_NOTICE "owl cpufreq: " format, ## __VA_ARGS__)
+
+#else /* DEBUG */
+#define CPUFREQ_DBG(format, ...)
+#endif
+
+#define CPUFREQ_ERR(format, ...) \
+       printk(KERN_ERR "owl cpufreq: " format, ## __VA_ARGS__)
+
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <asm/cpu.h>
+
+#include <mach/module-owl.h>
+#include <mach/clkname.h>
+#include <mach/clkname_priv.h>
+#include <mach/cpu_map-owl.h>
+
+
+#define CPU_REGULATOR_NAME     "cpuvdd"
+
+static unsigned int transition_latency;
+
+static struct device *cpu_dev;
+static struct clk *cpu_clk;
+static struct clk *core_pll;
+static struct regulator *cpu_reg;
+struct cpufreq_frequency_table *freq_table;
+
+static unsigned int locking_frequency;
+static bool frequency_locked;
+static DEFINE_MUTEX(freq_table_mux);
+
+static struct cpu0_opp_table cpu0_table[] = {
+       /*khz           uV*/
+
+       {1104000, 1175000},
+       { 900000, 1025000},
+       { 720000,  975000},
+       { 504000,  950000},
+       { 240000,  950000},
+};
+
+struct device cpu0_dev;
+
+struct cpu0_opp_info {
+       struct device *dev;
+       struct cpu0_opp_table *cpu0_table;
+       int cpu0_table_num;
+};
+
+static struct cpu0_opp_info cpu0_opp_info;
+static struct cpu0_opp_info *cpu0_opp_info_cur;
+
+static u32 cpuinfo_max_freq;
+static u32 scaling_max_freq;
+static u32 cpuinfo_min_freq;
+
+/*
+       find max_freq/min_freq in cpu0_opp_info
+*/
+static int find_cpu0_opp_info_max_min_freq(u32 *max_freq, u32 
*scaling_max_freq, u32 *min_freq)
+{
+       u32 max_tmp = 0;
+       u32 min_tmp = UINT_MAX;
+
+       struct cpu0_opp_table *opp_table;
+       int opp_table_num;
+
+       opp_table = cpu0_opp_info.cpu0_table;
+       opp_table_num = cpu0_opp_info.cpu0_table_num;
+       if (opp_table[0].clk > max_tmp)
+               max_tmp = opp_table[0].clk;
+               
+       if (opp_table[opp_table_num - 1].clk < min_tmp)
+               min_tmp = opp_table[opp_table_num - 1].clk;
+
+       if (max_tmp < min_tmp)
+               return -EINVAL;
+
+       *max_freq = max_tmp;
+       *scaling_max_freq = max_tmp;
+       *min_freq = min_tmp;
+       return 0;
+}
+
+/*
+       init cpu0_opp_info/cpu0_opp_info_user
+*/
+static int init_cpu0_opp_info(void)
+{
+       cpu0_opp_info.cpu0_table = cpu0_table;
+       cpu0_opp_info.cpu0_table_num = ARRAY_SIZE(cpu0_table);
+               
+       cpu0_opp_info.dev = &cpu0_dev;
+    
+       return 0;
+}
+
+/*
+       get cpu0_opp_info_cur according to online_cpus
+*/
+static int select_cur_opp_info(void)
+{
+
+       cpu0_opp_info_cur = &cpu0_opp_info;
+
+       return 0;
+}
+
+int cpu0_add_opp_table(struct device *cpu_dev, struct cpu0_opp_table *table, 
int table_size)
+{
+       int i;
+       int err;
+
+       if (!cpu_dev) {
+               printk("cpufreq map: failed to get cpu0 device\n");
+               return -ENODEV;
+       }
+
+       for (i = 0; i < table_size; i++) {
+               /*table.clk is in khz*/
+               err = dev_pm_opp_add(cpu_dev, table[i].clk * 1000, 
table[i].volt);
+               if (err) {
+                       printk("cpufreq map: Cannot add opp entries.\n");
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int CF0AOT_all(void)
+{
+       int ret;
+       
+       /* opp_add clk/volt of cpu0_opp_info*/
+       ret = cpu0_add_opp_table(cpu0_opp_info.dev, cpu0_opp_info.cpu0_table,
+                       cpu0_opp_info.cpu0_table_num);
+       if (ret) {
+               CPUFREQ_ERR("failed to init OPP table: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int owl_cpu0_clk_init(void)
+{
+       int ret;
+
+       core_pll = clk_get(NULL, CLKNAME_COREPLL);
+       if (IS_ERR(core_pll)) {
+               ret = PTR_ERR(core_pll);
+               CPUFREQ_ERR("failed to get core pll: %d\n", ret);
+               return ret;
+       }
+
+       cpu_clk = clk_get(NULL, CLKNAME_CPU_CLK);
+       if (IS_ERR(cpu_clk)) {
+               ret = PTR_ERR(cpu_clk);
+               CPUFREQ_ERR("failed to get cpu clk: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void owl_cpu0_clk_free(void)
+{
+       clk_put(core_pll);
+       clk_put(cpu_clk);
+}
+
+static long owl_round_cpu0_clk(unsigned long rate)
+{
+       long freq_HZ;
+       freq_HZ = clk_round_rate(core_pll, rate);
+       return freq_HZ;
+}
+
+static unsigned long owl_get_cpu0_clk(void)
+{
+       unsigned long freq_HZ;
+       freq_HZ = clk_get_rate(cpu_clk);
+       return freq_HZ;
+}
+
+static int owl_set_cpu0_clk(unsigned long rate)
+{
+       int ret = 0;
+       ret = clk_set_rate(core_pll, rate);
+       return ret;
+}
+
+static int cpu0_cpuinfo_update(struct cpufreq_policy *policy, u32 max_freq, 
u32 min_freq)
+{
+       policy->min = policy->cpuinfo.min_freq = min_freq;
+       policy->max = policy->cpuinfo.max_freq = max_freq;
+       return 0;
+}
+
+static int cpu0_verify_speed(struct cpufreq_policy *policy)
+{
+       int ret;
+
+       if (mutex_lock_interruptible(&freq_table_mux))
+               return -EINVAL;
+
+       ret = cpufreq_frequency_table_verify(policy, freq_table);
+
+       mutex_unlock(&freq_table_mux);
+       return ret;
+}
+
+static unsigned int cpu0_get_speed(unsigned int cpu)
+{
+       int freq_khz;
+
+       freq_khz = owl_get_cpu0_clk() / 1000;
+       return freq_khz;
+}
+
+#define volt_align(d, a) (((d) + (a - 1)) & ~(a - 1))
+
+static int owl_cpufreq_scale(struct cpufreq_policy *policy,
+                          unsigned int target_freq, unsigned int relation)
+{
+       struct cpufreq_freqs freqs;
+       struct dev_pm_opp *opp;
+       unsigned long freq_hz, volt = 0, volt_old = 0;
+       unsigned long volt_delta = 0, volt_step = 0;
+       unsigned int index;
+       int ret = 0;
+
+       CPUFREQ_DBG("%s , target freq = %d\n", __func__, target_freq);
+       
+       ret = cpufreq_frequency_table_target(policy, freq_table,
+                               target_freq, relation, &index);
+       if (ret) {
+               CPUFREQ_ERR("failed to match target freqency %d: %d\n",
+                      target_freq, ret);
+               goto target_out;
+       }
+
+       target_freq = freq_table[index].frequency;
+
+       freq_hz = owl_round_cpu0_clk(target_freq * 1000);
+       if (freq_hz < 0)
+               freq_hz = target_freq * 1000;
+
+       freqs.new = freq_hz / 1000;
+       freqs.old = owl_get_cpu0_clk() / 1000;
+
+       if (freqs.old == freqs.new) {
+               ret = 0;
+               goto target_out;
+       }
+
+       cpufreq_freq_transition_begin(policy, &freqs);
+
+       if (cpu_reg) {
+               rcu_read_lock();
+               opp = dev_pm_opp_find_freq_ceil(cpu0_opp_info_cur->dev, 
&freq_hz);
+               if (IS_ERR(opp)) {
+                       rcu_read_unlock();
+                       CPUFREQ_ERR("failed to find OPP for %ld\n", freq_hz);
+                       freqs.new = freqs.old;
+                       ret = PTR_ERR(opp);
+                       goto post_notify;
+               }
+               volt = dev_pm_opp_get_voltage(opp);
+               rcu_read_unlock();
+               volt_old = regulator_get_voltage(cpu_reg);
+       }
+
+       CPUFREQ_DBG("%u MHz, %ld mV --> %u MHz, %ld mV\n",
+               freqs.old / 1000, volt_old ? volt_old / 1000 : -1,
+               freqs.new / 1000, volt ? volt / 1000 : -1);
+
+       /* scaling up?  scale voltage before frequency */
+       if (cpu_reg && (volt > volt_old)) {
+               CPUFREQ_DBG("set voltage PRE freqscale\n");
+
+               ret = regulator_set_voltage(cpu_reg, volt, INT_MAX);
+               if (ret) {
+                       CPUFREQ_ERR("failed to scale voltage up: %d\n", ret);
+                       freqs.new = freqs.old;
+                       //freqs.new = CFRUC(real_freqs.new);
+                       goto post_notify;
+               }
+               CPUFREQ_DBG("set voltage PRE freqscale OK\n");
+       }
+
+       ret = owl_set_cpu0_clk(freqs.new * 1000);
+
+       if (ret) {
+               CPUFREQ_ERR("failed to set clock rate: %d\n", ret);
+               if (cpu_reg)
+                       regulator_set_voltage(cpu_reg, volt_old, INT_MAX);
+               freqs.new = freqs.old;
+               //freqs.new = CFRUC(real_freqs.new);
+               goto post_notify;
+       }
+
+       /* scaling down?  scale voltage after frequency */
+       if (cpu_reg && (volt < volt_old)) {
+               CPUFREQ_DBG("set voltage after freqscale\n");
+               volt_delta = volt_old - volt;
+               if (volt_delta >= 100*1000) {
+                       volt_step = volt + volt_align(volt_delta/2, 25*1000);
+                       ret = regulator_set_voltage(cpu_reg, volt_step, 
INT_MAX);
+                       if (ret == 0)
+                               ret = regulator_set_voltage(cpu_reg, volt, 
INT_MAX);
+               } else
+                       ret = regulator_set_voltage(cpu_reg, volt, INT_MAX);
+
+               if (ret) {
+                       CPUFREQ_ERR("failed to scale voltage down: %d\n", ret);
+                       owl_set_cpu0_clk(freqs.old * 1000);
+                       freqs.new = freqs.old;
+                       //freqs.new = CFRUC(real_freqs.new);
+               }
+               CPUFREQ_DBG("set voltage after freqscale OK\n");
+       }
+
+post_notify:
+       cpufreq_freq_transition_end(policy, &freqs, 0);
+target_out:
+       return ret;
+}
+
+static int cpu0_set_target(struct cpufreq_policy *policy,
+                          unsigned int target_freq, unsigned int relation)
+{
+       int ret = 0;
+
+       if (mutex_lock_interruptible(&freq_table_mux))
+               return -EINVAL;
+
+       if (frequency_locked)
+               goto target_out;
+
+       ret = owl_cpufreq_scale(policy, target_freq, relation);
+
+target_out:
+       mutex_unlock(&freq_table_mux);
+       return ret;
+}
+
+static int cpu0_cpufreq_init(struct cpufreq_policy *policy)
+{
+       int ret = 0;
+       int freq_khz;
+       unsigned int index;
+
+       if (mutex_lock_interruptible(&freq_table_mux))
+               return -EINVAL;
+
+       if (policy->cpu != 0) {
+
+               /*let cpu1,2,3 as the managered cpu*/
+               policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+               cpumask_setall(policy->cpus);
+
+               /*The newly booted cpu will corrupt loops_per_jiffy*/
+               /*and its own percpu loops_per_jiffy is also wrong*/
+               /*these 2 value were init calc value without cpufreq*/
+               /*so we should change them back according to current*/
+               /*freq.*/
+               loops_per_jiffy =
+                       per_cpu(cpu_data, policy->cpu).loops_per_jiffy =
+                       per_cpu(cpu_data, 0).loops_per_jiffy;
+
+               ret = 0;
+               goto init_out;
+       }
+
+       ret = cpu0_cpuinfo_update(policy, cpuinfo_max_freq, cpuinfo_min_freq);
+       if (ret) {
+               CPUFREQ_ERR("invalid frequency table: %d\n", ret);
+               goto init_out;
+       }
+
+       freq_khz = owl_get_cpu0_clk() / 1000;
+       cpufreq_frequency_table_target(policy, freq_table, freq_khz,
+                                       CPUFREQ_RELATION_L, &index);
+       freq_khz = freq_table[index].frequency;
+       /***************************************/
+
+       /***we need to report freq table******/
+       ret = cpu0_cpuinfo_update(policy, cpuinfo_max_freq, cpuinfo_min_freq);
+       if (ret) {
+               CPUFREQ_ERR("invalid frequency table: %d\n", ret);
+               goto init_out;
+       }
+
+       policy->min = cpuinfo_min_freq;
+       policy->max = scaling_max_freq;
+
+       policy->cpuinfo.transition_latency = transition_latency;
+       policy->cur = freq_khz;
+       /*
+        * The driver only supports the SMP configuartion where all processors
+        * share the clock and voltage and clock.  Use cpufreq affected_cpus
+        * interface to have all CPUs scaled together.
+        */
+       policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+       cpumask_setall(policy->cpus);
+
+       cpufreq_table_validate_and_show(policy, freq_table);
+
+init_out:
+       mutex_unlock(&freq_table_mux);
+       return ret;
+}
+
+static int cpu0_cpufreq_exit(struct cpufreq_policy *policy)
+{
+       if (policy->cpu != 0)
+               return 0;
+
+       return 0;
+}
+
+static struct freq_attr *cpu0_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
+static struct cpufreq_driver cpu0_cpufreq_driver = {
+       .flags = CPUFREQ_STICKY,
+       .verify = cpu0_verify_speed,
+       .target = cpu0_set_target,
+       .get = cpu0_get_speed,
+       .init = cpu0_cpufreq_init,
+       .exit = cpu0_cpufreq_exit,
+       .name = "owl_cpu0",
+       .attr = cpu0_cpufreq_attr,
+};
+
+/**
+ * owl_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
+ *                     context
+ * @notifier
+ * @pm_event
+ * @v
+ *
+ * While frequency_locked == true, target() ignores every frequency but
+ * locking_frequency. The locking_frequency value is the initial frequency,
+ * which is set by the bootloader. In order to eliminate possible
+ * inconsistency in clock values, we save and restore frequencies during
+ * suspend and resume and block CPUFREQ activities. Note that the standard
+ * suspend/resume cannot be used as they are too deep (syscore_ops) for
+ * regulator actions.
+ */
+static int owl_cpufreq_pm_notifier(struct notifier_block *notifier,
+                                      unsigned long pm_event, void *v)
+{
+       int i;
+#ifdef SUSPEND_LOCK_FREQ
+       int ret;
+#endif
+
+       switch (pm_event) {
+       case PM_SUSPEND_PREPARE:
+       case PM_HIBERNATION_PREPARE:
+       case PM_RESTORE_PREPARE:
+               mutex_lock(&freq_table_mux);
+               frequency_locked = true;
+               mutex_unlock(&freq_table_mux);
+               disable_nonboot_cpus();
+#ifdef SUSPEND_LOCK_FREQ
+               ret = owl_cpufreq_scale(cpufreq_cpu_get(0), locking_frequency, 
CPUFREQ_RELATION_L);
+               if (ret < 0)
+                       return NOTIFY_BAD;
+#endif
+               break;
+
+       case PM_POST_SUSPEND:
+       case PM_POST_HIBERNATION:
+       case PM_POST_RESTORE:
+               for (i = 1; i < CONFIG_NR_CPUS; i++)
+                       cpu_up(i);
+               mutex_lock(&freq_table_mux);
+               frequency_locked = false;
+               mutex_unlock(&freq_table_mux);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+void owl_cpufreq_frequency_lock(void)
+{
+       frequency_locked = true;
+}
+void owl_cpufreq_frequency_unlock(void)
+{
+       frequency_locked = false;
+}
+
+static struct notifier_block owl_cpufreq_nb = {
+       .notifier_call = owl_cpufreq_pm_notifier,
+};
+int owl_cpu0_cpufreq_driver_init(void)
+{
+       struct device_node *np;
+       int ret;
+
+       np = of_find_node_by_path("/cpus/cpu@0");
+       if (!np) {
+               CPUFREQ_ERR("failed to find cpu0 node\n");
+               return -ENOENT;
+       }
+
+       cpu_dev = get_cpu_device(0);
+       if (!cpu_dev) {
+               CPUFREQ_ERR("failed to get cpu0 device\n");
+               ret = -ENODEV;
+               goto out_put_node;
+       }
+
+       cpu_dev->of_node = np;
+
+       ret = owl_cpu0_clk_init();
+       if (ret) {
+               CPUFREQ_ERR("failed to get cpu0 clock: %d\n", ret);
+               goto out_put_node;
+       }
+       cpu_reg = devm_regulator_get(cpu_dev, CPU_REGULATOR_NAME);
+       if (IS_ERR(cpu_reg)) {
+               CPUFREQ_ERR("failed to get cpu0 regulator\n");
+               cpu_reg = NULL;
+       }
+
+       /*of_init_opp_table*/
+       init_cpu0_opp_info();
+       find_cpu0_opp_info_max_min_freq(&cpuinfo_max_freq, &scaling_max_freq, 
&cpuinfo_min_freq);
+       select_cur_opp_info();
+
+       ret = CF0AOT_all();
+       if (ret)
+               goto out_put_clk;
+
+       ret = dev_pm_opp_init_cpufreq_table(cpu0_opp_info_cur->dev, 
&freq_table);
+       if (ret) {
+               CPUFREQ_ERR("failed to init cpufreq table: %d\n", ret);
+               goto out_put_clk;
+       }
+
+       if (of_property_read_u32(np, "clock-latency", &transition_latency)){
+               transition_latency = CPUFREQ_ETERNAL;
+       }
+
+       if (cpu_reg) {
+               struct dev_pm_opp *opp;
+               unsigned long min_uV, max_uV;
+               int i;
+
+               /*
+                * OPP is maintained in order of increasing frequency, and
+                * freq_table initialised from OPP is therefore sorted in the
+                * same order.
+                */
+               for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
+                       ;
+               rcu_read_lock();
+               opp = dev_pm_opp_find_freq_exact(cpu0_opp_info_cur->dev,
+                               freq_table[0].frequency * 1000, true);
+               min_uV = dev_pm_opp_get_voltage(opp);
+               opp = dev_pm_opp_find_freq_exact(cpu0_opp_info_cur->dev,
+                               freq_table[i-1].frequency * 1000, true);
+               max_uV = dev_pm_opp_get_voltage(opp);
+               rcu_read_unlock();
+#ifdef CONFIG_REGULATOR
+               ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
+               if (ret > 0)
+                       transition_latency += ret;
+#endif
+       }
+
+       locking_frequency = cpu0_get_speed(0);
+       CPUFREQ_DBG("[%s], locking_frequency:%d\n", __func__, 
locking_frequency);
+       register_pm_notifier(&owl_cpufreq_nb);
+       
+       ret = cpufreq_register_driver(&cpu0_cpufreq_driver);
+       if (ret) {
+               CPUFREQ_ERR("failed register driver: %d\n", ret);
+               goto out_free_convert_table;
+       }
+
+       of_node_put(np);
+
+       return 0;
+
+out_free_convert_table:
+       unregister_pm_notifier(&owl_cpufreq_nb);
+       dev_pm_opp_free_cpufreq_table(cpu0_opp_info_cur->dev, &freq_table);
+out_put_clk:
+       owl_cpu0_clk_free();
+out_put_node:
+       of_node_put(np);
+       return ret;
+}
+
+void owl_cpu0_cpufreq_driver_exit(void)
+{
+       cpufreq_unregister_driver(&cpu0_cpufreq_driver);
+       dev_pm_opp_free_cpufreq_table(cpu0_opp_info_cur->dev, &freq_table);
+       owl_cpu0_clk_free();
+}
-- 
2.7.4

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to