Blame view
drivers/cpufreq/s3c64xx-cpufreq.c
6.47 KB
be2de99be [CPUFREQ/S3C64xx]... |
1 |
/* |
b3748ddd8 [ARM] S3C64XX: In... |
2 3 4 5 6 7 8 9 |
* Copyright 2009 Wolfson Microelectronics plc * * S3C64xx CPUfreq Support * * 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. */ |
a6a434124 [CPUFREQ] s3c64xx... |
10 |
#define pr_fmt(fmt) "cpufreq: " fmt |
b3748ddd8 [ARM] S3C64XX: In... |
11 12 13 14 15 16 17 |
#include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> #include <linux/cpufreq.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/regulator/consumer.h> |
a6ee87790 cpufreq: Fix buil... |
18 |
#include <linux/module.h> |
b3748ddd8 [ARM] S3C64XX: In... |
19 20 21 |
static struct clk *armclk; static struct regulator *vddarm; |
43f1069ef ARM: S3C64XX: Sep... |
22 |
static unsigned long regulator_latency; |
b3748ddd8 [ARM] S3C64XX: In... |
23 24 25 26 27 28 29 30 |
#ifdef CONFIG_CPU_S3C6410 struct s3c64xx_dvfs { unsigned int vddarm_min; unsigned int vddarm_max; }; static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = { |
e9c08f0d5 ARM: S3C64XX: Red... |
31 32 33 34 |
[0] = { 1000000, 1150000 }, [1] = { 1050000, 1150000 }, [2] = { 1100000, 1150000 }, [3] = { 1200000, 1350000 }, |
c6e2d6855 [CPUFREQ] S3C6410... |
35 |
[4] = { 1300000, 1350000 }, |
b3748ddd8 [ARM] S3C64XX: In... |
36 37 38 39 |
}; static struct cpufreq_frequency_table s3c64xx_freq_table[] = { { 0, 66000 }, |
ef993ef8d [CPUFREQ] S3C6410... |
40 |
{ 0, 100000 }, |
b3748ddd8 [ARM] S3C64XX: In... |
41 |
{ 0, 133000 }, |
ef993ef8d [CPUFREQ] S3C6410... |
42 |
{ 1, 200000 }, |
b3748ddd8 [ARM] S3C64XX: In... |
43 44 45 46 |
{ 1, 222000 }, { 1, 266000 }, { 2, 333000 }, { 2, 400000 }, |
e9c08f0d5 ARM: S3C64XX: Red... |
47 48 49 |
{ 2, 532000 }, { 2, 533000 }, { 3, 667000 }, |
c6e2d6855 [CPUFREQ] S3C6410... |
50 |
{ 4, 800000 }, |
b3748ddd8 [ARM] S3C64XX: In... |
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
{ 0, CPUFREQ_TABLE_END }, }; #endif static int s3c64xx_cpufreq_verify_speed(struct cpufreq_policy *policy) { if (policy->cpu != 0) return -EINVAL; return cpufreq_frequency_table_verify(policy, s3c64xx_freq_table); } static unsigned int s3c64xx_cpufreq_get_speed(unsigned int cpu) { if (cpu != 0) return 0; return clk_get_rate(armclk) / 1000; } static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { int ret; unsigned int i; struct cpufreq_freqs freqs; struct s3c64xx_dvfs *dvfs; ret = cpufreq_frequency_table_target(policy, s3c64xx_freq_table, target_freq, relation, &i); if (ret != 0) return ret; freqs.cpu = 0; freqs.old = clk_get_rate(armclk) / 1000; freqs.new = s3c64xx_freq_table[i].frequency; freqs.flags = 0; dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index]; if (freqs.old == freqs.new) return 0; |
a6a434124 [CPUFREQ] s3c64xx... |
93 94 |
pr_debug("Transition %d-%dkHz ", freqs.old, freqs.new); |
b3748ddd8 [ARM] S3C64XX: In... |
95 96 97 98 99 100 101 102 103 |
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); #ifdef CONFIG_REGULATOR if (vddarm && freqs.new > freqs.old) { ret = regulator_set_voltage(vddarm, dvfs->vddarm_min, dvfs->vddarm_max); if (ret != 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
104 105 |
pr_err("Failed to set VDDARM for %dkHz: %d ", |
b3748ddd8 [ARM] S3C64XX: In... |
106 107 108 109 110 111 112 113 |
freqs.new, ret); goto err; } } #endif ret = clk_set_rate(armclk, freqs.new * 1000); if (ret < 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
114 115 |
pr_err("Failed to set rate %dkHz: %d ", |
b3748ddd8 [ARM] S3C64XX: In... |
116 117 118 |
freqs.new, ret); goto err; } |
fb3b1fefa [CPUFREQ] S3C64xx... |
119 |
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
b3748ddd8 [ARM] S3C64XX: In... |
120 121 122 123 124 125 |
#ifdef CONFIG_REGULATOR if (vddarm && freqs.new < freqs.old) { ret = regulator_set_voltage(vddarm, dvfs->vddarm_min, dvfs->vddarm_max); if (ret != 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
126 127 |
pr_err("Failed to set VDDARM for %dkHz: %d ", |
b3748ddd8 [ARM] S3C64XX: In... |
128 129 130 131 132 |
freqs.new, ret); goto err_clk; } } #endif |
a6a434124 [CPUFREQ] s3c64xx... |
133 134 |
pr_debug("Set actual frequency %lukHz ", |
b3748ddd8 [ARM] S3C64XX: In... |
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
clk_get_rate(armclk) / 1000); return 0; err_clk: if (clk_set_rate(armclk, freqs.old * 1000) < 0) pr_err("Failed to restore original clock rate "); err: cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); return ret; } #ifdef CONFIG_REGULATOR |
43f1069ef ARM: S3C64XX: Sep... |
150 |
static void __init s3c64xx_cpufreq_config_regulator(void) |
b3748ddd8 [ARM] S3C64XX: In... |
151 152 153 154 155 156 157 |
{ int count, v, i, found; struct cpufreq_frequency_table *freq; struct s3c64xx_dvfs *dvfs; count = regulator_count_voltages(vddarm); if (count < 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
158 159 |
pr_err("Unable to check supported voltages "); |
b3748ddd8 [ARM] S3C64XX: In... |
160 161 162 |
} freq = s3c64xx_freq_table; |
43f1069ef ARM: S3C64XX: Sep... |
163 |
while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) { |
b3748ddd8 [ARM] S3C64XX: In... |
164 165 166 167 168 169 170 171 172 173 174 175 176 |
if (freq->frequency == CPUFREQ_ENTRY_INVALID) continue; dvfs = &s3c64xx_dvfs_table[freq->index]; found = 0; for (i = 0; i < count; i++) { v = regulator_list_voltage(vddarm, i); if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max) found = 1; } if (!found) { |
a6a434124 [CPUFREQ] s3c64xx... |
177 178 |
pr_debug("%dkHz unsupported by regulator ", |
b3748ddd8 [ARM] S3C64XX: In... |
179 180 181 182 183 184 |
freq->frequency); freq->frequency = CPUFREQ_ENTRY_INVALID; } freq++; } |
43f1069ef ARM: S3C64XX: Sep... |
185 186 187 188 |
/* Guess based on having to do an I2C/SPI write; in future we * will be able to query the regulator performance here. */ regulator_latency = 1 * 1000 * 1000; |
b3748ddd8 [ARM] S3C64XX: In... |
189 190 |
} #endif |
6d0de1577 ARM: S3C64XX: Fix... |
191 |
static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) |
b3748ddd8 [ARM] S3C64XX: In... |
192 193 194 195 196 197 198 199 |
{ int ret; struct cpufreq_frequency_table *freq; if (policy->cpu != 0) return -EINVAL; if (s3c64xx_freq_table == NULL) { |
a6a434124 [CPUFREQ] s3c64xx... |
200 201 |
pr_err("No frequency information for this CPU "); |
b3748ddd8 [ARM] S3C64XX: In... |
202 203 204 205 206 |
return -ENODEV; } armclk = clk_get(NULL, "armclk"); if (IS_ERR(armclk)) { |
a6a434124 [CPUFREQ] s3c64xx... |
207 208 |
pr_err("Unable to obtain ARMCLK: %ld ", |
b3748ddd8 [ARM] S3C64XX: In... |
209 210 211 212 213 214 215 216 |
PTR_ERR(armclk)); return PTR_ERR(armclk); } #ifdef CONFIG_REGULATOR vddarm = regulator_get(NULL, "vddarm"); if (IS_ERR(vddarm)) { ret = PTR_ERR(vddarm); |
a6a434124 [CPUFREQ] s3c64xx... |
217 218 219 220 |
pr_err("Failed to obtain VDDARM: %d ", ret); pr_err("Only frequency scaling available "); |
b3748ddd8 [ARM] S3C64XX: In... |
221 222 |
vddarm = NULL; } else { |
43f1069ef ARM: S3C64XX: Sep... |
223 |
s3c64xx_cpufreq_config_regulator(); |
b3748ddd8 [ARM] S3C64XX: In... |
224 |
} |
a6a434124 [CPUFREQ] s3c64xx... |
225 226 227 228 229 230 231 232 |
vddint = regulator_get(NULL, "vddint"); if (IS_ERR(vddint)) { ret = PTR_ERR(vddint); pr_err("Failed to obtain VDDINT: %d ", ret); vddint = NULL; } |
b3748ddd8 [ARM] S3C64XX: In... |
233 234 235 236 237 238 239 240 241 |
#endif freq = s3c64xx_freq_table; while (freq->frequency != CPUFREQ_TABLE_END) { unsigned long r; /* Check for frequencies we can generate */ r = clk_round_rate(armclk, freq->frequency * 1000); r /= 1000; |
383af9c25 ARM: S3C64XX: Pro... |
242 |
if (r != freq->frequency) { |
a6a434124 [CPUFREQ] s3c64xx... |
243 244 |
pr_debug("%dkHz unsupported by clock ", |
383af9c25 ARM: S3C64XX: Pro... |
245 |
freq->frequency); |
b3748ddd8 [ARM] S3C64XX: In... |
246 |
freq->frequency = CPUFREQ_ENTRY_INVALID; |
383af9c25 ARM: S3C64XX: Pro... |
247 |
} |
b3748ddd8 [ARM] S3C64XX: In... |
248 249 250 251 252 253 254 255 256 257 |
/* If we have no regulator then assume startup * frequency is the maximum we can support. */ if (!vddarm && freq->frequency > s3c64xx_cpufreq_get_speed(0)) freq->frequency = CPUFREQ_ENTRY_INVALID; freq++; } policy->cur = clk_get_rate(armclk) / 1000; |
43f1069ef ARM: S3C64XX: Sep... |
258 259 260 261 262 |
/* Datasheet says PLL stabalisation time (if we were to use * the PLLs, which we don't currently) is ~300us worst case, * but add some fudge. */ policy->cpuinfo.transition_latency = (500 * 1000) + regulator_latency; |
b3748ddd8 [ARM] S3C64XX: In... |
263 264 265 |
ret = cpufreq_frequency_table_cpuinfo(policy, s3c64xx_freq_table); if (ret != 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
266 267 |
pr_err("Failed to configure frequency table: %d ", |
b3748ddd8 [ARM] S3C64XX: In... |
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
ret); regulator_put(vddarm); clk_put(armclk); } return ret; } static struct cpufreq_driver s3c64xx_cpufreq_driver = { .owner = THIS_MODULE, .flags = 0, .verify = s3c64xx_cpufreq_verify_speed, .target = s3c64xx_cpufreq_set_target, .get = s3c64xx_cpufreq_get_speed, .init = s3c64xx_cpufreq_driver_init, .name = "s3c", }; static int __init s3c64xx_cpufreq_init(void) { return cpufreq_register_driver(&s3c64xx_cpufreq_driver); } module_init(s3c64xx_cpufreq_init); |