1
/* linux/arch/arm/plat-s3c64xx/cpufreq.c
3
* Copyright 2009 Wolfson Microelectronics plc
5
* S3C64xx CPUfreq Support
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.
12
#include <linux/kernel.h>
13
#include <linux/types.h>
14
#include <linux/init.h>
15
#include <linux/cpufreq.h>
16
#include <linux/clk.h>
17
#include <linux/err.h>
18
#include <linux/regulator/consumer.h>
20
static struct clk *armclk;
21
static struct regulator *vddarm;
23
#ifdef CONFIG_CPU_S3C6410
25
unsigned int vddarm_min;
26
unsigned int vddarm_max;
29
static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
30
[0] = { 1000000, 1000000 },
31
[1] = { 1000000, 1050000 },
32
[2] = { 1050000, 1100000 },
33
[3] = { 1050000, 1150000 },
34
[4] = { 1250000, 1350000 },
37
static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
47
{ 0, CPUFREQ_TABLE_END },
51
static int s3c64xx_cpufreq_verify_speed(struct cpufreq_policy *policy)
56
return cpufreq_frequency_table_verify(policy, s3c64xx_freq_table);
59
static unsigned int s3c64xx_cpufreq_get_speed(unsigned int cpu)
64
return clk_get_rate(armclk) / 1000;
67
static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
68
unsigned int target_freq,
69
unsigned int relation)
73
struct cpufreq_freqs freqs;
74
struct s3c64xx_dvfs *dvfs;
76
ret = cpufreq_frequency_table_target(policy, s3c64xx_freq_table,
77
target_freq, relation, &i);
82
freqs.old = clk_get_rate(armclk) / 1000;
83
freqs.new = s3c64xx_freq_table[i].frequency;
85
dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index];
87
if (freqs.old == freqs.new)
90
pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new);
92
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
94
#ifdef CONFIG_REGULATOR
95
if (vddarm && freqs.new > freqs.old) {
96
ret = regulator_set_voltage(vddarm,
100
pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
107
ret = clk_set_rate(armclk, freqs.new * 1000);
109
pr_err("cpufreq: Failed to set rate %dkHz: %d\n",
114
#ifdef CONFIG_REGULATOR
115
if (vddarm && freqs.new < freqs.old) {
116
ret = regulator_set_voltage(vddarm,
120
pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
127
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
129
pr_debug("cpufreq: Set actual frequency %lukHz\n",
130
clk_get_rate(armclk) / 1000);
135
if (clk_set_rate(armclk, freqs.old * 1000) < 0)
136
pr_err("Failed to restore original clock rate\n");
138
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
143
#ifdef CONFIG_REGULATOR
144
static void __init s3c64xx_cpufreq_constrain_voltages(void)
146
int count, v, i, found;
147
struct cpufreq_frequency_table *freq;
148
struct s3c64xx_dvfs *dvfs;
150
count = regulator_count_voltages(vddarm);
152
pr_err("cpufreq: Unable to check supported voltages\n");
156
freq = s3c64xx_freq_table;
157
while (freq->frequency != CPUFREQ_TABLE_END) {
158
if (freq->frequency == CPUFREQ_ENTRY_INVALID)
161
dvfs = &s3c64xx_dvfs_table[freq->index];
164
for (i = 0; i < count; i++) {
165
v = regulator_list_voltage(vddarm, i);
166
if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max)
171
pr_debug("cpufreq: %dkHz unsupported by regulator\n",
173
freq->frequency = CPUFREQ_ENTRY_INVALID;
181
static int __init s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
184
struct cpufreq_frequency_table *freq;
186
if (policy->cpu != 0)
189
if (s3c64xx_freq_table == NULL) {
190
pr_err("cpufreq: No frequency information for this CPU\n");
194
armclk = clk_get(NULL, "armclk");
195
if (IS_ERR(armclk)) {
196
pr_err("cpufreq: Unable to obtain ARMCLK: %ld\n",
198
return PTR_ERR(armclk);
201
#ifdef CONFIG_REGULATOR
202
vddarm = regulator_get(NULL, "vddarm");
203
if (IS_ERR(vddarm)) {
204
ret = PTR_ERR(vddarm);
205
pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
206
pr_err("cpufreq: Only frequency scaling available\n");
209
s3c64xx_cpufreq_constrain_voltages();
213
freq = s3c64xx_freq_table;
214
while (freq->frequency != CPUFREQ_TABLE_END) {
217
/* Check for frequencies we can generate */
218
r = clk_round_rate(armclk, freq->frequency * 1000);
220
if (r != freq->frequency)
221
freq->frequency = CPUFREQ_ENTRY_INVALID;
223
/* If we have no regulator then assume startup
224
* frequency is the maximum we can support. */
225
if (!vddarm && freq->frequency > s3c64xx_cpufreq_get_speed(0))
226
freq->frequency = CPUFREQ_ENTRY_INVALID;
231
policy->cur = clk_get_rate(armclk) / 1000;
233
/* Pick a conservative guess in ns: we'll need ~1 I2C/SPI
234
* write plus clock reprogramming. */
235
policy->cpuinfo.transition_latency = 2 * 1000 * 1000;
237
ret = cpufreq_frequency_table_cpuinfo(policy, s3c64xx_freq_table);
239
pr_err("cpufreq: Failed to configure frequency table: %d\n",
241
regulator_put(vddarm);
248
static struct cpufreq_driver s3c64xx_cpufreq_driver = {
249
.owner = THIS_MODULE,
251
.verify = s3c64xx_cpufreq_verify_speed,
252
.target = s3c64xx_cpufreq_set_target,
253
.get = s3c64xx_cpufreq_get_speed,
254
.init = s3c64xx_cpufreq_driver_init,
258
static int __init s3c64xx_cpufreq_init(void)
260
return cpufreq_register_driver(&s3c64xx_cpufreq_driver);
262
module_init(s3c64xx_cpufreq_init);