linux/drivers/cpufreq/cppc_cpufreq.c
<<
>>
Prefs
   1/*
   2 * CPPC (Collaborative Processor Performance Control) driver for
   3 * interfacing with the CPUfreq layer and governors. See
   4 * cppc_acpi.c for CPPC specific methods.
   5 *
   6 * (C) Copyright 2014, 2015 Linaro Ltd.
   7 * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License
  11 * as published by the Free Software Foundation; version 2
  12 * of the License.
  13 */
  14
  15#define pr_fmt(fmt)     "CPPC Cpufreq:" fmt
  16
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/delay.h>
  20#include <linux/cpu.h>
  21#include <linux/cpufreq.h>
  22#include <linux/dmi.h>
  23#include <linux/vmalloc.h>
  24
  25#include <asm/unaligned.h>
  26
  27#include <acpi/cppc_acpi.h>
  28
  29/* Minimum struct length needed for the DMI processor entry we want */
  30#define DMI_ENTRY_PROCESSOR_MIN_LENGTH  48
  31
  32/* Offest in the DMI processor structure for the max frequency */
  33#define DMI_PROCESSOR_MAX_SPEED  0x14
  34
  35/*
  36 * These structs contain information parsed from per CPU
  37 * ACPI _CPC structures.
  38 * e.g. For each CPU the highest, lowest supported
  39 * performance capabilities, desired performance level
  40 * requested etc.
  41 */
  42static struct cppc_cpudata **all_cpu_data;
  43
  44/* Capture the max KHz from DMI */
  45static u64 cppc_dmi_max_khz;
  46
  47/* Callback function used to retrieve the max frequency from DMI */
  48static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
  49{
  50        const u8 *dmi_data = (const u8 *)dm;
  51        u16 *mhz = (u16 *)private;
  52
  53        if (dm->type == DMI_ENTRY_PROCESSOR &&
  54            dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) {
  55                u16 val = (u16)get_unaligned((const u16 *)
  56                                (dmi_data + DMI_PROCESSOR_MAX_SPEED));
  57                *mhz = val > *mhz ? val : *mhz;
  58        }
  59}
  60
  61/* Look up the max frequency in DMI */
  62static u64 cppc_get_dmi_max_khz(void)
  63{
  64        u16 mhz = 0;
  65
  66        dmi_walk(cppc_find_dmi_mhz, &mhz);
  67
  68        /*
  69         * Real stupid fallback value, just in case there is no
  70         * actual value set.
  71         */
  72        mhz = mhz ? mhz : 1;
  73
  74        return (1000 * mhz);
  75}
  76
  77static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
  78                unsigned int target_freq,
  79                unsigned int relation)
  80{
  81        struct cppc_cpudata *cpu;
  82        struct cpufreq_freqs freqs;
  83        u32 desired_perf;
  84        int ret = 0;
  85
  86        cpu = all_cpu_data[policy->cpu];
  87
  88        desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz;
  89        /* Return if it is exactly the same perf */
  90        if (desired_perf == cpu->perf_ctrls.desired_perf)
  91                return ret;
  92
  93        cpu->perf_ctrls.desired_perf = desired_perf;
  94        freqs.old = policy->cur;
  95        freqs.new = target_freq;
  96
  97        cpufreq_freq_transition_begin(policy, &freqs);
  98        ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls);
  99        cpufreq_freq_transition_end(policy, &freqs, ret != 0);
 100
 101        if (ret)
 102                pr_debug("Failed to set target on CPU:%d. ret:%d\n",
 103                                cpu->cpu, ret);
 104
 105        return ret;
 106}
 107
 108static int cppc_verify_policy(struct cpufreq_policy *policy)
 109{
 110        cpufreq_verify_within_cpu_limits(policy);
 111        return 0;
 112}
 113
 114static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
 115{
 116        int cpu_num = policy->cpu;
 117        struct cppc_cpudata *cpu = all_cpu_data[cpu_num];
 118        int ret;
 119
 120        cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf;
 121
 122        ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
 123        if (ret)
 124                pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
 125                                cpu->perf_caps.lowest_perf, cpu_num, ret);
 126}
 127
 128static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 129{
 130        struct cppc_cpudata *cpu;
 131        unsigned int cpu_num = policy->cpu;
 132        int ret = 0;
 133
 134        cpu = all_cpu_data[policy->cpu];
 135
 136        cpu->cpu = cpu_num;
 137        ret = cppc_get_perf_caps(policy->cpu, &cpu->perf_caps);
 138
 139        if (ret) {
 140                pr_debug("Err reading CPU%d perf capabilities. ret:%d\n",
 141                                cpu_num, ret);
 142                return ret;
 143        }
 144
 145        cppc_dmi_max_khz = cppc_get_dmi_max_khz();
 146
 147        /*
 148         * Set min to lowest nonlinear perf to avoid any efficiency penalty (see
 149         * Section 8.4.7.1.1.5 of ACPI 6.1 spec)
 150         */
 151        policy->min = cpu->perf_caps.lowest_nonlinear_perf * cppc_dmi_max_khz /
 152                cpu->perf_caps.highest_perf;
 153        policy->max = cppc_dmi_max_khz;
 154
 155        /*
 156         * Set cpuinfo.min_freq to Lowest to make the full range of performance
 157         * available if userspace wants to use any perf between lowest & lowest
 158         * nonlinear perf
 159         */
 160        policy->cpuinfo.min_freq = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz /
 161                cpu->perf_caps.highest_perf;
 162        policy->cpuinfo.max_freq = cppc_dmi_max_khz;
 163
 164        policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num);
 165        policy->shared_type = cpu->shared_type;
 166
 167        if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
 168                cpumask_copy(policy->cpus, cpu->shared_cpu_map);
 169        else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) {
 170                /* Support only SW_ANY for now. */
 171                pr_debug("Unsupported CPU co-ord type\n");
 172                return -EFAULT;
 173        }
 174
 175        cpumask_set_cpu(policy->cpu, policy->cpus);
 176        cpu->cur_policy = policy;
 177
 178        /* Set policy->cur to max now. The governors will adjust later. */
 179        policy->cur = cppc_dmi_max_khz;
 180        cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
 181
 182        ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
 183        if (ret)
 184                pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
 185                                cpu->perf_caps.highest_perf, cpu_num, ret);
 186
 187        return ret;
 188}
 189
 190static struct cpufreq_driver cppc_cpufreq_driver = {
 191        .flags = CPUFREQ_CONST_LOOPS,
 192        .verify = cppc_verify_policy,
 193        .target = cppc_cpufreq_set_target,
 194        .init = cppc_cpufreq_cpu_init,
 195        .stop_cpu = cppc_cpufreq_stop_cpu,
 196        .name = "cppc_cpufreq",
 197};
 198
 199static int __init cppc_cpufreq_init(void)
 200{
 201        int i, ret = 0;
 202        struct cppc_cpudata *cpu;
 203
 204        if (acpi_disabled)
 205                return -ENODEV;
 206
 207        all_cpu_data = kzalloc(sizeof(void *) * num_possible_cpus(), GFP_KERNEL);
 208        if (!all_cpu_data)
 209                return -ENOMEM;
 210
 211        for_each_possible_cpu(i) {
 212                all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL);
 213                if (!all_cpu_data[i])
 214                        goto out;
 215
 216                cpu = all_cpu_data[i];
 217                if (!zalloc_cpumask_var(&cpu->shared_cpu_map, GFP_KERNEL))
 218                        goto out;
 219        }
 220
 221        ret = acpi_get_psd_map(all_cpu_data);
 222        if (ret) {
 223                pr_debug("Error parsing PSD data. Aborting cpufreq registration.\n");
 224                goto out;
 225        }
 226
 227        ret = cpufreq_register_driver(&cppc_cpufreq_driver);
 228        if (ret)
 229                goto out;
 230
 231        return ret;
 232
 233out:
 234        for_each_possible_cpu(i)
 235                kfree(all_cpu_data[i]);
 236
 237        kfree(all_cpu_data);
 238        return -ENODEV;
 239}
 240
 241static void __exit cppc_cpufreq_exit(void)
 242{
 243        struct cppc_cpudata *cpu;
 244        int i;
 245
 246        cpufreq_unregister_driver(&cppc_cpufreq_driver);
 247
 248        for_each_possible_cpu(i) {
 249                cpu = all_cpu_data[i];
 250                free_cpumask_var(cpu->shared_cpu_map);
 251                kfree(cpu);
 252        }
 253
 254        kfree(all_cpu_data);
 255}
 256
 257module_exit(cppc_cpufreq_exit);
 258MODULE_AUTHOR("Ashwin Chaugule");
 259MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec");
 260MODULE_LICENSE("GPL");
 261
 262late_initcall(cppc_cpufreq_init);
 263
 264static const struct acpi_device_id cppc_acpi_ids[] = {
 265        {ACPI_PROCESSOR_DEVICE_HID, },
 266        {}
 267};
 268
 269MODULE_DEVICE_TABLE(acpi, cppc_acpi_ids);
 270