0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/kernel.h>
0013 #include <linux/types.h>
0014 #include <linux/init.h>
0015 #include <linux/cpufreq.h>
0016 #include <linux/clk.h>
0017 #include <linux/err.h>
0018 #include <linux/regulator/consumer.h>
0019 #include <linux/reboot.h>
0020 #include <linux/module.h>
0021
0022 static DEFINE_MUTEX(cpufreq_lock);
0023
0024 struct s3c2416_data {
0025 struct clk *armdiv;
0026 struct clk *armclk;
0027 struct clk *hclk;
0028
0029 unsigned long regulator_latency;
0030 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0031 struct regulator *vddarm;
0032 #endif
0033
0034 struct cpufreq_frequency_table *freq_table;
0035
0036 bool is_dvs;
0037 bool disable_dvs;
0038 };
0039
0040 static struct s3c2416_data s3c2416_cpufreq;
0041
0042 struct s3c2416_dvfs {
0043 unsigned int vddarm_min;
0044 unsigned int vddarm_max;
0045 };
0046
0047
0048 #define FREQ_DVS 132333
0049
0050
0051
0052
0053
0054 #define FREQ_SLEEP 133333
0055
0056
0057 #define SOURCE_HCLK 0
0058 #define SOURCE_ARMDIV 1
0059
0060 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0061
0062
0063
0064
0065 static struct s3c2416_dvfs s3c2416_dvfs_table[] = {
0066 [SOURCE_HCLK] = { 950000, 1250000 },
0067 [SOURCE_ARMDIV] = { 1250000, 1350000 },
0068 };
0069 #endif
0070
0071 static struct cpufreq_frequency_table s3c2416_freq_table[] = {
0072 { 0, SOURCE_HCLK, FREQ_DVS },
0073 { 0, SOURCE_ARMDIV, 133333 },
0074 { 0, SOURCE_ARMDIV, 266666 },
0075 { 0, SOURCE_ARMDIV, 400000 },
0076 { 0, 0, CPUFREQ_TABLE_END },
0077 };
0078
0079 static struct cpufreq_frequency_table s3c2450_freq_table[] = {
0080 { 0, SOURCE_HCLK, FREQ_DVS },
0081 { 0, SOURCE_ARMDIV, 133500 },
0082 { 0, SOURCE_ARMDIV, 267000 },
0083 { 0, SOURCE_ARMDIV, 534000 },
0084 { 0, 0, CPUFREQ_TABLE_END },
0085 };
0086
0087 static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu)
0088 {
0089 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
0090
0091 if (cpu != 0)
0092 return 0;
0093
0094
0095 if (s3c_freq->is_dvs)
0096 return FREQ_DVS;
0097
0098 return clk_get_rate(s3c_freq->armclk) / 1000;
0099 }
0100
0101 static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq,
0102 unsigned int freq)
0103 {
0104 int ret;
0105
0106 if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) {
0107 ret = clk_set_rate(s3c_freq->armdiv, freq * 1000);
0108 if (ret < 0) {
0109 pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n",
0110 freq, ret);
0111 return ret;
0112 }
0113 }
0114
0115 return 0;
0116 }
0117
0118 static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx)
0119 {
0120 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0121 struct s3c2416_dvfs *dvfs;
0122 #endif
0123 int ret;
0124
0125 if (s3c_freq->is_dvs) {
0126 pr_debug("cpufreq: already in dvs mode, nothing to do\n");
0127 return 0;
0128 }
0129
0130 pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n",
0131 clk_get_rate(s3c_freq->hclk) / 1000);
0132 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk);
0133 if (ret < 0) {
0134 pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret);
0135 return ret;
0136 }
0137
0138 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0139
0140 if (s3c_freq->vddarm) {
0141 dvfs = &s3c2416_dvfs_table[idx];
0142
0143 pr_debug("cpufreq: setting regulator to %d-%d\n",
0144 dvfs->vddarm_min, dvfs->vddarm_max);
0145 ret = regulator_set_voltage(s3c_freq->vddarm,
0146 dvfs->vddarm_min,
0147 dvfs->vddarm_max);
0148
0149
0150 if (ret != 0)
0151 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
0152 }
0153 #endif
0154
0155 s3c_freq->is_dvs = 1;
0156
0157 return 0;
0158 }
0159
0160 static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx)
0161 {
0162 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0163 struct s3c2416_dvfs *dvfs;
0164 #endif
0165 int ret;
0166
0167 if (!s3c_freq->is_dvs) {
0168 pr_debug("cpufreq: not in dvs mode, so can't leave\n");
0169 return 0;
0170 }
0171
0172 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0173 if (s3c_freq->vddarm) {
0174 dvfs = &s3c2416_dvfs_table[idx];
0175
0176 pr_debug("cpufreq: setting regulator to %d-%d\n",
0177 dvfs->vddarm_min, dvfs->vddarm_max);
0178 ret = regulator_set_voltage(s3c_freq->vddarm,
0179 dvfs->vddarm_min,
0180 dvfs->vddarm_max);
0181 if (ret != 0) {
0182 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
0183 return ret;
0184 }
0185 }
0186 #endif
0187
0188
0189 if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) {
0190 pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n",
0191 clk_get_rate(s3c_freq->hclk) / 1000);
0192 ret = s3c2416_cpufreq_set_armdiv(s3c_freq,
0193 clk_get_rate(s3c_freq->hclk) / 1000);
0194 if (ret < 0) {
0195 pr_err("cpufreq: Failed to set the armdiv to %lukHz: %d\n",
0196 clk_get_rate(s3c_freq->hclk) / 1000, ret);
0197 return ret;
0198 }
0199 }
0200
0201 pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n",
0202 clk_get_rate(s3c_freq->armdiv) / 1000);
0203
0204 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv);
0205 if (ret < 0) {
0206 pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n",
0207 ret);
0208 return ret;
0209 }
0210
0211 s3c_freq->is_dvs = 0;
0212
0213 return 0;
0214 }
0215
0216 static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
0217 unsigned int index)
0218 {
0219 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
0220 unsigned int new_freq;
0221 int idx, ret, to_dvs = 0;
0222
0223 mutex_lock(&cpufreq_lock);
0224
0225 idx = s3c_freq->freq_table[index].driver_data;
0226
0227 if (idx == SOURCE_HCLK)
0228 to_dvs = 1;
0229
0230
0231 if (to_dvs && s3c_freq->disable_dvs) {
0232 pr_debug("cpufreq: entering dvs mode not allowed\n");
0233 ret = -EINVAL;
0234 goto out;
0235 }
0236
0237
0238
0239
0240
0241 new_freq = (s3c_freq->is_dvs && !to_dvs)
0242 ? clk_get_rate(s3c_freq->hclk) / 1000
0243 : s3c_freq->freq_table[index].frequency;
0244
0245 if (to_dvs) {
0246 pr_debug("cpufreq: enter dvs\n");
0247 ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx);
0248 } else if (s3c_freq->is_dvs) {
0249 pr_debug("cpufreq: leave dvs\n");
0250 ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx);
0251 } else {
0252 pr_debug("cpufreq: change armdiv to %dkHz\n", new_freq);
0253 ret = s3c2416_cpufreq_set_armdiv(s3c_freq, new_freq);
0254 }
0255
0256 out:
0257 mutex_unlock(&cpufreq_lock);
0258
0259 return ret;
0260 }
0261
0262 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0263 static void s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
0264 {
0265 int count, v, i, found;
0266 struct cpufreq_frequency_table *pos;
0267 struct s3c2416_dvfs *dvfs;
0268
0269 count = regulator_count_voltages(s3c_freq->vddarm);
0270 if (count < 0) {
0271 pr_err("cpufreq: Unable to check supported voltages\n");
0272 return;
0273 }
0274
0275 if (!count)
0276 goto out;
0277
0278 cpufreq_for_each_valid_entry(pos, s3c_freq->freq_table) {
0279 dvfs = &s3c2416_dvfs_table[pos->driver_data];
0280 found = 0;
0281
0282
0283 for (i = 0; i < count; i++) {
0284 v = regulator_list_voltage(s3c_freq->vddarm, i);
0285 if (v >= dvfs->vddarm_min)
0286 found = 1;
0287 }
0288
0289 if (!found) {
0290 pr_debug("cpufreq: %dkHz unsupported by regulator\n",
0291 pos->frequency);
0292 pos->frequency = CPUFREQ_ENTRY_INVALID;
0293 }
0294 }
0295
0296 out:
0297
0298 s3c_freq->regulator_latency = 1 * 1000 * 1000;
0299 }
0300 #endif
0301
0302 static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this,
0303 unsigned long event, void *ptr)
0304 {
0305 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
0306 int ret;
0307 struct cpufreq_policy *policy;
0308
0309 mutex_lock(&cpufreq_lock);
0310
0311
0312 s3c_freq->disable_dvs = 1;
0313
0314 mutex_unlock(&cpufreq_lock);
0315
0316
0317
0318
0319
0320 if (s3c_freq->is_dvs) {
0321 pr_debug("cpufreq: leave dvs on reboot\n");
0322
0323 policy = cpufreq_cpu_get(0);
0324 if (!policy) {
0325 pr_debug("cpufreq: get no policy for cpu0\n");
0326 return NOTIFY_BAD;
0327 }
0328
0329 ret = cpufreq_driver_target(policy, FREQ_SLEEP, 0);
0330 cpufreq_cpu_put(policy);
0331
0332 if (ret < 0)
0333 return NOTIFY_BAD;
0334 }
0335
0336 return NOTIFY_DONE;
0337 }
0338
0339 static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
0340 .notifier_call = s3c2416_cpufreq_reboot_notifier_evt,
0341 };
0342
0343 static int s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
0344 {
0345 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
0346 struct cpufreq_frequency_table *pos;
0347 struct clk *msysclk;
0348 unsigned long rate;
0349 int ret;
0350
0351 if (policy->cpu != 0)
0352 return -EINVAL;
0353
0354 msysclk = clk_get(NULL, "msysclk");
0355 if (IS_ERR(msysclk)) {
0356 ret = PTR_ERR(msysclk);
0357 pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret);
0358 return ret;
0359 }
0360
0361
0362
0363
0364
0365
0366 rate = clk_get_rate(msysclk);
0367 if (rate == 800 * 1000 * 1000) {
0368 pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n",
0369 rate / 1000);
0370 s3c_freq->freq_table = s3c2416_freq_table;
0371 policy->cpuinfo.max_freq = 400000;
0372 } else if (rate / 1000 == 534000) {
0373 pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n",
0374 rate / 1000);
0375 s3c_freq->freq_table = s3c2450_freq_table;
0376 policy->cpuinfo.max_freq = 534000;
0377 }
0378
0379
0380 clk_put(msysclk);
0381
0382 if (s3c_freq->freq_table == NULL) {
0383 pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n",
0384 rate / 1000);
0385 return -ENODEV;
0386 }
0387
0388 s3c_freq->is_dvs = 0;
0389
0390 s3c_freq->armdiv = clk_get(NULL, "armdiv");
0391 if (IS_ERR(s3c_freq->armdiv)) {
0392 ret = PTR_ERR(s3c_freq->armdiv);
0393 pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret);
0394 return ret;
0395 }
0396
0397 s3c_freq->hclk = clk_get(NULL, "hclk");
0398 if (IS_ERR(s3c_freq->hclk)) {
0399 ret = PTR_ERR(s3c_freq->hclk);
0400 pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret);
0401 goto err_hclk;
0402 }
0403
0404
0405
0406
0407 rate = clk_get_rate(s3c_freq->hclk);
0408 if (rate < 133 * 1000 * 1000) {
0409 pr_err("cpufreq: HCLK not at 133MHz\n");
0410 ret = -EINVAL;
0411 goto err_armclk;
0412 }
0413
0414 s3c_freq->armclk = clk_get(NULL, "armclk");
0415 if (IS_ERR(s3c_freq->armclk)) {
0416 ret = PTR_ERR(s3c_freq->armclk);
0417 pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret);
0418 goto err_armclk;
0419 }
0420
0421 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0422 s3c_freq->vddarm = regulator_get(NULL, "vddarm");
0423 if (IS_ERR(s3c_freq->vddarm)) {
0424 ret = PTR_ERR(s3c_freq->vddarm);
0425 pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
0426 goto err_vddarm;
0427 }
0428
0429 s3c2416_cpufreq_cfg_regulator(s3c_freq);
0430 #else
0431 s3c_freq->regulator_latency = 0;
0432 #endif
0433
0434 cpufreq_for_each_entry(pos, s3c_freq->freq_table) {
0435
0436 if (pos->driver_data == 0) {
0437 if (!s3c_freq->hclk) {
0438 pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
0439 pos->frequency);
0440 pos->frequency = CPUFREQ_ENTRY_INVALID;
0441 } else {
0442 continue;
0443 }
0444 }
0445
0446
0447 rate = clk_round_rate(s3c_freq->armdiv,
0448 pos->frequency * 1000);
0449 rate /= 1000;
0450 if (rate != pos->frequency) {
0451 pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
0452 pos->frequency, rate);
0453 pos->frequency = CPUFREQ_ENTRY_INVALID;
0454 }
0455 }
0456
0457
0458
0459
0460 cpufreq_generic_init(policy, s3c_freq->freq_table,
0461 (500 * 1000) + s3c_freq->regulator_latency);
0462 register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier);
0463
0464 return 0;
0465
0466 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0467 err_vddarm:
0468 clk_put(s3c_freq->armclk);
0469 #endif
0470 err_armclk:
0471 clk_put(s3c_freq->hclk);
0472 err_hclk:
0473 clk_put(s3c_freq->armdiv);
0474
0475 return ret;
0476 }
0477
0478 static struct cpufreq_driver s3c2416_cpufreq_driver = {
0479 .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
0480 .verify = cpufreq_generic_frequency_table_verify,
0481 .target_index = s3c2416_cpufreq_set_target,
0482 .get = s3c2416_cpufreq_get_speed,
0483 .init = s3c2416_cpufreq_driver_init,
0484 .name = "s3c2416",
0485 .attr = cpufreq_generic_attr,
0486 };
0487
0488 static int __init s3c2416_cpufreq_init(void)
0489 {
0490 return cpufreq_register_driver(&s3c2416_cpufreq_driver);
0491 }
0492 module_init(s3c2416_cpufreq_init);