linux/drivers/cpufreq/ia64-acpi-cpufreq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * This file provides the ACPI based P-state support. This
   4 * module works with generic cpufreq infrastructure. Most of
   5 * the code is based on i386 version
   6 * (arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c)
   7 *
   8 * Copyright (C) 2005 Intel Corp
   9 *      Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
  10 */
  11
  12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13
  14#include <linux/kernel.h>
  15#include <linux/slab.h>
  16#include <linux/module.h>
  17#include <linux/init.h>
  18#include <linux/cpufreq.h>
  19#include <linux/proc_fs.h>
  20#include <asm/io.h>
  21#include <linux/uaccess.h>
  22#include <asm/pal.h>
  23
  24#include <linux/acpi.h>
  25#include <acpi/processor.h>
  26
  27MODULE_AUTHOR("Venkatesh Pallipadi");
  28MODULE_DESCRIPTION("ACPI Processor P-States Driver");
  29MODULE_LICENSE("GPL");
  30
  31struct cpufreq_acpi_io {
  32        struct acpi_processor_performance       acpi_data;
  33        unsigned int                            resume;
  34};
  35
  36struct cpufreq_acpi_req {
  37        unsigned int            cpu;
  38        unsigned int            state;
  39};
  40
  41static struct cpufreq_acpi_io   *acpi_io_data[NR_CPUS];
  42
  43static struct cpufreq_driver acpi_cpufreq_driver;
  44
  45
  46static int
  47processor_set_pstate (
  48        u32     value)
  49{
  50        s64 retval;
  51
  52        pr_debug("processor_set_pstate\n");
  53
  54        retval = ia64_pal_set_pstate((u64)value);
  55
  56        if (retval) {
  57                pr_debug("Failed to set freq to 0x%x, with error 0x%llx\n",
  58                        value, retval);
  59                return -ENODEV;
  60        }
  61        return (int)retval;
  62}
  63
  64
  65static int
  66processor_get_pstate (
  67        u32     *value)
  68{
  69        u64     pstate_index = 0;
  70        s64     retval;
  71
  72        pr_debug("processor_get_pstate\n");
  73
  74        retval = ia64_pal_get_pstate(&pstate_index,
  75                                     PAL_GET_PSTATE_TYPE_INSTANT);
  76        *value = (u32) pstate_index;
  77
  78        if (retval)
  79                pr_debug("Failed to get current freq with "
  80                        "error 0x%llx, idx 0x%x\n", retval, *value);
  81
  82        return (int)retval;
  83}
  84
  85
  86/* To be used only after data->acpi_data is initialized */
  87static unsigned
  88extract_clock (
  89        struct cpufreq_acpi_io *data,
  90        unsigned value)
  91{
  92        unsigned long i;
  93
  94        pr_debug("extract_clock\n");
  95
  96        for (i = 0; i < data->acpi_data.state_count; i++) {
  97                if (value == data->acpi_data.states[i].status)
  98                        return data->acpi_data.states[i].core_frequency;
  99        }
 100        return data->acpi_data.states[i-1].core_frequency;
 101}
 102
 103
 104static long
 105processor_get_freq (
 106        void *arg)
 107{
 108        struct cpufreq_acpi_req *req = arg;
 109        unsigned int            cpu = req->cpu;
 110        struct cpufreq_acpi_io  *data = acpi_io_data[cpu];
 111        u32                     value;
 112        int                     ret;
 113
 114        pr_debug("processor_get_freq\n");
 115        if (smp_processor_id() != cpu)
 116                return -EAGAIN;
 117
 118        /* processor_get_pstate gets the instantaneous frequency */
 119        ret = processor_get_pstate(&value);
 120        if (ret) {
 121                pr_warn("get performance failed with error %d\n", ret);
 122                return ret;
 123        }
 124        return 1000 * extract_clock(data, value);
 125}
 126
 127
 128static long
 129processor_set_freq (
 130        void *arg)
 131{
 132        struct cpufreq_acpi_req *req = arg;
 133        unsigned int            cpu = req->cpu;
 134        struct cpufreq_acpi_io  *data = acpi_io_data[cpu];
 135        int                     ret, state = req->state;
 136        u32                     value;
 137
 138        pr_debug("processor_set_freq\n");
 139        if (smp_processor_id() != cpu)
 140                return -EAGAIN;
 141
 142        if (state == data->acpi_data.state) {
 143                if (unlikely(data->resume)) {
 144                        pr_debug("Called after resume, resetting to P%d\n", state);
 145                        data->resume = 0;
 146                } else {
 147                        pr_debug("Already at target state (P%d)\n", state);
 148                        return 0;
 149                }
 150        }
 151
 152        pr_debug("Transitioning from P%d to P%d\n",
 153                data->acpi_data.state, state);
 154
 155        /*
 156         * First we write the target state's 'control' value to the
 157         * control_register.
 158         */
 159        value = (u32) data->acpi_data.states[state].control;
 160
 161        pr_debug("Transitioning to state: 0x%08x\n", value);
 162
 163        ret = processor_set_pstate(value);
 164        if (ret) {
 165                pr_warn("Transition failed with error %d\n", ret);
 166                return -ENODEV;
 167        }
 168
 169        data->acpi_data.state = state;
 170        return 0;
 171}
 172
 173
 174static unsigned int
 175acpi_cpufreq_get (
 176        unsigned int            cpu)
 177{
 178        struct cpufreq_acpi_req req;
 179        long ret;
 180
 181        req.cpu = cpu;
 182        ret = work_on_cpu(cpu, processor_get_freq, &req);
 183
 184        return ret > 0 ? (unsigned int) ret : 0;
 185}
 186
 187
 188static int
 189acpi_cpufreq_target (
 190        struct cpufreq_policy   *policy,
 191        unsigned int index)
 192{
 193        struct cpufreq_acpi_req req;
 194
 195        req.cpu = policy->cpu;
 196        req.state = index;
 197
 198        return work_on_cpu(req.cpu, processor_set_freq, &req);
 199}
 200
 201static int
 202acpi_cpufreq_cpu_init (
 203        struct cpufreq_policy   *policy)
 204{
 205        unsigned int            i;
 206        unsigned int            cpu = policy->cpu;
 207        struct cpufreq_acpi_io  *data;
 208        unsigned int            result = 0;
 209        struct cpufreq_frequency_table *freq_table;
 210
 211        pr_debug("acpi_cpufreq_cpu_init\n");
 212
 213        data = kzalloc(sizeof(*data), GFP_KERNEL);
 214        if (!data)
 215                return (-ENOMEM);
 216
 217        acpi_io_data[cpu] = data;
 218
 219        result = acpi_processor_register_performance(&data->acpi_data, cpu);
 220
 221        if (result)
 222                goto err_free;
 223
 224        /* capability check */
 225        if (data->acpi_data.state_count <= 1) {
 226                pr_debug("No P-States\n");
 227                result = -ENODEV;
 228                goto err_unreg;
 229        }
 230
 231        if ((data->acpi_data.control_register.space_id !=
 232                                        ACPI_ADR_SPACE_FIXED_HARDWARE) ||
 233            (data->acpi_data.status_register.space_id !=
 234                                        ACPI_ADR_SPACE_FIXED_HARDWARE)) {
 235                pr_debug("Unsupported address space [%d, %d]\n",
 236                        (u32) (data->acpi_data.control_register.space_id),
 237                        (u32) (data->acpi_data.status_register.space_id));
 238                result = -ENODEV;
 239                goto err_unreg;
 240        }
 241
 242        /* alloc freq_table */
 243        freq_table = kcalloc(data->acpi_data.state_count + 1,
 244                                   sizeof(*freq_table),
 245                                   GFP_KERNEL);
 246        if (!freq_table) {
 247                result = -ENOMEM;
 248                goto err_unreg;
 249        }
 250
 251        /* detect transition latency */
 252        policy->cpuinfo.transition_latency = 0;
 253        for (i=0; i<data->acpi_data.state_count; i++) {
 254                if ((data->acpi_data.states[i].transition_latency * 1000) >
 255                    policy->cpuinfo.transition_latency) {
 256                        policy->cpuinfo.transition_latency =
 257                            data->acpi_data.states[i].transition_latency * 1000;
 258                }
 259        }
 260
 261        /* table init */
 262        for (i = 0; i <= data->acpi_data.state_count; i++)
 263        {
 264                if (i < data->acpi_data.state_count) {
 265                        freq_table[i].frequency =
 266                              data->acpi_data.states[i].core_frequency * 1000;
 267                } else {
 268                        freq_table[i].frequency = CPUFREQ_TABLE_END;
 269                }
 270        }
 271
 272        policy->freq_table = freq_table;
 273
 274        /* notify BIOS that we exist */
 275        acpi_processor_notify_smm(THIS_MODULE);
 276
 277        pr_info("CPU%u - ACPI performance management activated\n", cpu);
 278
 279        for (i = 0; i < data->acpi_data.state_count; i++)
 280                pr_debug("     %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n",
 281                        (i == data->acpi_data.state?'*':' '), i,
 282                        (u32) data->acpi_data.states[i].core_frequency,
 283                        (u32) data->acpi_data.states[i].power,
 284                        (u32) data->acpi_data.states[i].transition_latency,
 285                        (u32) data->acpi_data.states[i].bus_master_latency,
 286                        (u32) data->acpi_data.states[i].status,
 287                        (u32) data->acpi_data.states[i].control);
 288
 289        /* the first call to ->target() should result in us actually
 290         * writing something to the appropriate registers. */
 291        data->resume = 1;
 292
 293        return (result);
 294
 295 err_unreg:
 296        acpi_processor_unregister_performance(cpu);
 297 err_free:
 298        kfree(data);
 299        acpi_io_data[cpu] = NULL;
 300
 301        return (result);
 302}
 303
 304
 305static int
 306acpi_cpufreq_cpu_exit (
 307        struct cpufreq_policy   *policy)
 308{
 309        struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
 310
 311        pr_debug("acpi_cpufreq_cpu_exit\n");
 312
 313        if (data) {
 314                acpi_io_data[policy->cpu] = NULL;
 315                acpi_processor_unregister_performance(policy->cpu);
 316                kfree(policy->freq_table);
 317                kfree(data);
 318        }
 319
 320        return (0);
 321}
 322
 323
 324static struct cpufreq_driver acpi_cpufreq_driver = {
 325        .verify         = cpufreq_generic_frequency_table_verify,
 326        .target_index   = acpi_cpufreq_target,
 327        .get            = acpi_cpufreq_get,
 328        .init           = acpi_cpufreq_cpu_init,
 329        .exit           = acpi_cpufreq_cpu_exit,
 330        .name           = "acpi-cpufreq",
 331        .attr           = cpufreq_generic_attr,
 332};
 333
 334
 335static int __init
 336acpi_cpufreq_init (void)
 337{
 338        pr_debug("acpi_cpufreq_init\n");
 339
 340        return cpufreq_register_driver(&acpi_cpufreq_driver);
 341}
 342
 343
 344static void __exit
 345acpi_cpufreq_exit (void)
 346{
 347        pr_debug("acpi_cpufreq_exit\n");
 348
 349        cpufreq_unregister_driver(&acpi_cpufreq_driver);
 350}
 351
 352late_initcall(acpi_cpufreq_init);
 353module_exit(acpi_cpufreq_exit);
 354