.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * CPPC (Collaborative Processor Performance Control) driver for |
---|
3 | 4 | * interfacing with the CPUfreq layer and governors. See |
---|
.. | .. |
---|
5 | 6 | * |
---|
6 | 7 | * (C) Copyright 2014, 2015 Linaro Ltd. |
---|
7 | 8 | * 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 | 9 | */ |
---|
14 | 10 | |
---|
15 | 11 | #define pr_fmt(fmt) "CPPC Cpufreq:" fmt |
---|
.. | .. |
---|
41 | 37 | * requested etc. |
---|
42 | 38 | */ |
---|
43 | 39 | static struct cppc_cpudata **all_cpu_data; |
---|
| 40 | +static bool boost_supported; |
---|
| 41 | + |
---|
| 42 | +struct cppc_workaround_oem_info { |
---|
| 43 | + char oem_id[ACPI_OEM_ID_SIZE + 1]; |
---|
| 44 | + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; |
---|
| 45 | + u32 oem_revision; |
---|
| 46 | +}; |
---|
| 47 | + |
---|
| 48 | +static struct cppc_workaround_oem_info wa_info[] = { |
---|
| 49 | + { |
---|
| 50 | + .oem_id = "HISI ", |
---|
| 51 | + .oem_table_id = "HIP07 ", |
---|
| 52 | + .oem_revision = 0, |
---|
| 53 | + }, { |
---|
| 54 | + .oem_id = "HISI ", |
---|
| 55 | + .oem_table_id = "HIP08 ", |
---|
| 56 | + .oem_revision = 0, |
---|
| 57 | + } |
---|
| 58 | +}; |
---|
44 | 59 | |
---|
45 | 60 | /* Callback function used to retrieve the max frequency from DMI */ |
---|
46 | 61 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) |
---|
.. | .. |
---|
100 | 115 | if (!max_khz) |
---|
101 | 116 | max_khz = cppc_get_dmi_max_khz(); |
---|
102 | 117 | mul = max_khz; |
---|
103 | | - div = cpu->perf_caps.highest_perf; |
---|
| 118 | + div = caps->highest_perf; |
---|
104 | 119 | } |
---|
105 | 120 | return (u64)perf * mul / div; |
---|
106 | 121 | } |
---|
.. | .. |
---|
123 | 138 | } else { |
---|
124 | 139 | if (!max_khz) |
---|
125 | 140 | max_khz = cppc_get_dmi_max_khz(); |
---|
126 | | - mul = cpu->perf_caps.highest_perf; |
---|
| 141 | + mul = caps->highest_perf; |
---|
127 | 142 | div = max_khz; |
---|
128 | 143 | } |
---|
129 | 144 | |
---|
.. | .. |
---|
161 | 176 | return ret; |
---|
162 | 177 | } |
---|
163 | 178 | |
---|
164 | | -static int cppc_verify_policy(struct cpufreq_policy *policy) |
---|
| 179 | +static int cppc_verify_policy(struct cpufreq_policy_data *policy) |
---|
165 | 180 | { |
---|
166 | 181 | cpufreq_verify_within_cpu_limits(policy); |
---|
167 | 182 | return 0; |
---|
.. | .. |
---|
250 | 265 | * Section 8.4.7.1.1.5 of ACPI 6.1 spec) |
---|
251 | 266 | */ |
---|
252 | 267 | policy->min = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_nonlinear_perf); |
---|
253 | | - policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf); |
---|
| 268 | + policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.nominal_perf); |
---|
254 | 269 | |
---|
255 | 270 | /* |
---|
256 | 271 | * Set cpuinfo.min_freq to Lowest to make the full range of performance |
---|
.. | .. |
---|
258 | 273 | * nonlinear perf |
---|
259 | 274 | */ |
---|
260 | 275 | policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_perf); |
---|
261 | | - policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf); |
---|
| 276 | + policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.nominal_perf); |
---|
262 | 277 | |
---|
263 | 278 | policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu_num); |
---|
264 | 279 | policy->shared_type = cpu->shared_type; |
---|
.. | .. |
---|
282 | 297 | } |
---|
283 | 298 | |
---|
284 | 299 | cpu->cur_policy = policy; |
---|
| 300 | + |
---|
| 301 | + /* |
---|
| 302 | + * If 'highest_perf' is greater than 'nominal_perf', we assume CPU Boost |
---|
| 303 | + * is supported. |
---|
| 304 | + */ |
---|
| 305 | + if (cpu->perf_caps.highest_perf > cpu->perf_caps.nominal_perf) |
---|
| 306 | + boost_supported = true; |
---|
285 | 307 | |
---|
286 | 308 | /* Set policy->cur to max now. The governors will adjust later. */ |
---|
287 | 309 | policy->cur = cppc_cpufreq_perf_to_khz(cpu, |
---|
.. | .. |
---|
347 | 369 | return cppc_get_rate_from_fbctrs(cpu, fb_ctrs_t0, fb_ctrs_t1); |
---|
348 | 370 | } |
---|
349 | 371 | |
---|
| 372 | +static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state) |
---|
| 373 | +{ |
---|
| 374 | + struct cppc_cpudata *cpudata; |
---|
| 375 | + int ret; |
---|
| 376 | + |
---|
| 377 | + if (!boost_supported) { |
---|
| 378 | + pr_err("BOOST not supported by CPU or firmware\n"); |
---|
| 379 | + return -EINVAL; |
---|
| 380 | + } |
---|
| 381 | + |
---|
| 382 | + cpudata = all_cpu_data[policy->cpu]; |
---|
| 383 | + if (state) |
---|
| 384 | + policy->max = cppc_cpufreq_perf_to_khz(cpudata, |
---|
| 385 | + cpudata->perf_caps.highest_perf); |
---|
| 386 | + else |
---|
| 387 | + policy->max = cppc_cpufreq_perf_to_khz(cpudata, |
---|
| 388 | + cpudata->perf_caps.nominal_perf); |
---|
| 389 | + policy->cpuinfo.max_freq = policy->max; |
---|
| 390 | + |
---|
| 391 | + ret = freq_qos_update_request(policy->max_freq_req, policy->max); |
---|
| 392 | + if (ret < 0) |
---|
| 393 | + return ret; |
---|
| 394 | + |
---|
| 395 | + return 0; |
---|
| 396 | +} |
---|
| 397 | + |
---|
350 | 398 | static struct cpufreq_driver cppc_cpufreq_driver = { |
---|
351 | 399 | .flags = CPUFREQ_CONST_LOOPS, |
---|
352 | 400 | .verify = cppc_verify_policy, |
---|
.. | .. |
---|
354 | 402 | .get = cppc_cpufreq_get_rate, |
---|
355 | 403 | .init = cppc_cpufreq_cpu_init, |
---|
356 | 404 | .stop_cpu = cppc_cpufreq_stop_cpu, |
---|
| 405 | + .set_boost = cppc_cpufreq_set_boost, |
---|
357 | 406 | .name = "cppc_cpufreq", |
---|
358 | 407 | }; |
---|
| 408 | + |
---|
| 409 | +/* |
---|
| 410 | + * HISI platform does not support delivered performance counter and |
---|
| 411 | + * reference performance counter. It can calculate the performance using the |
---|
| 412 | + * platform specific mechanism. We reuse the desired performance register to |
---|
| 413 | + * store the real performance calculated by the platform. |
---|
| 414 | + */ |
---|
| 415 | +static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum) |
---|
| 416 | +{ |
---|
| 417 | + struct cppc_cpudata *cpudata = all_cpu_data[cpunum]; |
---|
| 418 | + u64 desired_perf; |
---|
| 419 | + int ret; |
---|
| 420 | + |
---|
| 421 | + ret = cppc_get_desired_perf(cpunum, &desired_perf); |
---|
| 422 | + if (ret < 0) |
---|
| 423 | + return -EIO; |
---|
| 424 | + |
---|
| 425 | + return cppc_cpufreq_perf_to_khz(cpudata, desired_perf); |
---|
| 426 | +} |
---|
| 427 | + |
---|
| 428 | +static void cppc_check_hisi_workaround(void) |
---|
| 429 | +{ |
---|
| 430 | + struct acpi_table_header *tbl; |
---|
| 431 | + acpi_status status = AE_OK; |
---|
| 432 | + int i; |
---|
| 433 | + |
---|
| 434 | + status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); |
---|
| 435 | + if (ACPI_FAILURE(status) || !tbl) |
---|
| 436 | + return; |
---|
| 437 | + |
---|
| 438 | + for (i = 0; i < ARRAY_SIZE(wa_info); i++) { |
---|
| 439 | + if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && |
---|
| 440 | + !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && |
---|
| 441 | + wa_info[i].oem_revision == tbl->oem_revision) { |
---|
| 442 | + /* Overwrite the get() callback */ |
---|
| 443 | + cppc_cpufreq_driver.get = hisi_cppc_cpufreq_get_rate; |
---|
| 444 | + break; |
---|
| 445 | + } |
---|
| 446 | + } |
---|
| 447 | + |
---|
| 448 | + acpi_put_table(tbl); |
---|
| 449 | +} |
---|
359 | 450 | |
---|
360 | 451 | static int __init cppc_cpufreq_init(void) |
---|
361 | 452 | { |
---|
.. | .. |
---|
385 | 476 | pr_debug("Error parsing PSD data. Aborting cpufreq registration.\n"); |
---|
386 | 477 | goto out; |
---|
387 | 478 | } |
---|
| 479 | + |
---|
| 480 | + cppc_check_hisi_workaround(); |
---|
388 | 481 | |
---|
389 | 482 | ret = cpufreq_register_driver(&cppc_cpufreq_driver); |
---|
390 | 483 | if (ret) |
---|
.. | .. |
---|
428 | 521 | |
---|
429 | 522 | late_initcall(cppc_cpufreq_init); |
---|
430 | 523 | |
---|
431 | | -static const struct acpi_device_id cppc_acpi_ids[] = { |
---|
| 524 | +static const struct acpi_device_id cppc_acpi_ids[] __used = { |
---|
432 | 525 | {ACPI_PROCESSOR_DEVICE_HID, }, |
---|
433 | 526 | {} |
---|
434 | 527 | }; |
---|