From: Jongpill Lee <boyko....@samsung.com>

This patch adds DVS lock feature for other driver and pm/
reboot notifier to enhance stability.

Signed-off-by: Jongpill Lee <boyko....@samsung.com>
Signed-off-by: SangWook Ju <sw...@samsung.com>
Signed-off-by: Jonghwan Choi <jhbird.c...@samsung.com>
Signed-off-by: Jaecheol Lee <jc....@samsung.com>
---
 arch/arm/mach-exynos4/include/mach/cpufreq.h |   39 ++++++
 drivers/cpufreq/exynos4210-cpufreq.c         |  174
+++++++++++++++++++++++++-
 2 files changed, 207 insertions(+), 6 deletions(-)
 create mode 100644 arch/arm/mach-exynos4/include/mach/cpufreq.h

diff --git a/arch/arm/mach-exynos4/include/mach/cpufreq.h
b/arch/arm/mach-exynos4/include/mach/cpufreq.h
new file mode 100644
index 0000000..7e00931
--- /dev/null
+++ b/arch/arm/mach-exynos4/include/mach/cpufreq.h
@@ -0,0 +1,39 @@
+/* linux/arch/arm/mach-exynos4/include/mach/cpufreq.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * EXYNOS4 - CPUFreq support
+ *
+ * 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.
+ */
+
+/*
+ * CPU frequency level index for using cpufreq lock API
+ * This should be same with cpufreq_frequency_table
+ */
+enum cpufreq_level_request {
+       CPU_L0,         /* 1200MHz */
+       CPU_L1,         /* 1000MHz */
+       CPU_L2,         /* 800MHz */
+       CPU_L3,         /* 500MHz */
+       CPU_L4,         /* 200MHz */
+       CPU_LEVEL_END,
+};
+
+enum cpufreq_lock_ID {
+       DVFS_LOCK_ID_G2D,       /* G2D */
+       DVFS_LOCK_ID_TV,        /* TV */
+       DVFS_LOCK_ID_MFC,       /* MFC */
+       DVFS_LOCK_ID_USB,       /* USB */
+       DVFS_LOCK_ID_CAM,       /* CAM */
+       DVFS_LOCK_ID_PM,        /* PM */
+       DVFS_LOCK_ID_USER,      /* USER */
+       DVFS_LOCK_ID_END,
+};
+
+int exynos4_cpufreq_lock(unsigned int nId,
+                       enum cpufreq_level_request cpufreq_level);
+void exynos4_cpufreq_lock_free(unsigned int nId);
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c
b/drivers/cpufreq/exynos4210-cpufreq.c
index 246f9e2..30e1949 100644
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -17,14 +17,21 @@
 #include <linux/slab.h>
 #include <linux/regulator/consumer.h>
 #include <linux/cpufreq.h>
+#include <linux/suspend.h>
+#include <linux/reboot.h>
 
 #include <mach/map.h>
 #include <mach/regs-clock.h>
 #include <mach/regs-mem.h>
+#include <mach/cpufreq.h>
 
 #include <plat/clock.h>
 #include <plat/pm.h>
 
+static bool exynos4_cpufreq_init_done;
+static DEFINE_MUTEX(set_freq_lock);
+static DEFINE_MUTEX(set_cpu_freq_lock);
+
 static struct clk *cpu_clk;
 static struct clk *moutcore;
 static struct clk *mout_mpll;
@@ -53,6 +60,12 @@ static struct cpufreq_frequency_table
exynos4_freq_table[] = {
        {0, CPUFREQ_TABLE_END},
 };
 
+/* This defines are for cpufreq lock */
+#define CPUFREQ_MIN_LEVEL      (CPUFREQ_LEVEL_END - 1)
+unsigned int cpufreq_lock_id;
+unsigned int cpufreq_lock_val[DVFS_LOCK_ID_END];
+unsigned int cpufreq_lock_level = CPUFREQ_MIN_LEVEL;
+
 static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = {
        /*
         * Clock divider value for following
@@ -272,22 +285,31 @@ static int exynos4_target(struct cpufreq_policy
*policy,
 {
        unsigned int index, old_index;
        unsigned int arm_volt;
+       int ret = 0;
+
+       mutex_lock(&set_freq_lock);
 
        freqs.old = exynos4_getspeed(policy->cpu);
 
        if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
-                                          freqs.old, relation, &old_index))
-               return -EINVAL;
+                                          freqs.old, relation, &old_index))
{
+               ret = -EINVAL;
+               goto out;
+       }
 
        if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
-                                          target_freq, relation, &index))
-               return -EINVAL;
+                                          target_freq, relation, &index)) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        freqs.new = exynos4_freq_table[index].frequency;
        freqs.cpu = policy->cpu;
 
-       if (freqs.new == freqs.old)
-               return 0;
+       if (freqs.new == freqs.old) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        /* get the voltage value */
        arm_volt = exynos4_volt_table[index].arm_volt;
@@ -311,8 +333,98 @@ static int exynos4_target(struct cpufreq_policy
*policy,
                regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
        }
 
+out:
+       mutex_unlock(&set_freq_lock);
+
+       return ret;
+}
+
+atomic_t exynos4_cpufreq_lock_count;
+
+int exynos4_cpufreq_lock(unsigned int id,
+               enum cpufreq_level_request cpufreq_level)
+{
+       int i, old_idx = 0;
+       unsigned int freq_old, freq_new, arm_volt;
+
+       if (!exynos4_cpufreq_init_done)
+               return 0;
+
+       if (cpufreq_lock_id & (1 << id)) {
+               printk(KERN_ERR "%s:Device [%d] already locked cpufreq\n",
+                               __func__,  id);
+               return 0;
+       }
+       mutex_lock(&set_cpu_freq_lock);
+       cpufreq_lock_id |= (1 << id);
+       cpufreq_lock_val[id] = cpufreq_level;
+
+       /* If the requested cpufreq is higher than current min frequency */
+       if (cpufreq_level < cpufreq_lock_level)
+               cpufreq_lock_level = cpufreq_level;
+
+       mutex_unlock(&set_cpu_freq_lock);
+
+       /*
+        * If current frequency is lower than requested freq,
+        * it needs to update
+        */
+       mutex_lock(&set_freq_lock);
+       freq_old = exynos4_getspeed(0);
+       freq_new = exynos4_freq_table[cpufreq_level].frequency;
+       if (freq_old < freq_new) {
+               /* Find out current level index */
+               for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++) {
+                       if (freq_old == exynos4_freq_table[i].frequency) {
+                               old_idx = exynos4_freq_table[i].index;
+                               break;
+                       } else if (i == (CPUFREQ_LEVEL_END - 1)) {
+                               printk(KERN_ERR "%s: Level not found\n",
+                                               __func__);
+                               mutex_unlock(&set_freq_lock);
+                               return -EINVAL;
+                       } else {
+                               continue;
+                       }
+               }
+               freqs.old = freq_old;
+               freqs.new = freq_new;
+               cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+               /* get the voltage value */
+               arm_volt = exynos4_volt_table[cpufreq_level].arm_volt;
+               regulator_set_voltage(arm_regulator, arm_volt,
+                               arm_volt);
+
+               exynos4_set_frequency(old_idx, cpufreq_level);
+               cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+       }
+       mutex_unlock(&set_freq_lock);
+
        return 0;
 }
+EXPORT_SYMBOL_GPL(exynos4_cpufreq_lock);
+
+void exynos4_cpufreq_lock_free(unsigned int id)
+{
+       int i;
+
+       if (!exynos4_cpufreq_init_done)
+               return;
+
+       mutex_lock(&set_cpu_freq_lock);
+       cpufreq_lock_id &= ~(1 << id);
+       cpufreq_lock_val[id] = CPUFREQ_MIN_LEVEL;
+       cpufreq_lock_level = CPUFREQ_MIN_LEVEL;
+       if (cpufreq_lock_id) {
+               for (i = 0; i < DVFS_LOCK_ID_END; i++) {
+                       if (cpufreq_lock_val[i] < cpufreq_lock_level)
+                               cpufreq_lock_level = cpufreq_lock_val[i];
+               }
+       }
+       mutex_unlock(&set_cpu_freq_lock);
+}
+EXPORT_SYMBOL_GPL(exynos4_cpufreq_lock_free);
 
 #ifdef CONFIG_PM
 static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy)
@@ -326,6 +438,31 @@ static int exynos4_cpufreq_resume(struct cpufreq_policy
*policy)
 }
 #endif
 
+static int exynos4_cpufreq_notifier_event(struct notifier_block *this,
+               unsigned long event, void *ptr)
+{
+       int ret;
+
+       switch (event) {
+       case PM_SUSPEND_PREPARE:
+               ret = exynos4_cpufreq_lock(DVFS_LOCK_ID_PM, CPU_L0);
+               if (ret < 0)
+                       return NOTIFY_BAD;
+               pr_debug("PM_SUSPEND_PREPARE for CPUFREQ\n");
+               return NOTIFY_OK;
+       case PM_POST_RESTORE:
+       case PM_POST_SUSPEND:
+               pr_debug("PM_POST_SUSPEND for CPUFREQ\n");
+               exynos4_cpufreq_lock_free(DVFS_LOCK_ID_PM);
+               return NOTIFY_OK;
+       }
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block exynos4_cpufreq_notifier = {
+       .notifier_call = exynos4_cpufreq_notifier_event,
+};
+
 static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
        policy->cur = policy->min = policy->max =
exynos4_getspeed(policy->cpu);
@@ -351,6 +488,23 @@ static int exynos4_cpufreq_cpu_init(struct
cpufreq_policy *policy)
        return cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
 }
 
+static int exynos4_cpufreq_reboot_notifier_call(struct notifier_block
*this,
+               unsigned long code, void *_cmd)
+{
+       int ret;
+
+       ret = exynos4_cpufreq_lock(DVFS_LOCK_ID_PM, CPU_L0);
+       if (ret < 0)
+               return NOTIFY_BAD;
+
+       printk(KERN_INFO "REBOOT Notifier for CPUFREQ\n");
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block exynos4_cpufreq_reboot_notifier = {
+       .notifier_call = exynos4_cpufreq_reboot_notifier_call,
+};
+
 static struct cpufreq_driver exynos4_driver = {
        .flags          = CPUFREQ_STICKY,
        .verify         = exynos4_verify_speed,
@@ -391,6 +545,11 @@ static int __init exynos4_cpufreq_init(void)
                goto err_vdd_arm;
        }
 
+       register_pm_notifier(&exynos4_cpufreq_notifier);
+       register_reboot_notifier(&exynos4_cpufreq_reboot_notifier);
+
+       exynos4_cpufreq_init_done = true;
+
        tmp = __raw_readl(S5P_CLKDIV_CPU);
 
        for (i = L0; i <  CPUFREQ_LEVEL_END; i++) {
@@ -420,6 +579,9 @@ static int __init exynos4_cpufreq_init(void)
 
        return 0;
 err_cpufreq:
+       unregister_reboot_notifier(&exynos4_cpufreq_reboot_notifier);
+       unregister_pm_notifier(&exynos4_cpufreq_notifier);
+
        if (!IS_ERR(arm_regulator))
                regulator_put(arm_regulator);
 
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to