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#include <linux/cpu_cooling.h> 22#include <linux/cpu.h> 23 24#include "exynos-cpufreq.h" 25 26static struct exynos_dvfs_info *exynos_info; 27static struct thermal_cooling_device *cdev; 28static struct regulator *arm_regulator; 29static unsigned int locking_frequency; 30 31static int exynos_cpufreq_get_index(unsigned int freq) 32{ 33 struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; 34 struct cpufreq_frequency_table *pos; 35 36 cpufreq_for_each_entry(pos, freq_table) 37 if (pos->frequency == freq) 38 break; 39 40 if (pos->frequency == CPUFREQ_TABLE_END) 41 return -EINVAL; 42 43 return pos - freq_table; 44} 45 46static int exynos_cpufreq_scale(unsigned int target_freq) 47{ 48 struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; 49 unsigned int *volt_table = exynos_info->volt_table; 50 struct cpufreq_policy *policy = cpufreq_cpu_get(0); 51 unsigned int arm_volt, safe_arm_volt = 0; 52 unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz; 53 struct device *dev = exynos_info->dev; 54 unsigned int old_freq; 55 int index, old_index; 56 int ret = 0; 57 58 old_freq = policy->cur; 59 60 /* 61 * The policy max have been changed so that we cannot get proper 62 * old_index with cpufreq_frequency_table_target(). Thus, ignore 63 * policy and get the index from the raw frequency table. 64 */ 65 old_index = exynos_cpufreq_get_index(old_freq); 66 if (old_index < 0) { 67 ret = old_index; 68 goto out; 69 } 70 71 index = exynos_cpufreq_get_index(target_freq); 72 if (index < 0) { 73 ret = index; 74 goto out; 75 } 76 77 /* 78 * ARM clock source will be changed APLL to MPLL temporary 79 * To support this level, need to control regulator for 80 * required voltage level 81 */ 82 if (exynos_info->need_apll_change != NULL) { 83 if (exynos_info->need_apll_change(old_index, index) && 84 (freq_table[index].frequency < mpll_freq_khz) && 85 (freq_table[old_index].frequency < mpll_freq_khz)) 86 safe_arm_volt = volt_table[exynos_info->pll_safe_idx]; 87 } 88 arm_volt = volt_table[index]; 89 90 /* When the new frequency is higher than current frequency */ 91 if ((target_freq > old_freq) && !safe_arm_volt) { 92 /* Firstly, voltage up to increase frequency */ 93 ret = regulator_set_voltage(arm_regulator, arm_volt, arm_volt); 94 if (ret) { 95 dev_err(dev, "failed to set cpu voltage to %d\n", 96 arm_volt); 97 return ret; 98 } 99 } 100 101 if (safe_arm_volt) { 102 ret = regulator_set_voltage(arm_regulator, safe_arm_volt, 103 safe_arm_volt); 104 if (ret) { 105 dev_err(dev, "failed to set cpu voltage to %d\n", 106 safe_arm_volt); 107 return ret; 108 } 109 } 110 111 exynos_info->set_freq(old_index, index); 112 113 /* When the new frequency is lower than current frequency */ 114 if ((target_freq < old_freq) || 115 ((target_freq > old_freq) && safe_arm_volt)) { 116 /* down the voltage after frequency change */ 117 ret = regulator_set_voltage(arm_regulator, arm_volt, 118 arm_volt); 119 if (ret) { 120 dev_err(dev, "failed to set cpu voltage to %d\n", 121 arm_volt); 122 goto out; 123 } 124 } 125 126out: 127 cpufreq_cpu_put(policy); 128 129 return ret; 130} 131 132static int exynos_target(struct cpufreq_policy *policy, unsigned int index) 133{ 134 return exynos_cpufreq_scale(exynos_info->freq_table[index].frequency); 135} 136 137static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) 138{ 139 policy->clk = exynos_info->cpu_clk; 140 policy->suspend_freq = locking_frequency; 141 return cpufreq_generic_init(policy, exynos_info->freq_table, 100000); 142} 143 144static struct cpufreq_driver exynos_driver = { 145 .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, 146 .verify = cpufreq_generic_frequency_table_verify, 147 .target_index = exynos_target, 148 .get = cpufreq_generic_get, 149 .init = exynos_cpufreq_cpu_init, 150 .name = "exynos_cpufreq", 151 .attr = cpufreq_generic_attr, 152#ifdef CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW 153 .boost_supported = true, 154#endif 155#ifdef CONFIG_PM 156 .suspend = cpufreq_generic_suspend, 157#endif 158}; 159 160static int exynos_cpufreq_probe(struct platform_device *pdev) 161{ 162 struct device_node *cpu0; 163 int ret = -EINVAL; 164 165 exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL); 166 if (!exynos_info) 167 return -ENOMEM; 168 169 exynos_info->dev = &pdev->dev; 170 171 if (of_machine_is_compatible("samsung,exynos4210")) { 172 exynos_info->type = EXYNOS_SOC_4210; 173 ret = exynos4210_cpufreq_init(exynos_info); 174 } else if (of_machine_is_compatible("samsung,exynos4212")) { 175 exynos_info->type = EXYNOS_SOC_4212; 176 ret = exynos4x12_cpufreq_init(exynos_info); 177 } else if (of_machine_is_compatible("samsung,exynos4412")) { 178 exynos_info->type = EXYNOS_SOC_4412; 179 ret = exynos4x12_cpufreq_init(exynos_info); 180 } else if (of_machine_is_compatible("samsung,exynos5250")) { 181 exynos_info->type = EXYNOS_SOC_5250; 182 ret = exynos5250_cpufreq_init(exynos_info); 183 } else { 184 pr_err("%s: Unknown SoC type\n", __func__); 185 return -ENODEV; 186 } 187 188 if (ret) 189 goto err_vdd_arm; 190 191 if (exynos_info->set_freq == NULL) { 192 dev_err(&pdev->dev, "No set_freq function (ERR)\n"); 193 goto err_vdd_arm; 194 } 195 196 arm_regulator = regulator_get(NULL, "vdd_arm"); 197 if (IS_ERR(arm_regulator)) { 198 dev_err(&pdev->dev, "failed to get resource vdd_arm\n"); 199 goto err_vdd_arm; 200 } 201 202 /* Done here as we want to capture boot frequency */ 203 locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000; 204 205 ret = cpufreq_register_driver(&exynos_driver); 206 if (ret) 207 goto err_cpufreq_reg; 208 209 cpu0 = of_get_cpu_node(0, NULL); 210 if (!cpu0) { 211 pr_err("failed to find cpu0 node\n"); 212 return 0; 213 } 214 215 if (of_find_property(cpu0, "#cooling-cells", NULL)) { 216 cdev = of_cpufreq_cooling_register(cpu0, 217 cpu_present_mask); 218 if (IS_ERR(cdev)) 219 pr_err("running cpufreq without cooling device: %ld\n", 220 PTR_ERR(cdev)); 221 } 222 223 return 0; 224 225err_cpufreq_reg: 226 dev_err(&pdev->dev, "failed to register cpufreq driver\n"); 227 regulator_put(arm_regulator); 228err_vdd_arm: 229 kfree(exynos_info); 230 return -EINVAL; 231} 232 233static struct platform_driver exynos_cpufreq_platdrv = { 234 .driver = { 235 .name = "exynos-cpufreq", 236 }, 237 .probe = exynos_cpufreq_probe, 238}; 239module_platform_driver(exynos_cpufreq_platdrv); 240