Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.19 217 lines 5.9 kB view raw
1/* 2 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 3 * http://www.samsung.com 4 * 5 * EXYNOS - CPU frequency scaling support for EXYNOS series 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10*/ 11 12#include <linux/kernel.h> 13#include <linux/err.h> 14#include <linux/clk.h> 15#include <linux/io.h> 16#include <linux/slab.h> 17#include <linux/regulator/consumer.h> 18#include <linux/cpufreq.h> 19#include <linux/platform_device.h> 20#include <linux/of.h> 21 22#include "exynos-cpufreq.h" 23 24static struct exynos_dvfs_info *exynos_info; 25static struct regulator *arm_regulator; 26static unsigned int locking_frequency; 27 28static int exynos_cpufreq_get_index(unsigned int freq) 29{ 30 struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; 31 struct cpufreq_frequency_table *pos; 32 33 cpufreq_for_each_entry(pos, freq_table) 34 if (pos->frequency == freq) 35 break; 36 37 if (pos->frequency == CPUFREQ_TABLE_END) 38 return -EINVAL; 39 40 return pos - freq_table; 41} 42 43static int exynos_cpufreq_scale(unsigned int target_freq) 44{ 45 struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; 46 unsigned int *volt_table = exynos_info->volt_table; 47 struct cpufreq_policy *policy = cpufreq_cpu_get(0); 48 unsigned int arm_volt, safe_arm_volt = 0; 49 unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz; 50 struct device *dev = exynos_info->dev; 51 unsigned int old_freq; 52 int index, old_index; 53 int ret = 0; 54 55 old_freq = policy->cur; 56 57 /* 58 * The policy max have been changed so that we cannot get proper 59 * old_index with cpufreq_frequency_table_target(). Thus, ignore 60 * policy and get the index from the raw frequency table. 61 */ 62 old_index = exynos_cpufreq_get_index(old_freq); 63 if (old_index < 0) { 64 ret = old_index; 65 goto out; 66 } 67 68 index = exynos_cpufreq_get_index(target_freq); 69 if (index < 0) { 70 ret = index; 71 goto out; 72 } 73 74 /* 75 * ARM clock source will be changed APLL to MPLL temporary 76 * To support this level, need to control regulator for 77 * required voltage level 78 */ 79 if (exynos_info->need_apll_change != NULL) { 80 if (exynos_info->need_apll_change(old_index, index) && 81 (freq_table[index].frequency < mpll_freq_khz) && 82 (freq_table[old_index].frequency < mpll_freq_khz)) 83 safe_arm_volt = volt_table[exynos_info->pll_safe_idx]; 84 } 85 arm_volt = volt_table[index]; 86 87 /* When the new frequency is higher than current frequency */ 88 if ((target_freq > old_freq) && !safe_arm_volt) { 89 /* Firstly, voltage up to increase frequency */ 90 ret = regulator_set_voltage(arm_regulator, arm_volt, arm_volt); 91 if (ret) { 92 dev_err(dev, "failed to set cpu voltage to %d\n", 93 arm_volt); 94 return ret; 95 } 96 } 97 98 if (safe_arm_volt) { 99 ret = regulator_set_voltage(arm_regulator, safe_arm_volt, 100 safe_arm_volt); 101 if (ret) { 102 dev_err(dev, "failed to set cpu voltage to %d\n", 103 safe_arm_volt); 104 return ret; 105 } 106 } 107 108 exynos_info->set_freq(old_index, index); 109 110 /* When the new frequency is lower than current frequency */ 111 if ((target_freq < old_freq) || 112 ((target_freq > old_freq) && safe_arm_volt)) { 113 /* down the voltage after frequency change */ 114 ret = regulator_set_voltage(arm_regulator, arm_volt, 115 arm_volt); 116 if (ret) { 117 dev_err(dev, "failed to set cpu voltage to %d\n", 118 arm_volt); 119 goto out; 120 } 121 } 122 123out: 124 cpufreq_cpu_put(policy); 125 126 return ret; 127} 128 129static int exynos_target(struct cpufreq_policy *policy, unsigned int index) 130{ 131 return exynos_cpufreq_scale(exynos_info->freq_table[index].frequency); 132} 133 134static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) 135{ 136 policy->clk = exynos_info->cpu_clk; 137 policy->suspend_freq = locking_frequency; 138 return cpufreq_generic_init(policy, exynos_info->freq_table, 100000); 139} 140 141static struct cpufreq_driver exynos_driver = { 142 .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, 143 .verify = cpufreq_generic_frequency_table_verify, 144 .target_index = exynos_target, 145 .get = cpufreq_generic_get, 146 .init = exynos_cpufreq_cpu_init, 147 .name = "exynos_cpufreq", 148 .attr = cpufreq_generic_attr, 149#ifdef CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW 150 .boost_supported = true, 151#endif 152#ifdef CONFIG_PM 153 .suspend = cpufreq_generic_suspend, 154#endif 155}; 156 157static int exynos_cpufreq_probe(struct platform_device *pdev) 158{ 159 int ret = -EINVAL; 160 161 exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL); 162 if (!exynos_info) 163 return -ENOMEM; 164 165 exynos_info->dev = &pdev->dev; 166 167 if (of_machine_is_compatible("samsung,exynos4210")) { 168 exynos_info->type = EXYNOS_SOC_4210; 169 ret = exynos4210_cpufreq_init(exynos_info); 170 } else if (of_machine_is_compatible("samsung,exynos4212")) { 171 exynos_info->type = EXYNOS_SOC_4212; 172 ret = exynos4x12_cpufreq_init(exynos_info); 173 } else if (of_machine_is_compatible("samsung,exynos4412")) { 174 exynos_info->type = EXYNOS_SOC_4412; 175 ret = exynos4x12_cpufreq_init(exynos_info); 176 } else if (of_machine_is_compatible("samsung,exynos5250")) { 177 exynos_info->type = EXYNOS_SOC_5250; 178 ret = exynos5250_cpufreq_init(exynos_info); 179 } else { 180 pr_err("%s: Unknown SoC type\n", __func__); 181 return -ENODEV; 182 } 183 184 if (ret) 185 goto err_vdd_arm; 186 187 if (exynos_info->set_freq == NULL) { 188 dev_err(&pdev->dev, "No set_freq function (ERR)\n"); 189 goto err_vdd_arm; 190 } 191 192 arm_regulator = regulator_get(NULL, "vdd_arm"); 193 if (IS_ERR(arm_regulator)) { 194 dev_err(&pdev->dev, "failed to get resource vdd_arm\n"); 195 goto err_vdd_arm; 196 } 197 198 /* Done here as we want to capture boot frequency */ 199 locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000; 200 201 if (!cpufreq_register_driver(&exynos_driver)) 202 return 0; 203 204 dev_err(&pdev->dev, "failed to register cpufreq driver\n"); 205 regulator_put(arm_regulator); 206err_vdd_arm: 207 kfree(exynos_info); 208 return -EINVAL; 209} 210 211static struct platform_driver exynos_cpufreq_platdrv = { 212 .driver = { 213 .name = "exynos-cpufreq", 214 }, 215 .probe = exynos_cpufreq_probe, 216}; 217module_platform_driver(exynos_cpufreq_platdrv);