linux/drivers/cpufreq/s3c64xx-cpufreq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright 2009 Wolfson Microelectronics plc
   4 *
   5 * S3C64xx CPUfreq Support
   6 */
   7
   8#define pr_fmt(fmt) "cpufreq: " fmt
   9
  10#include <linux/kernel.h>
  11#include <linux/types.h>
  12#include <linux/init.h>
  13#include <linux/cpufreq.h>
  14#include <linux/clk.h>
  15#include <linux/err.h>
  16#include <linux/regulator/consumer.h>
  17#include <linux/module.h>
  18
  19static struct regulator *vddarm;
  20static unsigned long regulator_latency;
  21
  22struct s3c64xx_dvfs {
  23        unsigned int vddarm_min;
  24        unsigned int vddarm_max;
  25};
  26
  27static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
  28        [0] = { 1000000, 1150000 },
  29        [1] = { 1050000, 1150000 },
  30        [2] = { 1100000, 1150000 },
  31        [3] = { 1200000, 1350000 },
  32        [4] = { 1300000, 1350000 },
  33};
  34
  35static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
  36        { 0, 0,  66000 },
  37        { 0, 0, 100000 },
  38        { 0, 0, 133000 },
  39        { 0, 1, 200000 },
  40        { 0, 1, 222000 },
  41        { 0, 1, 266000 },
  42        { 0, 2, 333000 },
  43        { 0, 2, 400000 },
  44        { 0, 2, 532000 },
  45        { 0, 2, 533000 },
  46        { 0, 3, 667000 },
  47        { 0, 4, 800000 },
  48        { 0, 0, CPUFREQ_TABLE_END },
  49};
  50
  51static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
  52                                      unsigned int index)
  53{
  54        struct s3c64xx_dvfs *dvfs;
  55        unsigned int old_freq, new_freq;
  56        int ret;
  57
  58        old_freq = clk_get_rate(policy->clk) / 1000;
  59        new_freq = s3c64xx_freq_table[index].frequency;
  60        dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[index].driver_data];
  61
  62#ifdef CONFIG_REGULATOR
  63        if (vddarm && new_freq > old_freq) {
  64                ret = regulator_set_voltage(vddarm,
  65                                            dvfs->vddarm_min,
  66                                            dvfs->vddarm_max);
  67                if (ret != 0) {
  68                        pr_err("Failed to set VDDARM for %dkHz: %d\n",
  69                               new_freq, ret);
  70                        return ret;
  71                }
  72        }
  73#endif
  74
  75        ret = clk_set_rate(policy->clk, new_freq * 1000);
  76        if (ret < 0) {
  77                pr_err("Failed to set rate %dkHz: %d\n",
  78                       new_freq, ret);
  79                return ret;
  80        }
  81
  82#ifdef CONFIG_REGULATOR
  83        if (vddarm && new_freq < old_freq) {
  84                ret = regulator_set_voltage(vddarm,
  85                                            dvfs->vddarm_min,
  86                                            dvfs->vddarm_max);
  87                if (ret != 0) {
  88                        pr_err("Failed to set VDDARM for %dkHz: %d\n",
  89                               new_freq, ret);
  90                        if (clk_set_rate(policy->clk, old_freq * 1000) < 0)
  91                                pr_err("Failed to restore original clock rate\n");
  92
  93                        return ret;
  94                }
  95        }
  96#endif
  97
  98        pr_debug("Set actual frequency %lukHz\n",
  99                 clk_get_rate(policy->clk) / 1000);
 100
 101        return 0;
 102}
 103
 104#ifdef CONFIG_REGULATOR
 105static void s3c64xx_cpufreq_config_regulator(void)
 106{
 107        int count, v, i, found;
 108        struct cpufreq_frequency_table *freq;
 109        struct s3c64xx_dvfs *dvfs;
 110
 111        count = regulator_count_voltages(vddarm);
 112        if (count < 0) {
 113                pr_err("Unable to check supported voltages\n");
 114        }
 115
 116        if (!count)
 117                goto out;
 118
 119        cpufreq_for_each_valid_entry(freq, s3c64xx_freq_table) {
 120                dvfs = &s3c64xx_dvfs_table[freq->driver_data];
 121                found = 0;
 122
 123                for (i = 0; i < count; i++) {
 124                        v = regulator_list_voltage(vddarm, i);
 125                        if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max)
 126                                found = 1;
 127                }
 128
 129                if (!found) {
 130                        pr_debug("%dkHz unsupported by regulator\n",
 131                                 freq->frequency);
 132                        freq->frequency = CPUFREQ_ENTRY_INVALID;
 133                }
 134        }
 135
 136out:
 137        /* Guess based on having to do an I2C/SPI write; in future we
 138         * will be able to query the regulator performance here. */
 139        regulator_latency = 1 * 1000 * 1000;
 140}
 141#endif
 142
 143static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
 144{
 145        struct cpufreq_frequency_table *freq;
 146
 147        if (policy->cpu != 0)
 148                return -EINVAL;
 149
 150        policy->clk = clk_get(NULL, "armclk");
 151        if (IS_ERR(policy->clk)) {
 152                pr_err("Unable to obtain ARMCLK: %ld\n",
 153                       PTR_ERR(policy->clk));
 154                return PTR_ERR(policy->clk);
 155        }
 156
 157#ifdef CONFIG_REGULATOR
 158        vddarm = regulator_get(NULL, "vddarm");
 159        if (IS_ERR(vddarm)) {
 160                pr_err("Failed to obtain VDDARM: %ld\n", PTR_ERR(vddarm));
 161                pr_err("Only frequency scaling available\n");
 162                vddarm = NULL;
 163        } else {
 164                s3c64xx_cpufreq_config_regulator();
 165        }
 166#endif
 167
 168        cpufreq_for_each_entry(freq, s3c64xx_freq_table) {
 169                unsigned long r;
 170
 171                /* Check for frequencies we can generate */
 172                r = clk_round_rate(policy->clk, freq->frequency * 1000);
 173                r /= 1000;
 174                if (r != freq->frequency) {
 175                        pr_debug("%dkHz unsupported by clock\n",
 176                                 freq->frequency);
 177                        freq->frequency = CPUFREQ_ENTRY_INVALID;
 178                }
 179
 180                /* If we have no regulator then assume startup
 181                 * frequency is the maximum we can support. */
 182                if (!vddarm && freq->frequency > clk_get_rate(policy->clk) / 1000)
 183                        freq->frequency = CPUFREQ_ENTRY_INVALID;
 184        }
 185
 186        /* Datasheet says PLL stabalisation time (if we were to use
 187         * the PLLs, which we don't currently) is ~300us worst case,
 188         * but add some fudge.
 189         */
 190        cpufreq_generic_init(policy, s3c64xx_freq_table,
 191                        (500 * 1000) + regulator_latency);
 192        return 0;
 193}
 194
 195static struct cpufreq_driver s3c64xx_cpufreq_driver = {
 196        .flags          = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
 197        .verify         = cpufreq_generic_frequency_table_verify,
 198        .target_index   = s3c64xx_cpufreq_set_target,
 199        .get            = cpufreq_generic_get,
 200        .init           = s3c64xx_cpufreq_driver_init,
 201        .name           = "s3c",
 202};
 203
 204static int __init s3c64xx_cpufreq_init(void)
 205{
 206        return cpufreq_register_driver(&s3c64xx_cpufreq_driver);
 207}
 208module_init(s3c64xx_cpufreq_init);
 209