diff options
Diffstat (limited to 'drivers/cpufreq/exynos-cpufreq.c')
| -rw-r--r-- | drivers/cpufreq/exynos-cpufreq.c | 259 | 
1 files changed, 70 insertions, 189 deletions
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 0fac34439e3..1e0ec57bf6e 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -16,46 +16,28 @@  #include <linux/slab.h>  #include <linux/regulator/consumer.h>  #include <linux/cpufreq.h> -#include <linux/suspend.h> - -#include <plat/cpu.h> +#include <linux/platform_device.h> +#include <linux/of.h>  #include "exynos-cpufreq.h"  static struct exynos_dvfs_info *exynos_info; -  static struct regulator *arm_regulator; -static struct cpufreq_freqs freqs; -  static unsigned int locking_frequency; -static bool frequency_locked; -static DEFINE_MUTEX(cpufreq_lock); - -static int exynos_verify_speed(struct cpufreq_policy *policy) -{ -	return cpufreq_frequency_table_verify(policy, -					      exynos_info->freq_table); -} - -static unsigned int exynos_getspeed(unsigned int cpu) -{ -	return clk_get_rate(exynos_info->cpu_clk) / 1000; -}  static int exynos_cpufreq_get_index(unsigned int freq)  {  	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; -	int index; +	struct cpufreq_frequency_table *pos; -	for (index = 0; -		freq_table[index].frequency != CPUFREQ_TABLE_END; index++) -		if (freq_table[index].frequency == freq) +	cpufreq_for_each_entry(pos, freq_table) +		if (pos->frequency == freq)  			break; -	if (freq_table[index].frequency == CPUFREQ_TABLE_END) +	if (pos->frequency == CPUFREQ_TABLE_END)  		return -EINVAL; -	return index; +	return pos - freq_table;  }  static int exynos_cpufreq_scale(unsigned int target_freq) @@ -65,21 +47,19 @@ static int exynos_cpufreq_scale(unsigned int target_freq)  	struct cpufreq_policy *policy = cpufreq_cpu_get(0);  	unsigned int arm_volt, safe_arm_volt = 0;  	unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz; +	struct device *dev = exynos_info->dev; +	unsigned int old_freq;  	int index, old_index;  	int ret = 0; -	freqs.old = policy->cur; -	freqs.new = target_freq; - -	if (freqs.new == freqs.old) -		goto out; +	old_freq = policy->cur;  	/*  	 * The policy max have been changed so that we cannot get proper  	 * old_index with cpufreq_frequency_table_target(). Thus, ignore -	 * policy and get the index from the raw freqeuncy table. +	 * policy and get the index from the raw frequency table.  	 */ -	old_index = exynos_cpufreq_get_index(freqs.old); +	old_index = exynos_cpufreq_get_index(old_freq);  	if (old_index < 0) {  		ret = old_index;  		goto out; @@ -104,17 +84,14 @@ static int exynos_cpufreq_scale(unsigned int target_freq)  	}  	arm_volt = volt_table[index]; -	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); -  	/* When the new frequency is higher than current frequency */ -	if ((freqs.new > freqs.old) && !safe_arm_volt) { +	if ((target_freq > old_freq) && !safe_arm_volt) {  		/* Firstly, voltage up to increase frequency */  		ret = regulator_set_voltage(arm_regulator, arm_volt, arm_volt);  		if (ret) { -			pr_err("%s: failed to set cpu voltage to %d\n", -				__func__, arm_volt); -			freqs.new = freqs.old; -			goto post_notify; +			dev_err(dev, "failed to set cpu voltage to %d\n", +				arm_volt); +			return ret;  		}  	} @@ -122,170 +99,62 @@ static int exynos_cpufreq_scale(unsigned int target_freq)  		ret = regulator_set_voltage(arm_regulator, safe_arm_volt,  				      safe_arm_volt);  		if (ret) { -			pr_err("%s: failed to set cpu voltage to %d\n", -				__func__, safe_arm_volt); -			freqs.new = freqs.old; -			goto post_notify; +			dev_err(dev, "failed to set cpu voltage to %d\n", +				safe_arm_volt); +			return ret;  		}  	}  	exynos_info->set_freq(old_index, index); -post_notify: -	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - -	if (ret) -		goto out; -  	/* When the new frequency is lower than current frequency */ -	if ((freqs.new < freqs.old) || -	   ((freqs.new > freqs.old) && safe_arm_volt)) { +	if ((target_freq < old_freq) || +	   ((target_freq > old_freq) && safe_arm_volt)) {  		/* down the voltage after frequency change */ -		regulator_set_voltage(arm_regulator, arm_volt, +		ret = regulator_set_voltage(arm_regulator, arm_volt,  				arm_volt);  		if (ret) { -			pr_err("%s: failed to set cpu voltage to %d\n", -				__func__, arm_volt); +			dev_err(dev, "failed to set cpu voltage to %d\n", +				arm_volt);  			goto out;  		}  	}  out: -  	cpufreq_cpu_put(policy);  	return ret;  } -static int exynos_target(struct cpufreq_policy *policy, -			  unsigned int target_freq, -			  unsigned int relation) -{ -	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; -	unsigned int index; -	unsigned int new_freq; -	int ret = 0; - -	mutex_lock(&cpufreq_lock); - -	if (frequency_locked) -		goto out; - -	if (cpufreq_frequency_table_target(policy, freq_table, -					   target_freq, relation, &index)) { -		ret = -EINVAL; -		goto out; -	} - -	new_freq = freq_table[index].frequency; - -	ret = exynos_cpufreq_scale(new_freq); - -out: -	mutex_unlock(&cpufreq_lock); - -	return ret; -} - -#ifdef CONFIG_PM -static int exynos_cpufreq_suspend(struct cpufreq_policy *policy) +static int exynos_target(struct cpufreq_policy *policy, unsigned int index)  { -	return 0; +	return exynos_cpufreq_scale(exynos_info->freq_table[index].frequency);  } -static int exynos_cpufreq_resume(struct cpufreq_policy *policy) -{ -	return 0; -} -#endif - -/** - * exynos_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 exynos_cpufreq_pm_notifier(struct notifier_block *notifier, -				       unsigned long pm_event, void *v) -{ -	int ret; - -	switch (pm_event) { -	case PM_SUSPEND_PREPARE: -		mutex_lock(&cpufreq_lock); -		frequency_locked = true; -		mutex_unlock(&cpufreq_lock); - -		ret = exynos_cpufreq_scale(locking_frequency); -		if (ret < 0) -			return NOTIFY_BAD; - -		break; - -	case PM_POST_SUSPEND: -		mutex_lock(&cpufreq_lock); -		frequency_locked = false; -		mutex_unlock(&cpufreq_lock); -		break; -	} - -	return NOTIFY_OK; -} - -static struct notifier_block exynos_cpufreq_nb = { -	.notifier_call = exynos_cpufreq_pm_notifier, -}; -  static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)  { -	policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu); - -	cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); - -	/* set the transition latency value */ -	policy->cpuinfo.transition_latency = 100000; - -	cpumask_setall(policy->cpus); - -	return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); -} - -static int exynos_cpufreq_cpu_exit(struct cpufreq_policy *policy) -{ -	cpufreq_frequency_table_put_attr(policy->cpu); -	return 0; +	policy->clk = exynos_info->cpu_clk; +	policy->suspend_freq = locking_frequency; +	return cpufreq_generic_init(policy, exynos_info->freq_table, 100000);  } -static struct freq_attr *exynos_cpufreq_attr[] = { -	&cpufreq_freq_attr_scaling_available_freqs, -	NULL, -}; -  static struct cpufreq_driver exynos_driver = { -	.flags		= CPUFREQ_STICKY, -	.verify		= exynos_verify_speed, -	.target		= exynos_target, -	.get		= exynos_getspeed, +	.flags		= CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, +	.verify		= cpufreq_generic_frequency_table_verify, +	.target_index	= exynos_target, +	.get		= cpufreq_generic_get,  	.init		= exynos_cpufreq_cpu_init, -	.exit		= exynos_cpufreq_cpu_exit,  	.name		= "exynos_cpufreq", -	.attr		= exynos_cpufreq_attr, +	.attr		= cpufreq_generic_attr, +#ifdef CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW +	.boost_supported = true, +#endif  #ifdef CONFIG_PM -	.suspend	= exynos_cpufreq_suspend, -	.resume		= exynos_cpufreq_resume, +	.suspend	= cpufreq_generic_suspend,  #endif  }; -static int __init exynos_cpufreq_init(void) +static int exynos_cpufreq_probe(struct platform_device *pdev)  {  	int ret = -EINVAL; @@ -293,45 +162,57 @@ static int __init exynos_cpufreq_init(void)  	if (!exynos_info)  		return -ENOMEM; -	if (soc_is_exynos4210()) +	exynos_info->dev = &pdev->dev; + +	if (of_machine_is_compatible("samsung,exynos4210")) { +		exynos_info->type = EXYNOS_SOC_4210;  		ret = exynos4210_cpufreq_init(exynos_info); -	else if (soc_is_exynos4212() || soc_is_exynos4412()) +	} else if (of_machine_is_compatible("samsung,exynos4212")) { +		exynos_info->type = EXYNOS_SOC_4212;  		ret = exynos4x12_cpufreq_init(exynos_info); -	else if (soc_is_exynos5250()) +	} else if (of_machine_is_compatible("samsung,exynos4412")) { +		exynos_info->type = EXYNOS_SOC_4412; +		ret = exynos4x12_cpufreq_init(exynos_info); +	} else if (of_machine_is_compatible("samsung,exynos5250")) { +		exynos_info->type = EXYNOS_SOC_5250;  		ret = exynos5250_cpufreq_init(exynos_info); -	else -		return 0; +	} else { +		pr_err("%s: Unknown SoC type\n", __func__); +		return -ENODEV; +	}  	if (ret)  		goto err_vdd_arm;  	if (exynos_info->set_freq == NULL) { -		pr_err("%s: No set_freq function (ERR)\n", __func__); +		dev_err(&pdev->dev, "No set_freq function (ERR)\n");  		goto err_vdd_arm;  	}  	arm_regulator = regulator_get(NULL, "vdd_arm");  	if (IS_ERR(arm_regulator)) { -		pr_err("%s: failed to get resource vdd_arm\n", __func__); +		dev_err(&pdev->dev, "failed to get resource vdd_arm\n");  		goto err_vdd_arm;  	} -	locking_frequency = exynos_getspeed(0); +	/* Done here as we want to capture boot frequency */ +	locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000; -	register_pm_notifier(&exynos_cpufreq_nb); - -	if (cpufreq_register_driver(&exynos_driver)) { -		pr_err("%s: failed to register cpufreq driver\n", __func__); -		goto err_cpufreq; -	} - -	return 0; -err_cpufreq: -	unregister_pm_notifier(&exynos_cpufreq_nb); +	if (!cpufreq_register_driver(&exynos_driver)) +		return 0; +	dev_err(&pdev->dev, "failed to register cpufreq driver\n");  	regulator_put(arm_regulator);  err_vdd_arm:  	kfree(exynos_info);  	return -EINVAL;  } -late_initcall(exynos_cpufreq_init); + +static struct platform_driver exynos_cpufreq_platdrv = { +	.driver = { +		.name	= "exynos-cpufreq", +		.owner	= THIS_MODULE, +	}, +	.probe = exynos_cpufreq_probe, +}; +module_platform_driver(exynos_cpufreq_platdrv);  | 
