Blame view
drivers/cpufreq/s3c64xx-cpufreq.c
4.93 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
be2de99be [CPUFREQ/S3C64xx]... |
2 |
/* |
b3748ddd8 [ARM] S3C64XX: In... |
3 4 5 |
* Copyright 2009 Wolfson Microelectronics plc * * S3C64xx CPUfreq Support |
b3748ddd8 [ARM] S3C64XX: In... |
6 |
*/ |
a6a434124 [CPUFREQ] s3c64xx... |
7 |
#define pr_fmt(fmt) "cpufreq: " fmt |
b3748ddd8 [ARM] S3C64XX: In... |
8 9 10 11 12 13 14 |
#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... |
15 |
#include <linux/module.h> |
b3748ddd8 [ARM] S3C64XX: In... |
16 |
|
b3748ddd8 [ARM] S3C64XX: In... |
17 |
static struct regulator *vddarm; |
43f1069ef ARM: S3C64XX: Sep... |
18 |
static unsigned long regulator_latency; |
b3748ddd8 [ARM] S3C64XX: In... |
19 |
|
b3748ddd8 [ARM] S3C64XX: In... |
20 21 22 23 24 25 |
struct s3c64xx_dvfs { unsigned int vddarm_min; unsigned int vddarm_max; }; static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = { |
e9c08f0d5 ARM: S3C64XX: Red... |
26 27 28 29 |
[0] = { 1000000, 1150000 }, [1] = { 1050000, 1150000 }, [2] = { 1100000, 1150000 }, [3] = { 1200000, 1350000 }, |
c6e2d6855 [CPUFREQ] S3C6410... |
30 |
[4] = { 1300000, 1350000 }, |
b3748ddd8 [ARM] S3C64XX: In... |
31 32 33 |
}; static struct cpufreq_frequency_table s3c64xx_freq_table[] = { |
7f4b04614 cpufreq: create a... |
34 35 36 37 38 39 40 41 42 43 44 45 46 |
{ 0, 0, 66000 }, { 0, 0, 100000 }, { 0, 0, 133000 }, { 0, 1, 200000 }, { 0, 1, 222000 }, { 0, 1, 266000 }, { 0, 2, 333000 }, { 0, 2, 400000 }, { 0, 2, 532000 }, { 0, 2, 533000 }, { 0, 3, 667000 }, { 0, 4, 800000 }, { 0, 0, CPUFREQ_TABLE_END }, |
b3748ddd8 [ARM] S3C64XX: In... |
47 |
}; |
b3748ddd8 [ARM] S3C64XX: In... |
48 |
|
b3748ddd8 [ARM] S3C64XX: In... |
49 |
static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, |
9c0ebcf78 cpufreq: Implemen... |
50 |
unsigned int index) |
b3748ddd8 [ARM] S3C64XX: In... |
51 |
{ |
b3748ddd8 [ARM] S3C64XX: In... |
52 |
struct s3c64xx_dvfs *dvfs; |
d4019f0a9 cpufreq: move fre... |
53 54 |
unsigned int old_freq, new_freq; int ret; |
b3748ddd8 [ARM] S3C64XX: In... |
55 |
|
652ed95d5 cpufreq: introduc... |
56 |
old_freq = clk_get_rate(policy->clk) / 1000; |
d4019f0a9 cpufreq: move fre... |
57 |
new_freq = s3c64xx_freq_table[index].frequency; |
9c0ebcf78 cpufreq: Implemen... |
58 |
dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[index].driver_data]; |
b3748ddd8 [ARM] S3C64XX: In... |
59 |
|
b3748ddd8 [ARM] S3C64XX: In... |
60 |
#ifdef CONFIG_REGULATOR |
d4019f0a9 cpufreq: move fre... |
61 |
if (vddarm && new_freq > old_freq) { |
b3748ddd8 [ARM] S3C64XX: In... |
62 63 64 65 |
ret = regulator_set_voltage(vddarm, dvfs->vddarm_min, dvfs->vddarm_max); if (ret != 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
66 67 |
pr_err("Failed to set VDDARM for %dkHz: %d ", |
d4019f0a9 cpufreq: move fre... |
68 69 |
new_freq, ret); return ret; |
b3748ddd8 [ARM] S3C64XX: In... |
70 71 72 |
} } #endif |
652ed95d5 cpufreq: introduc... |
73 |
ret = clk_set_rate(policy->clk, new_freq * 1000); |
b3748ddd8 [ARM] S3C64XX: In... |
74 |
if (ret < 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
75 76 |
pr_err("Failed to set rate %dkHz: %d ", |
d4019f0a9 cpufreq: move fre... |
77 78 |
new_freq, ret); return ret; |
b3748ddd8 [ARM] S3C64XX: In... |
79 80 81 |
} #ifdef CONFIG_REGULATOR |
d4019f0a9 cpufreq: move fre... |
82 |
if (vddarm && new_freq < old_freq) { |
b3748ddd8 [ARM] S3C64XX: In... |
83 84 85 86 |
ret = regulator_set_voltage(vddarm, dvfs->vddarm_min, dvfs->vddarm_max); if (ret != 0) { |
a6a434124 [CPUFREQ] s3c64xx... |
87 88 |
pr_err("Failed to set VDDARM for %dkHz: %d ", |
d4019f0a9 cpufreq: move fre... |
89 |
new_freq, ret); |
652ed95d5 cpufreq: introduc... |
90 |
if (clk_set_rate(policy->clk, old_freq * 1000) < 0) |
d4019f0a9 cpufreq: move fre... |
91 92 93 94 |
pr_err("Failed to restore original clock rate "); return ret; |
b3748ddd8 [ARM] S3C64XX: In... |
95 96 97 |
} } #endif |
a6a434124 [CPUFREQ] s3c64xx... |
98 99 |
pr_debug("Set actual frequency %lukHz ", |
652ed95d5 cpufreq: introduc... |
100 |
clk_get_rate(policy->clk) / 1000); |
b3748ddd8 [ARM] S3C64XX: In... |
101 102 |
return 0; |
b3748ddd8 [ARM] S3C64XX: In... |
103 104 105 |
} #ifdef CONFIG_REGULATOR |
adec57c61 cpufreq: s3c64xx:... |
106 |
static void s3c64xx_cpufreq_config_regulator(void) |
b3748ddd8 [ARM] S3C64XX: In... |
107 108 109 110 111 112 113 |
{ 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... |
114 115 |
pr_err("Unable to check supported voltages "); |
b3748ddd8 [ARM] S3C64XX: In... |
116 |
} |
041526f91 cpufreq: Use cpuf... |
117 118 |
if (!count) goto out; |
b3748ddd8 [ARM] S3C64XX: In... |
119 |
|
041526f91 cpufreq: Use cpuf... |
120 |
cpufreq_for_each_valid_entry(freq, s3c64xx_freq_table) { |
0e8244322 cpufreq: s3c64xx:... |
121 |
dvfs = &s3c64xx_dvfs_table[freq->driver_data]; |
b3748ddd8 [ARM] S3C64XX: In... |
122 123 124 125 126 127 128 129 130 |
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... |
131 132 |
pr_debug("%dkHz unsupported by regulator ", |
b3748ddd8 [ARM] S3C64XX: In... |
133 134 135 |
freq->frequency); freq->frequency = CPUFREQ_ENTRY_INVALID; } |
b3748ddd8 [ARM] S3C64XX: In... |
136 |
} |
43f1069ef ARM: S3C64XX: Sep... |
137 |
|
041526f91 cpufreq: Use cpuf... |
138 |
out: |
43f1069ef ARM: S3C64XX: Sep... |
139 140 141 |
/* 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... |
142 143 |
} #endif |
6d0de1577 ARM: S3C64XX: Fix... |
144 |
static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) |
b3748ddd8 [ARM] S3C64XX: In... |
145 |
{ |
b3748ddd8 [ARM] S3C64XX: In... |
146 147 148 149 |
struct cpufreq_frequency_table *freq; if (policy->cpu != 0) return -EINVAL; |
652ed95d5 cpufreq: introduc... |
150 151 |
policy->clk = clk_get(NULL, "armclk"); if (IS_ERR(policy->clk)) { |
a6a434124 [CPUFREQ] s3c64xx... |
152 153 |
pr_err("Unable to obtain ARMCLK: %ld ", |
652ed95d5 cpufreq: introduc... |
154 155 |
PTR_ERR(policy->clk)); return PTR_ERR(policy->clk); |
b3748ddd8 [ARM] S3C64XX: In... |
156 157 158 159 160 |
} #ifdef CONFIG_REGULATOR vddarm = regulator_get(NULL, "vddarm"); if (IS_ERR(vddarm)) { |
c4dcc8a16 cpufreq: Make cpu... |
161 162 |
pr_err("Failed to obtain VDDARM: %ld ", PTR_ERR(vddarm)); |
a6a434124 [CPUFREQ] s3c64xx... |
163 164 |
pr_err("Only frequency scaling available "); |
b3748ddd8 [ARM] S3C64XX: In... |
165 166 |
vddarm = NULL; } else { |
43f1069ef ARM: S3C64XX: Sep... |
167 |
s3c64xx_cpufreq_config_regulator(); |
b3748ddd8 [ARM] S3C64XX: In... |
168 169 |
} #endif |
041526f91 cpufreq: Use cpuf... |
170 |
cpufreq_for_each_entry(freq, s3c64xx_freq_table) { |
b3748ddd8 [ARM] S3C64XX: In... |
171 172 173 |
unsigned long r; /* Check for frequencies we can generate */ |
652ed95d5 cpufreq: introduc... |
174 |
r = clk_round_rate(policy->clk, freq->frequency * 1000); |
b3748ddd8 [ARM] S3C64XX: In... |
175 |
r /= 1000; |
383af9c25 ARM: S3C64XX: Pro... |
176 |
if (r != freq->frequency) { |
a6a434124 [CPUFREQ] s3c64xx... |
177 178 |
pr_debug("%dkHz unsupported by clock ", |
383af9c25 ARM: S3C64XX: Pro... |
179 |
freq->frequency); |
b3748ddd8 [ARM] S3C64XX: In... |
180 |
freq->frequency = CPUFREQ_ENTRY_INVALID; |
383af9c25 ARM: S3C64XX: Pro... |
181 |
} |
b3748ddd8 [ARM] S3C64XX: In... |
182 183 184 |
/* If we have no regulator then assume startup * frequency is the maximum we can support. */ |
652ed95d5 cpufreq: introduc... |
185 |
if (!vddarm && freq->frequency > clk_get_rate(policy->clk) / 1000) |
b3748ddd8 [ARM] S3C64XX: In... |
186 |
freq->frequency = CPUFREQ_ENTRY_INVALID; |
b3748ddd8 [ARM] S3C64XX: In... |
187 |
} |
43f1069ef ARM: S3C64XX: Sep... |
188 189 190 191 |
/* 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. */ |
c4dcc8a16 cpufreq: Make cpu... |
192 |
cpufreq_generic_init(policy, s3c64xx_freq_table, |
a307a1e6b cpufreq: s3c: use... |
193 |
(500 * 1000) + regulator_latency); |
c4dcc8a16 cpufreq: Make cpu... |
194 |
return 0; |
b3748ddd8 [ARM] S3C64XX: In... |
195 196 197 |
} static struct cpufreq_driver s3c64xx_cpufreq_driver = { |
ae6b42713 cpufreq: Mark ARM... |
198 |
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, |
e96a41054 cpufreq: s3cx4xx:... |
199 |
.verify = cpufreq_generic_frequency_table_verify, |
9c0ebcf78 cpufreq: Implemen... |
200 |
.target_index = s3c64xx_cpufreq_set_target, |
652ed95d5 cpufreq: introduc... |
201 |
.get = cpufreq_generic_get, |
b3748ddd8 [ARM] S3C64XX: In... |
202 203 204 205 206 207 208 209 210 |
.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); |