linux/drivers/cpufreq/arm_big_little.c
<<
tion14tion14>>tition ="+search" method="post" onsubmit="return do_search(this);">tion14 ">tion14tion14Searchtion14Prefs3 14ti ="ajax+*" method="post" onsubmit="return false;">ti ">ton14 43
4 41/*4 42 * ARM big.LITTLE Platforms CPUFreq support4 43 *4 44 * Copyright (C) 2013 ARM Ltd.4 45 * Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>4 46 *4 47 * Copyright (C) 2013 Linaro.4 48 * Viresh Kumar <viresh.kumar@linaro.org>4 49 *4 2" a> * This program is free software; you ca redistribute it and/or modify4 11 * it under the terms of the GNU General Public License verstion2 as4 12 * published by the Free Software Founda> .4 13 *4 14 * This program is distributed "as is" WITHOUT ANY WARRANTY of any4 15 * kind, whether express or implied; without even the implied warranty4 16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the4 17 * GNU General Public License for more details.4 18 */4 1934 2" a>#define4pr_fmt a>(fmt a>)4KBUILD_MODNAME a> ": "fmt a>34 21 a>34 22#include <linux/clk.h>34 23#include <linux/cpu.h>34 24#include <linux/cpufreq.h>34 25#include <linux/cpumask.h>34 26#include <linux/export.h>34 27#include <linux/of_platform.h>34 28#include <linux/opp.h>34 29#include <linux/slab.h>34 30#include <linux/topology.h>34 31#include <linux/types.h>34 32 a>34 33#include "arm_big_little.h"34 34 a>34 35/* Currently we support only two clusters */4 36 a>#define4MAX_CLUSTERS a> 4 4234 37 a>34 38static struct4cpufreq_arm_bL_ops a> *arm_bL_ops a>;34 39static struct4clk a> *clk a>[MAX_CLUSTERS a>];34 40static struct4cpufreq_frequency_table a> *freq_table a>[MAX_CLUSTERS a>];34 41static atomic_t a> cluster_usage a>[MAX_CLUSTERS a>] = {ATOMIC_INIT a>(0), ATOMIC_INIT a>(0)};34 42 a>34 43static unsigned int4bL_cpufreq_get a>(unsigned int4cpu a>)34 44 a>{34 45 a> 4 414 4u32 a>4cur_cluster a>4=4cpu_to_cluster a>(cpu a>);34 46 a>34 47 a> 4 414 4return clk_get_rate a>(clk a>[cur_cluster a>]) / 1000;34 48}34 4934 5" a>/* Validate policy frequency range */4 51static int4bL_cpufreq_verify_policy a>(struct4cpufreq_policy a> *policy a>)34 52 a>{34 53 a> 4 414 4u32 a>4cur_cluster a>4=4cpu_to_cluster a>(policy a>->cpu a>);34 54 a>34 55 a> 4 414 4return cpufreq_frequency_table_verify a>(policy a>, freq_table a>[cur_cluster a>]);34 56}34 57 a>34 58/* Set clock frequency */4 59static int4bL_cpufreq_set_target a>(struct4cpufreq_policy a> *policy a>,34 60 a> 4 414 444444444unsigned int4target_freq a>, unsigned int4rela> a>)34 61 a>{34 62 a> 4 414 4struct4cpufreq_freqs a>4freqs a>;34 63 a> 4 414 4u32 a>4cpu a>4=4policy a>->cpu a>, freq_tab_idx a>, cur_cluster a>;34 64 a> 4 414 4int4ret a>4=40;34 65 a>34 66 a> 4 414 4cur_cluster a>4=4cpu_to_cluster a>(policy a>->cpu a>);34 67 a>34 68 a> 4 414 4freqs a>.old a>4=4bL_cpufreq_get a>(policy a>->cpu a>);34 6934 70 a> 4 414 4/* Determine4valid target frequency using freq_table */4 71 a> 4 414 4cpufreq_frequency_table_target a>(policy a>, freq_table a>[cur_cluster a>],34 72 a> 4 414 44444444444444444target_freq a>, rela> a>, &freq_tab_idx a>);34 73 a> 4 414 4freqs a>.new a>4=4freq_table a>[cur_cluster a>][freq_tab_idx a>].frequency a>;34 74 a>34 75 a> 4 414 4pr_debug a>("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n"4 76 a> 4 414 44444444444444444__func__ a>, cpu a>, cur_cluster a>, freqs a>.old a>, target_freq a>,34 77 a> 4 414 44444444444444444freqs a>.new a>);34 78 a>34 79 a> 4 414 4if (freqs a>.old a>4==4freqs a>.new a>)34 80 a> 4 414 444444444return 0;34 81 a>34 82 a> 4 414 4cpufreq_notify_transi> a>(policy a>, &freqs a>, CPUFREQ_PRECHANGE a>);34 83 a>34 84 a> 4 414 4ret a>4=4clk_set_rate a>(clk a>[cur_cluster a>], freqs a>.new a>4* 1000);34 85 a> 4 414 4if (ret a>) {34 86 a> 4 414 444444444pr_err a>("clk_set_rate failed: %d\n"ret a>);34 87 a> 4 414 444444444freqs a>.new a>4=4freqs a>.old a>;34 88 a> 4 414 4}34 8934 90 a> 4 414 4cpufreq_notify_transi> a>(policy a>, &freqs a>, CPUFREQ_POSTCHANGE a>);34 91 a>34 92 a> 4 414 4return ret a>;34 93}34 94 a>34 95static void4put_cluster_clk_and_freq_table a>(struct4device a> *cpu_dev a>)34 96 a>{34 97 a> 4 414 4u32 a>4cluster a>4=4cpu_to_cluster a>(cpu_dev a>->id a>);34 98 a>34 99 a> 4 414 4if (!atomic_dec_return a>(&cluster_usage a>[cluster a>])) {34100 a> 4 414 444444444clk_put a>(clk a>[cluster a>]);34101 a> 4 414 444444444opp_free_cpufreq_table a>(cpu_dev a>, &freq_table a>[cluster a>]);34102 a> 4 414 444444444dev_dbg a>(cpu_dev a>, "%s: cluster: %d\n"__func__ a>, cluster a>);34103 a> 4 414 4}34104}34105 a>34106static int4get_cluster_clk_and_freq_table a>(struct4device a> *cpu_dev a>)34107 a>{34108 a> 4 414 4u32 a>4cluster a>4=4cpu_to_cluster a>(cpu_dev a>->id a>);34109 a> 4 414 4char4nam a>[14] = "cpu-cluster."4110 a> 4 414 4int4ret a>;34111 a>34112 a> 4 414 4if (atomic_inc_return a>(&cluster_usage a>[cluster a>]) != 1)34113 a> 4 414 444444444return 0;34114 a>34115 a> 4 414 4ret a>4=4arm_bL_ops a>->init_opp_table a>(cpu_dev a>);34116 a> 4 414 4if (ret a>) {34117 a> 4 414 444444444dev_err a>(cpu_dev a>, "%s: init_opp_table failed, cpu: %d, err: %d\n"4118 a> 4 414 444444444 4 414 444444444__func__ a>, cpu_dev a>->id a>,4ret a>);34119 a> 4 414 444444444goto atomic_dec a>;34120 a> 4 414 4}34121 a>34122 a> 4 414 4ret a>4=4opp_init_cpufreq_table a>(cpu_dev a>, &freq_table a>[cluster a>]);34123 a> 4 414 4if (ret a>) {34124 a> 4 414 444444444dev_err a>(cpu_dev a>, "%s: failed to init cpufreq table, cpu: %d, err: %d\n"4125 a> 4 414 444444444 4 414 444444444__func__ a>, cpu_dev a>->id a>,4ret a>);34126 a> 4 414 444444444goto atomic_dec a>;34127 a> 4 414 4}34128 a>34129 a> 4 414 4nam a>[12]4=4cluster a>4+ '0'4130 a> 4 414 4clk a>[cluster a>]4=4clk_get_sys a>(nam a>,4NULL a>);34131 a> 4 414 4if (!IS_ERR a>(clk a>[cluster a>])) {34132 a> 4 414 444444444dev_dbg a>(cpu_dev a>, "%s: clk: %p & freq table: %p, cluster: %d\n"4133 a> 4 414 444444444 4 414 444444444__func__ a>, clk a>[cluster a>], freq_table a>[cluster a>],34134 a> 4 414 444444444 4 414 444444444cluster a>);34135 a> 4 414 444444444return 0;34136 a> 4 414 4}34137 a>34138 a> 4 414 4dev_err a>(cpu_dev a>, "%s: Failed to get clk for cpu: %d, cluster: %d\n"4139 a> 4 414 44444444444444444__func__ a>, cpu_dev a>->id a>,4cluster a>);34140 a> 4 414 4ret a>4=4PTR_ERR a>(clk a>[cluster a>]);34141 a> 4 414 4opp_free_cpufreq_table a>(cpu_dev a>, &freq_table a>[cluster a>]);34142 a>34143atomic_dec a>:34144 a> 4 414 4atomic_dec a>(&cluster_usage a>[cluster a>]);34145 a> 4 414 4dev_err a>(cpu_dev a>, "%s: Failed to get data for cluster: %d\n"__func__ a>,34146 a> 4 414 44444444444444444cluster a>);34147 a> 4 414 4return ret a>;34148}341493415" a>/* Per-CPU initializa> */4151static int4bL_cpufreq_init a>(struct4cpufreq_policy a> *policy a>)34152 a>{34153 a> 4 414 4u32 a>4cur_cluster a>4=4cpu_to_cluster a>(policy a>->cpu a>);34154 a> 4 414 4struct4device a> *cpu_dev a>;34155 a> 4 414 4int4ret a>;34156 a>34157 a> 4 414 4cpu_dev a>4=4get_cpu_device a>(policy a>->cpu a>);34158 a> 4 414 4if (!cpu_dev a>) {34159 a> 4 414 444444444pr_err a>("%s: failed to get cpu%d device\n"__func__ a>,34160 a> 4 414 444444444 4 414 444444444policy a>->cpu a>);34161 a> 4 414 444444444return -ENODEV a>;34162 a> 4 414 4}34163 a>34164 a> 4 414 4ret a>4=4get_cluster_clk_and_freq_table a>(cpu_dev a>);34165 a> 4 414 4if (ret a>)34166 a> 4 414 444444444return ret a>;34167 a>34168 a> 4 414 4ret a>4=4cpufreq_frequency_table_cpuinfo a>(policy a>, freq_table a>[cur_cluster a>]);34169 a> 4 414 4if (ret a>) {34170 a> 4 414 444444444dev_err a>(cpu_dev a>, "CPU %d, cluster: %d4invalid freq table\n"4171 a> 4 414 444444444 4 414 444444444policy a>->cpu a>, cur_cluster a>);34172 a> 4 414 444444444put_cluster_clk_and_freq_table a>(cpu_dev a>);34173 a> 4 414 444444444return ret a>;34174 a> 4 414 4}34175 a>34176 a> 4 414 4cpufreq_frequency_table_get_attr a>(freq_table a>[cur_cluster a>], policy a>->cpu a>);34177 a>34178 a> 4 414 4if (arm_bL_ops a>->get_transi> _latency a>)34179 a> 4 414 444444444policy a>->cpuinfo a>.transi> _latency a> =34180 a> 4 414 44444444444444444arm_bL_ops a>->get_transi> _latency a>(cpu_dev a>);34181 a> 4 414 4else34182 a> 4 414 444444444policy a>->cpuinfo a>.transi> _latency a> = CPUFREQ_ETERNAL a>;34183 a>34184 a> 4 414 4policy a>->cur a> = bL_cpufreq_get a>(policy a>->cpu a>);34185 a>34186 a> 4 414 4cpumask_copy a>(policy a>->cpus a>, topology_core_cpumask a>(policy a>->cpu a>));34187 a>34188 a> 4 414 4dev_info a>(cpu_dev a>, "%s: CPU %d initialized\n"__func__ a>, policy a>->cpu a>);34189 a> 4 414 4return 0;34190 a>}34191 a>34192 a>static int4bL_cpufreq_exit a>(struct4cpufreq_policy a> *policy a>)34193{34194 a> 4 414 4struct4device a> *cpu_dev a>;34195 a>34196 a> 4 414 4cpu_dev a>4=4get_cpu_device a>(policy a>->cpu a>);34197 a> 4 414 4if (!cpu_dev a>) {34198 a> 4 414 444444444pr_err a>("%s: failed to get cpu%d device\n"__func__ a>,34199 a> 4 414 4444444444444444444444444policy a>->cpu a>);34200 a> 4 414 444444444return -ENODEV a>;34201 a> 4 414 4}34202 a>34203 a> 4 414 4put_cluster_clk_and_freq_table a>(cpu_dev a>);34204 a> 4 414 4dev_dbg a>(cpu_dev a>, "%s: Exited, cpu: %d\n"__func__ a>, policy a>->cpu a>);34205 a>34206 a> 4 414 4return 0;34207 a>}34208 a>34209 a>/* Export freq_table to sysfs */4210 a>static struct4freq_attr a> *bL_cpufreq_attr a>[]4=4{34211 a> 4 414 4&cpufreq_freq_attr_scaling_available_freqs a>,34212 a> 4 414 4NULL a>,34213 a>};34214 a>34215 a>static struct4cpufreq_driver a> bL_cpufreq_driver a>4=4{34216 a> 4 414 4.nam a>4444444444444444444= "arm-big-little"4217 a> 4 414 4.flags a>444444444444444444= CPUFREQ_STICKY a>,34218 a> 4 414 4.verify a>44444444444444444= bL_cpufreq_verify_policy a>,34219 a> 4 414 4.target a>44444444444444444= bL_cpufreq_set_target a>,34220 a> 4 414 4.get a>44444444444444444444= bL_cpufreq_get a>,34221 a> 4 414 4.init a>4444444444444444444= bL_cpufreq_init a>,34222 a> 4 414 4.exit a>4444444444444444444= bL_cpufreq_exit a>,34223 a> 4 414 4.have_governor_per_policy a>4=4tru a>,34224 a> 4 414 4.attr a> 444444444444444444= bL_cpufreq_attr a>,34225 a>};34226 a>34227 a>int4bL_cpufreq_register a>(struct4cpufreq_arm_bL_ops a> *ops a>)34228 a>{34229 a> 4 414 4int4ret a>;34230 a>34231 a> 4 414 4if (arm_bL_ops a>) {34232 a> 4 414 444444444pr_debug a>("%s: Already registered: %s, exiting\n"__func__ a>,34233 a> 4 414 444444444 4 414 444444444arm_bL_ops a>->nam a>);34234 a> 4 414 444444444return -EBUSY a>;34235 a> 4 414 4}34236 a>34237 a> 4 414 4if (!ops a> || !strlen a>(ops a>->nam a>) || !ops a>->init_opp_table a>) {34238 a> 4 414 444444444pr_err a>("%s: Invalid arm_bL_ops, exiting\n"__func__ a>);34239 a> 4 414 444444444return -ENODEV a>;34240 a> 4 414 4}34241 a>34242 a> 4 414 4arm_bL_ops a>4=4ops a>;34243 a>34244 a> 4 414 4ret a>4=4cpufreq_register_driver a>(&bL_cpufreq_driver a>);34245 a> 4 414 4if (ret a>) {34246 a> 4 414 444444444pr_info a>("%s: Failed registering platform driver: %s, err: %d\n"4247 a> 4 414 444444444 4 414 444444444__func__ a>, ops a>->nam a>,4ret a>);34248 a> 4 414 444444444arm_bL_ops a>4=4NULL a>;34249 a> 4 414 4}4else {34250 a> 4 414 444444444pr_info a>("%s: Registered platform driver: %s\n"__func__ a>,34251 a> 4 414 444444444 4 414 444444444ops a>->nam a>);34252 a> 4 414 4}34253 a>34254 a> 4 414 4return ret a>;34255 a>}34256 a>EXPORT_SYMBOL_GPL a>(bL_cpufreq_register a>);34257 a>34258 a>void4bL_cpufreq_unregister a>(struct4cpufreq_arm_bL_ops a> *ops a>)34259 a>{34260 a> 4 414 4if (arm_bL_ops a> != ops a>) {34261 a> 4 414 444444444pr_err a>("%s: Registered with: %s, can't unregister, exiting\n"4262 a> 4 414 444444444 4 414 444444444__func__ a>, arm_bL_ops a>->nam a>);34263 a> 4 414 444444444return;34264 a> 4 414 4}34265 a>34266 a> 4 414 4cpufreq_unregister_driver a>(&bL_cpufreq_driver a>);34267 a> 4 414 4pr_info a>("%s: Un-registered platform driver: %s\n"__func__ a>,34268 a> 4 414 444444444 4 414 4arm_bL_ops a>->nam a>);34269 a> 4 414 4arm_bL_ops a>4=4NULL a>;34270 a>}34271 a>EXPORT_SYMBOL_GPL a>(bL_cpufreq_unregister a>);34272 a>
lxr.linux.no kindly hosted by Redpill Linpro AS a>, provider of Linux consulting and opera> s services since41995.