Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * S3C2416/2450 CPUfreq Support
0004  *
0005  * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
0006  *
0007  * based on s3c64xx_cpufreq.c
0008  *
0009  * Copyright 2009 Wolfson Microelectronics plc
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 /* pseudo-frequency for dvs mode */
0048 #define FREQ_DVS    132333
0049 
0050 /* frequency to sleep and reboot in
0051  * it's essential to leave dvs, as some boards do not reconfigure the
0052  * regulator on reboot
0053  */
0054 #define FREQ_SLEEP  133333
0055 
0056 /* Sources for the ARMCLK */
0057 #define SOURCE_HCLK 0
0058 #define SOURCE_ARMDIV   1
0059 
0060 #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
0061 /* S3C2416 only supports changing the voltage in the dvs-mode.
0062  * Voltages down to 1.0V seem to work, so we take what the regulator
0063  * can get us.
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     /* return our pseudo-frequency when in dvs mode */
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     /* changing the core voltage is only allowed when in dvs mode */
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         /* when lowering the voltage failed, there is nothing to do */
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     /* force armdiv to hclk frequency for transition from dvs*/
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     /* switching to dvs when it's not allowed */
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     /* When leavin dvs mode, always switch the armdiv to the hclk rate
0238      * The S3C2416 has stability issues when switching directly to
0239      * higher frequencies.
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         /* Check only the min-voltage, more is always ok on S3C2416 */
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     /* Guessed */
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     /* disable further changes */
0312     s3c_freq->disable_dvs = 1;
0313 
0314     mutex_unlock(&cpufreq_lock);
0315 
0316     /* some boards don't reconfigure the regulator on reboot, which
0317      * could lead to undervolting the cpu when the clock is reset.
0318      * Therefore we always leave the DVS mode on reboot.
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      * S3C2416 and S3C2450 share the same processor-ID and also provide no
0363      * other means to distinguish them other than through the rate of
0364      * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz.
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     /* not needed anymore */
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     /* chech hclk rate, we only support the common 133MHz for now
0405      * hclk could also run at 66MHz, but this not often used
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         /* special handling for dvs mode */
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         /* Check for frequencies we can generate */
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     /* Datasheet says PLL stabalisation time must be at least 300us,
0458      * so but add some fudge. (reference in LOCKCON0 register description)
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);