| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | 
					
						
							|  |  |  |  *		http://www.samsung.com
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * EXYNOS - CPU frequency scaling support for EXYNOS series | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/err.h>
 | 
					
						
							|  |  |  | #include <linux/clk.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/regulator/consumer.h>
 | 
					
						
							|  |  |  | #include <linux/cpufreq.h>
 | 
					
						
							|  |  |  | #include <linux/suspend.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:39 +09:00
										 |  |  | #include <plat/cpu.h>
 | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-28 16:29:10 -08:00
										 |  |  | #include "exynos-cpufreq.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | static struct exynos_dvfs_info *exynos_info; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct regulator *arm_regulator; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned int locking_frequency; | 
					
						
							|  |  |  | static bool frequency_locked; | 
					
						
							|  |  |  | static DEFINE_MUTEX(cpufreq_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-22 00:19:25 +01:00
										 |  |  | static unsigned int exynos_getspeed(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	return clk_get_rate(exynos_info->cpu_clk) / 1000; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | static int exynos_cpufreq_get_index(unsigned int freq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; | 
					
						
							|  |  |  | 	int index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (index = 0; | 
					
						
							|  |  |  | 		freq_table[index].frequency != CPUFREQ_TABLE_END; index++) | 
					
						
							|  |  |  | 		if (freq_table[index].frequency == freq) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (freq_table[index].frequency == CPUFREQ_TABLE_END) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return index; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int exynos_cpufreq_scale(unsigned int target_freq) | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; | 
					
						
							|  |  |  | 	unsigned int *volt_table = exynos_info->volt_table; | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 	struct cpufreq_policy *policy = cpufreq_cpu_get(0); | 
					
						
							|  |  |  | 	unsigned int arm_volt, safe_arm_volt = 0; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz; | 
					
						
							| 
									
										
										
										
											2013-08-14 19:38:24 +05:30
										 |  |  | 	unsigned int old_freq; | 
					
						
							| 
									
										
										
										
											2013-01-25 10:18:09 -08:00
										 |  |  | 	int index, old_index; | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 19:38:24 +05:30
										 |  |  | 	old_freq = policy->cur; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 02:54:02 +00:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The policy max have been changed so that we cannot get proper | 
					
						
							|  |  |  | 	 * old_index with cpufreq_frequency_table_target(). Thus, ignore | 
					
						
							| 
									
										
										
										
											2013-09-26 16:50:21 +02:00
										 |  |  | 	 * policy and get the index from the raw frequency table. | 
					
						
							| 
									
										
										
										
											2012-07-20 02:54:02 +00:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-08-14 19:38:24 +05:30
										 |  |  | 	old_index = exynos_cpufreq_get_index(old_freq); | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 	if (old_index < 0) { | 
					
						
							|  |  |  | 		ret = old_index; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 	index = exynos_cpufreq_get_index(target_freq); | 
					
						
							|  |  |  | 	if (index < 0) { | 
					
						
							|  |  |  | 		ret = index; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * ARM clock source will be changed APLL to MPLL temporary | 
					
						
							|  |  |  | 	 * To support this level, need to control regulator for | 
					
						
							|  |  |  | 	 * required voltage level | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (exynos_info->need_apll_change != NULL) { | 
					
						
							|  |  |  | 		if (exynos_info->need_apll_change(old_index, index) && | 
					
						
							|  |  |  | 		   (freq_table[index].frequency < mpll_freq_khz) && | 
					
						
							|  |  |  | 		   (freq_table[old_index].frequency < mpll_freq_khz)) | 
					
						
							|  |  |  | 			safe_arm_volt = volt_table[exynos_info->pll_safe_idx]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	arm_volt = volt_table[index]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* When the new frequency is higher than current frequency */ | 
					
						
							| 
									
										
										
										
											2013-08-14 19:38:24 +05:30
										 |  |  | 	if ((target_freq > old_freq) && !safe_arm_volt) { | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 		/* Firstly, voltage up to increase frequency */ | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		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); | 
					
						
							| 
									
										
										
										
											2013-08-14 19:38:24 +05:30
										 |  |  | 			return ret; | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 	if (safe_arm_volt) { | 
					
						
							|  |  |  | 		ret = regulator_set_voltage(arm_regulator, safe_arm_volt, | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 				      safe_arm_volt); | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		if (ret) { | 
					
						
							|  |  |  | 			pr_err("%s: failed to set cpu voltage to %d\n", | 
					
						
							|  |  |  | 				__func__, safe_arm_volt); | 
					
						
							| 
									
										
										
										
											2013-08-14 19:38:24 +05:30
										 |  |  | 			return ret; | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:39 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	exynos_info->set_freq(old_index, index); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* When the new frequency is lower than current frequency */ | 
					
						
							| 
									
										
										
										
											2013-08-14 19:38:24 +05:30
										 |  |  | 	if ((target_freq < old_freq) || | 
					
						
							|  |  |  | 	   ((target_freq > old_freq) && safe_arm_volt)) { | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 		/* down the voltage after frequency change */ | 
					
						
							| 
									
										
										
										
											2013-10-09 20:43:37 +05:30
										 |  |  | 		ret = regulator_set_voltage(arm_regulator, arm_volt, | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 				arm_volt); | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		if (ret) { | 
					
						
							|  |  |  | 			pr_err("%s: failed to set cpu voltage to %d\n", | 
					
						
							|  |  |  | 				__func__, arm_volt); | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | out: | 
					
						
							|  |  |  | 	cpufreq_cpu_put(policy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-25 19:45:48 +05:30
										 |  |  | static int exynos_target(struct cpufreq_policy *policy, unsigned int index) | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; | 
					
						
							| 
									
										
										
										
											2013-01-31 17:13:39 -08:00
										 |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&cpufreq_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (frequency_locked) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-25 19:45:48 +05:30
										 |  |  | 	ret = exynos_cpufreq_scale(freq_table[index].frequency); | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&cpufreq_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | static int exynos_cpufreq_suspend(struct cpufreq_policy *policy) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (pm_event) { | 
					
						
							|  |  |  | 	case PM_SUSPEND_PREPARE: | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		mutex_lock(&cpufreq_lock); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 		frequency_locked = true; | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		mutex_unlock(&cpufreq_lock); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		ret = exynos_cpufreq_scale(locking_frequency); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return NOTIFY_BAD; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PM_POST_SUSPEND: | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		mutex_lock(&cpufreq_lock); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 		frequency_locked = false; | 
					
						
							| 
									
										
										
										
											2012-12-23 15:57:48 -08:00
										 |  |  | 		mutex_unlock(&cpufreq_lock); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 		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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-03 20:29:13 +05:30
										 |  |  | 	return cpufreq_generic_init(policy, exynos_info->freq_table, 100000); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct cpufreq_driver exynos_driver = { | 
					
						
							|  |  |  | 	.flags		= CPUFREQ_STICKY, | 
					
						
							| 
									
										
										
										
											2013-10-03 20:28:06 +05:30
										 |  |  | 	.verify		= cpufreq_generic_frequency_table_verify, | 
					
						
							| 
									
										
										
										
											2013-10-25 19:45:48 +05:30
										 |  |  | 	.target_index	= exynos_target, | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	.get		= exynos_getspeed, | 
					
						
							|  |  |  | 	.init		= exynos_cpufreq_cpu_init, | 
					
						
							| 
									
										
										
										
											2013-10-03 20:28:06 +05:30
										 |  |  | 	.exit		= cpufreq_generic_exit, | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	.name		= "exynos_cpufreq", | 
					
						
							| 
									
										
										
										
											2013-10-03 20:28:06 +05:30
										 |  |  | 	.attr		= cpufreq_generic_attr, | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | 	.suspend	= exynos_cpufreq_suspend, | 
					
						
							|  |  |  | 	.resume		= exynos_cpufreq_resume, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init exynos_cpufreq_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret = -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-06 22:53:06 +05:30
										 |  |  | 	exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	if (!exynos_info) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (soc_is_exynos4210()) | 
					
						
							|  |  |  | 		ret = exynos4210_cpufreq_init(exynos_info); | 
					
						
							| 
									
										
										
										
											2012-03-10 02:59:22 -08:00
										 |  |  | 	else if (soc_is_exynos4212() || soc_is_exynos4412()) | 
					
						
							|  |  |  | 		ret = exynos4x12_cpufreq_init(exynos_info); | 
					
						
							| 
									
										
										
										
											2012-03-10 03:00:02 -08:00
										 |  |  | 	else if (soc_is_exynos5250()) | 
					
						
							|  |  |  | 		ret = exynos5250_cpufreq_init(exynos_info); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2013-04-08 08:17:36 +00:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto err_vdd_arm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (exynos_info->set_freq == NULL) { | 
					
						
							|  |  |  | 		pr_err("%s: No set_freq function (ERR)\n", __func__); | 
					
						
							|  |  |  | 		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__); | 
					
						
							|  |  |  | 		goto err_vdd_arm; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-18 11:09:01 -08:00
										 |  |  | 	locking_frequency = exynos_getspeed(0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 15:51:40 -08:00
										 |  |  | 	regulator_put(arm_regulator); | 
					
						
							| 
									
										
										
										
											2012-01-07 20:18:35 +09:00
										 |  |  | err_vdd_arm: | 
					
						
							|  |  |  | 	kfree(exynos_info); | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | late_initcall(exynos_cpufreq_init); |