162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CPPC (Collaborative Processor Performance Control) driver for 462306a36Sopenharmony_ci * interfacing with the CPUfreq layer and governors. See 562306a36Sopenharmony_ci * cppc_acpi.c for CPPC specific methods. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) Copyright 2014, 2015 Linaro Ltd. 862306a36Sopenharmony_ci * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) "CPPC Cpufreq:" fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/arch_topology.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/cpu.h> 1862306a36Sopenharmony_ci#include <linux/cpufreq.h> 1962306a36Sopenharmony_ci#include <linux/dmi.h> 2062306a36Sopenharmony_ci#include <linux/irq_work.h> 2162306a36Sopenharmony_ci#include <linux/kthread.h> 2262306a36Sopenharmony_ci#include <linux/time.h> 2362306a36Sopenharmony_ci#include <linux/vmalloc.h> 2462306a36Sopenharmony_ci#include <uapi/linux/sched/types.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/unaligned.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <acpi/cppc_acpi.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Minimum struct length needed for the DMI processor entry we want */ 3162306a36Sopenharmony_ci#define DMI_ENTRY_PROCESSOR_MIN_LENGTH 48 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Offset in the DMI processor structure for the max frequency */ 3462306a36Sopenharmony_ci#define DMI_PROCESSOR_MAX_SPEED 0x14 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * This list contains information parsed from per CPU ACPI _CPC and _PSD 3862306a36Sopenharmony_ci * structures: e.g. the highest and lowest supported performance, capabilities, 3962306a36Sopenharmony_ci * desired performance, level requested etc. Depending on the share_type, not 4062306a36Sopenharmony_ci * all CPUs will have an entry in the list. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_cistatic LIST_HEAD(cpu_data_list); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic bool boost_supported; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct cppc_workaround_oem_info { 4762306a36Sopenharmony_ci char oem_id[ACPI_OEM_ID_SIZE + 1]; 4862306a36Sopenharmony_ci char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; 4962306a36Sopenharmony_ci u32 oem_revision; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct cppc_workaround_oem_info wa_info[] = { 5362306a36Sopenharmony_ci { 5462306a36Sopenharmony_ci .oem_id = "HISI ", 5562306a36Sopenharmony_ci .oem_table_id = "HIP07 ", 5662306a36Sopenharmony_ci .oem_revision = 0, 5762306a36Sopenharmony_ci }, { 5862306a36Sopenharmony_ci .oem_id = "HISI ", 5962306a36Sopenharmony_ci .oem_table_id = "HIP08 ", 6062306a36Sopenharmony_ci .oem_revision = 0, 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct cpufreq_driver cppc_cpufreq_driver; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic enum { 6762306a36Sopenharmony_ci FIE_UNSET = -1, 6862306a36Sopenharmony_ci FIE_ENABLED, 6962306a36Sopenharmony_ci FIE_DISABLED 7062306a36Sopenharmony_ci} fie_disabled = FIE_UNSET; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE 7362306a36Sopenharmony_cimodule_param(fie_disabled, int, 0444); 7462306a36Sopenharmony_ciMODULE_PARM_DESC(fie_disabled, "Disable Frequency Invariance Engine (FIE)"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Frequency invariance support */ 7762306a36Sopenharmony_cistruct cppc_freq_invariance { 7862306a36Sopenharmony_ci int cpu; 7962306a36Sopenharmony_ci struct irq_work irq_work; 8062306a36Sopenharmony_ci struct kthread_work work; 8162306a36Sopenharmony_ci struct cppc_perf_fb_ctrs prev_perf_fb_ctrs; 8262306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv); 8662306a36Sopenharmony_cistatic struct kthread_worker *kworker_fie; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu); 8962306a36Sopenharmony_cistatic int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, 9062306a36Sopenharmony_ci struct cppc_perf_fb_ctrs *fb_ctrs_t0, 9162306a36Sopenharmony_ci struct cppc_perf_fb_ctrs *fb_ctrs_t1); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/** 9462306a36Sopenharmony_ci * cppc_scale_freq_workfn - CPPC arch_freq_scale updater for frequency invariance 9562306a36Sopenharmony_ci * @work: The work item. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * The CPPC driver register itself with the topology core to provide its own 9862306a36Sopenharmony_ci * implementation (cppc_scale_freq_tick()) of topology_scale_freq_tick() which 9962306a36Sopenharmony_ci * gets called by the scheduler on every tick. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Note that the arch specific counters have higher priority than CPPC counters, 10262306a36Sopenharmony_ci * if available, though the CPPC driver doesn't need to have any special 10362306a36Sopenharmony_ci * handling for that. 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * On an invocation of cppc_scale_freq_tick(), we schedule an irq work (since we 10662306a36Sopenharmony_ci * reach here from hard-irq context), which then schedules a normal work item 10762306a36Sopenharmony_ci * and cppc_scale_freq_workfn() updates the per_cpu arch_freq_scale variable 10862306a36Sopenharmony_ci * based on the counter updates since the last tick. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic void cppc_scale_freq_workfn(struct kthread_work *work) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct cppc_freq_invariance *cppc_fi; 11362306a36Sopenharmony_ci struct cppc_perf_fb_ctrs fb_ctrs = {0}; 11462306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 11562306a36Sopenharmony_ci unsigned long local_freq_scale; 11662306a36Sopenharmony_ci u64 perf; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci cppc_fi = container_of(work, struct cppc_freq_invariance, work); 11962306a36Sopenharmony_ci cpu_data = cppc_fi->cpu_data; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (cppc_get_perf_ctrs(cppc_fi->cpu, &fb_ctrs)) { 12262306a36Sopenharmony_ci pr_warn("%s: failed to read perf counters\n", __func__); 12362306a36Sopenharmony_ci return; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci perf = cppc_perf_from_fbctrs(cpu_data, &cppc_fi->prev_perf_fb_ctrs, 12762306a36Sopenharmony_ci &fb_ctrs); 12862306a36Sopenharmony_ci cppc_fi->prev_perf_fb_ctrs = fb_ctrs; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci perf <<= SCHED_CAPACITY_SHIFT; 13162306a36Sopenharmony_ci local_freq_scale = div64_u64(perf, cpu_data->perf_caps.highest_perf); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* This can happen due to counter's overflow */ 13462306a36Sopenharmony_ci if (unlikely(local_freq_scale > 1024)) 13562306a36Sopenharmony_ci local_freq_scale = 1024; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci per_cpu(arch_freq_scale, cppc_fi->cpu) = local_freq_scale; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void cppc_irq_work(struct irq_work *irq_work) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct cppc_freq_invariance *cppc_fi; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci cppc_fi = container_of(irq_work, struct cppc_freq_invariance, irq_work); 14562306a36Sopenharmony_ci kthread_queue_work(kworker_fie, &cppc_fi->work); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void cppc_scale_freq_tick(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct cppc_freq_invariance *cppc_fi = &per_cpu(cppc_freq_inv, smp_processor_id()); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * cppc_get_perf_ctrs() can potentially sleep, call that from the right 15462306a36Sopenharmony_ci * context. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci irq_work_queue(&cppc_fi->irq_work); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic struct scale_freq_data cppc_sftd = { 16062306a36Sopenharmony_ci .source = SCALE_FREQ_SOURCE_CPPC, 16162306a36Sopenharmony_ci .set_freq_scale = cppc_scale_freq_tick, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct cppc_freq_invariance *cppc_fi; 16762306a36Sopenharmony_ci int cpu, ret; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (fie_disabled) 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for_each_cpu(cpu, policy->cpus) { 17362306a36Sopenharmony_ci cppc_fi = &per_cpu(cppc_freq_inv, cpu); 17462306a36Sopenharmony_ci cppc_fi->cpu = cpu; 17562306a36Sopenharmony_ci cppc_fi->cpu_data = policy->driver_data; 17662306a36Sopenharmony_ci kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn); 17762306a36Sopenharmony_ci init_irq_work(&cppc_fi->irq_work, cppc_irq_work); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs); 18062306a36Sopenharmony_ci if (ret) { 18162306a36Sopenharmony_ci pr_warn("%s: failed to read perf counters for cpu:%d: %d\n", 18262306a36Sopenharmony_ci __func__, cpu, ret); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * Don't abort if the CPU was offline while the driver 18662306a36Sopenharmony_ci * was getting registered. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci if (cpu_online(cpu)) 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Register for freq-invariance */ 19462306a36Sopenharmony_ci topology_set_scale_freq_source(&cppc_sftd, policy->cpus); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* 19862306a36Sopenharmony_ci * We free all the resources on policy's removal and not on CPU removal as the 19962306a36Sopenharmony_ci * irq-work are per-cpu and the hotplug core takes care of flushing the pending 20062306a36Sopenharmony_ci * irq-works (hint: smpcfd_dying_cpu()) on CPU hotplug. Even if the kthread-work 20162306a36Sopenharmony_ci * fires on another CPU after the concerned CPU is removed, it won't harm. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * We just need to make sure to remove them all on policy->exit(). 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistatic void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct cppc_freq_invariance *cppc_fi; 20862306a36Sopenharmony_ci int cpu; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (fie_disabled) 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* policy->cpus will be empty here, use related_cpus instead */ 21462306a36Sopenharmony_ci topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_CPPC, policy->related_cpus); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for_each_cpu(cpu, policy->related_cpus) { 21762306a36Sopenharmony_ci cppc_fi = &per_cpu(cppc_freq_inv, cpu); 21862306a36Sopenharmony_ci irq_work_sync(&cppc_fi->irq_work); 21962306a36Sopenharmony_ci kthread_cancel_work_sync(&cppc_fi->work); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void __init cppc_freq_invariance_init(void) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct sched_attr attr = { 22662306a36Sopenharmony_ci .size = sizeof(struct sched_attr), 22762306a36Sopenharmony_ci .sched_policy = SCHED_DEADLINE, 22862306a36Sopenharmony_ci .sched_nice = 0, 22962306a36Sopenharmony_ci .sched_priority = 0, 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * Fake (unused) bandwidth; workaround to "fix" 23262306a36Sopenharmony_ci * priority inheritance. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci .sched_runtime = 1000000, 23562306a36Sopenharmony_ci .sched_deadline = 10000000, 23662306a36Sopenharmony_ci .sched_period = 10000000, 23762306a36Sopenharmony_ci }; 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (fie_disabled != FIE_ENABLED && fie_disabled != FIE_DISABLED) { 24162306a36Sopenharmony_ci fie_disabled = FIE_ENABLED; 24262306a36Sopenharmony_ci if (cppc_perf_ctrs_in_pcc()) { 24362306a36Sopenharmony_ci pr_info("FIE not enabled on systems with registers in PCC\n"); 24462306a36Sopenharmony_ci fie_disabled = FIE_DISABLED; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (fie_disabled) 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci kworker_fie = kthread_create_worker(0, "cppc_fie"); 25262306a36Sopenharmony_ci if (IS_ERR(kworker_fie)) { 25362306a36Sopenharmony_ci pr_warn("%s: failed to create kworker_fie: %ld\n", __func__, 25462306a36Sopenharmony_ci PTR_ERR(kworker_fie)); 25562306a36Sopenharmony_ci fie_disabled = FIE_DISABLED; 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ret = sched_setattr_nocheck(kworker_fie->task, &attr); 26062306a36Sopenharmony_ci if (ret) { 26162306a36Sopenharmony_ci pr_warn("%s: failed to set SCHED_DEADLINE: %d\n", __func__, 26262306a36Sopenharmony_ci ret); 26362306a36Sopenharmony_ci kthread_destroy_worker(kworker_fie); 26462306a36Sopenharmony_ci fie_disabled = FIE_DISABLED; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void cppc_freq_invariance_exit(void) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci if (fie_disabled) 27162306a36Sopenharmony_ci return; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci kthread_destroy_worker(kworker_fie); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci#else 27762306a36Sopenharmony_cistatic inline void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic inline void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic inline void cppc_freq_invariance_init(void) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic inline void cppc_freq_invariance_exit(void) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci#endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* Callback function used to retrieve the max frequency from DMI */ 29562306a36Sopenharmony_cistatic void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci const u8 *dmi_data = (const u8 *)dm; 29862306a36Sopenharmony_ci u16 *mhz = (u16 *)private; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (dm->type == DMI_ENTRY_PROCESSOR && 30162306a36Sopenharmony_ci dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) { 30262306a36Sopenharmony_ci u16 val = (u16)get_unaligned((const u16 *) 30362306a36Sopenharmony_ci (dmi_data + DMI_PROCESSOR_MAX_SPEED)); 30462306a36Sopenharmony_ci *mhz = val > *mhz ? val : *mhz; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* Look up the max frequency in DMI */ 30962306a36Sopenharmony_cistatic u64 cppc_get_dmi_max_khz(void) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci u16 mhz = 0; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci dmi_walk(cppc_find_dmi_mhz, &mhz); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * Real stupid fallback value, just in case there is no 31762306a36Sopenharmony_ci * actual value set. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci mhz = mhz ? mhz : 1; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return (1000 * mhz); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* 32562306a36Sopenharmony_ci * If CPPC lowest_freq and nominal_freq registers are exposed then we can 32662306a36Sopenharmony_ci * use them to convert perf to freq and vice versa. The conversion is 32762306a36Sopenharmony_ci * extrapolated as an affine function passing by the 2 points: 32862306a36Sopenharmony_ci * - (Low perf, Low freq) 32962306a36Sopenharmony_ci * - (Nominal perf, Nominal perf) 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_cistatic unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu_data, 33262306a36Sopenharmony_ci unsigned int perf) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct cppc_perf_caps *caps = &cpu_data->perf_caps; 33562306a36Sopenharmony_ci s64 retval, offset = 0; 33662306a36Sopenharmony_ci static u64 max_khz; 33762306a36Sopenharmony_ci u64 mul, div; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (caps->lowest_freq && caps->nominal_freq) { 34062306a36Sopenharmony_ci mul = caps->nominal_freq - caps->lowest_freq; 34162306a36Sopenharmony_ci div = caps->nominal_perf - caps->lowest_perf; 34262306a36Sopenharmony_ci offset = caps->nominal_freq - div64_u64(caps->nominal_perf * mul, div); 34362306a36Sopenharmony_ci } else { 34462306a36Sopenharmony_ci if (!max_khz) 34562306a36Sopenharmony_ci max_khz = cppc_get_dmi_max_khz(); 34662306a36Sopenharmony_ci mul = max_khz; 34762306a36Sopenharmony_ci div = caps->highest_perf; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci retval = offset + div64_u64(perf * mul, div); 35162306a36Sopenharmony_ci if (retval >= 0) 35262306a36Sopenharmony_ci return retval; 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu_data, 35762306a36Sopenharmony_ci unsigned int freq) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct cppc_perf_caps *caps = &cpu_data->perf_caps; 36062306a36Sopenharmony_ci s64 retval, offset = 0; 36162306a36Sopenharmony_ci static u64 max_khz; 36262306a36Sopenharmony_ci u64 mul, div; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (caps->lowest_freq && caps->nominal_freq) { 36562306a36Sopenharmony_ci mul = caps->nominal_perf - caps->lowest_perf; 36662306a36Sopenharmony_ci div = caps->nominal_freq - caps->lowest_freq; 36762306a36Sopenharmony_ci offset = caps->nominal_perf - div64_u64(caps->nominal_freq * mul, div); 36862306a36Sopenharmony_ci } else { 36962306a36Sopenharmony_ci if (!max_khz) 37062306a36Sopenharmony_ci max_khz = cppc_get_dmi_max_khz(); 37162306a36Sopenharmony_ci mul = caps->highest_perf; 37262306a36Sopenharmony_ci div = max_khz; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci retval = offset + div64_u64(freq * mul, div); 37662306a36Sopenharmony_ci if (retval >= 0) 37762306a36Sopenharmony_ci return retval; 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int cppc_cpufreq_set_target(struct cpufreq_policy *policy, 38262306a36Sopenharmony_ci unsigned int target_freq, 38362306a36Sopenharmony_ci unsigned int relation) 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 38762306a36Sopenharmony_ci unsigned int cpu = policy->cpu; 38862306a36Sopenharmony_ci struct cpufreq_freqs freqs; 38962306a36Sopenharmony_ci u32 desired_perf; 39062306a36Sopenharmony_ci int ret = 0; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci desired_perf = cppc_cpufreq_khz_to_perf(cpu_data, target_freq); 39362306a36Sopenharmony_ci /* Return if it is exactly the same perf */ 39462306a36Sopenharmony_ci if (desired_perf == cpu_data->perf_ctrls.desired_perf) 39562306a36Sopenharmony_ci return ret; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci cpu_data->perf_ctrls.desired_perf = desired_perf; 39862306a36Sopenharmony_ci freqs.old = policy->cur; 39962306a36Sopenharmony_ci freqs.new = target_freq; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci cpufreq_freq_transition_begin(policy, &freqs); 40262306a36Sopenharmony_ci ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); 40362306a36Sopenharmony_ci cpufreq_freq_transition_end(policy, &freqs, ret != 0); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (ret) 40662306a36Sopenharmony_ci pr_debug("Failed to set target on CPU:%d. ret:%d\n", 40762306a36Sopenharmony_ci cpu, ret); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic unsigned int cppc_cpufreq_fast_switch(struct cpufreq_policy *policy, 41362306a36Sopenharmony_ci unsigned int target_freq) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 41662306a36Sopenharmony_ci unsigned int cpu = policy->cpu; 41762306a36Sopenharmony_ci u32 desired_perf; 41862306a36Sopenharmony_ci int ret; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci desired_perf = cppc_cpufreq_khz_to_perf(cpu_data, target_freq); 42162306a36Sopenharmony_ci cpu_data->perf_ctrls.desired_perf = desired_perf; 42262306a36Sopenharmony_ci ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (ret) { 42562306a36Sopenharmony_ci pr_debug("Failed to set target on CPU:%d. ret:%d\n", 42662306a36Sopenharmony_ci cpu, ret); 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return target_freq; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int cppc_verify_policy(struct cpufreq_policy_data *policy) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci cpufreq_verify_within_cpu_limits(policy); 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * The PCC subspace describes the rate at which platform can accept commands 44162306a36Sopenharmony_ci * on the shared PCC channel (including READs which do not count towards freq 44262306a36Sopenharmony_ci * transition requests), so ideally we need to use the PCC values as a fallback 44362306a36Sopenharmony_ci * if we don't have a platform specific transition_delay_us 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci#ifdef CONFIG_ARM64 44662306a36Sopenharmony_ci#include <asm/cputype.h> 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci unsigned long implementor = read_cpuid_implementor(); 45162306a36Sopenharmony_ci unsigned long part_num = read_cpuid_part_number(); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci switch (implementor) { 45462306a36Sopenharmony_ci case ARM_CPU_IMP_QCOM: 45562306a36Sopenharmony_ci switch (part_num) { 45662306a36Sopenharmony_ci case QCOM_CPU_PART_FALKOR_V1: 45762306a36Sopenharmony_ci case QCOM_CPU_PART_FALKOR: 45862306a36Sopenharmony_ci return 10000; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci#else 46462306a36Sopenharmony_cistatic unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci#endif 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci#if defined(CONFIG_ARM64) && defined(CONFIG_ENERGY_MODEL) 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned int, efficiency_class); 47362306a36Sopenharmony_cistatic void cppc_cpufreq_register_em(struct cpufreq_policy *policy); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/* Create an artificial performance state every CPPC_EM_CAP_STEP capacity unit. */ 47662306a36Sopenharmony_ci#define CPPC_EM_CAP_STEP (20) 47762306a36Sopenharmony_ci/* Increase the cost value by CPPC_EM_COST_STEP every performance state. */ 47862306a36Sopenharmony_ci#define CPPC_EM_COST_STEP (1) 47962306a36Sopenharmony_ci/* Add a cost gap correspnding to the energy of 4 CPUs. */ 48062306a36Sopenharmony_ci#define CPPC_EM_COST_GAP (4 * SCHED_CAPACITY_SCALE * CPPC_EM_COST_STEP \ 48162306a36Sopenharmony_ci / CPPC_EM_CAP_STEP) 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic unsigned int get_perf_level_count(struct cpufreq_policy *policy) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct cppc_perf_caps *perf_caps; 48662306a36Sopenharmony_ci unsigned int min_cap, max_cap; 48762306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 48862306a36Sopenharmony_ci int cpu = policy->cpu; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci cpu_data = policy->driver_data; 49162306a36Sopenharmony_ci perf_caps = &cpu_data->perf_caps; 49262306a36Sopenharmony_ci max_cap = arch_scale_cpu_capacity(cpu); 49362306a36Sopenharmony_ci min_cap = div_u64((u64)max_cap * perf_caps->lowest_perf, 49462306a36Sopenharmony_ci perf_caps->highest_perf); 49562306a36Sopenharmony_ci if ((min_cap == 0) || (max_cap < min_cap)) 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci return 1 + max_cap / CPPC_EM_CAP_STEP - min_cap / CPPC_EM_CAP_STEP; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/* 50162306a36Sopenharmony_ci * The cost is defined as: 50262306a36Sopenharmony_ci * cost = power * max_frequency / frequency 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_cistatic inline unsigned long compute_cost(int cpu, int step) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci return CPPC_EM_COST_GAP * per_cpu(efficiency_class, cpu) + 50762306a36Sopenharmony_ci step * CPPC_EM_COST_STEP; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int cppc_get_cpu_power(struct device *cpu_dev, 51162306a36Sopenharmony_ci unsigned long *power, unsigned long *KHz) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci unsigned long perf_step, perf_prev, perf, perf_check; 51462306a36Sopenharmony_ci unsigned int min_step, max_step, step, step_check; 51562306a36Sopenharmony_ci unsigned long prev_freq = *KHz; 51662306a36Sopenharmony_ci unsigned int min_cap, max_cap; 51762306a36Sopenharmony_ci struct cpufreq_policy *policy; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci struct cppc_perf_caps *perf_caps; 52062306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci policy = cpufreq_cpu_get_raw(cpu_dev->id); 52362306a36Sopenharmony_ci cpu_data = policy->driver_data; 52462306a36Sopenharmony_ci perf_caps = &cpu_data->perf_caps; 52562306a36Sopenharmony_ci max_cap = arch_scale_cpu_capacity(cpu_dev->id); 52662306a36Sopenharmony_ci min_cap = div_u64((u64)max_cap * perf_caps->lowest_perf, 52762306a36Sopenharmony_ci perf_caps->highest_perf); 52862306a36Sopenharmony_ci perf_step = div_u64((u64)CPPC_EM_CAP_STEP * perf_caps->highest_perf, 52962306a36Sopenharmony_ci max_cap); 53062306a36Sopenharmony_ci min_step = min_cap / CPPC_EM_CAP_STEP; 53162306a36Sopenharmony_ci max_step = max_cap / CPPC_EM_CAP_STEP; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci perf_prev = cppc_cpufreq_khz_to_perf(cpu_data, *KHz); 53462306a36Sopenharmony_ci step = perf_prev / perf_step; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (step > max_step) 53762306a36Sopenharmony_ci return -EINVAL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (min_step == max_step) { 54062306a36Sopenharmony_ci step = max_step; 54162306a36Sopenharmony_ci perf = perf_caps->highest_perf; 54262306a36Sopenharmony_ci } else if (step < min_step) { 54362306a36Sopenharmony_ci step = min_step; 54462306a36Sopenharmony_ci perf = perf_caps->lowest_perf; 54562306a36Sopenharmony_ci } else { 54662306a36Sopenharmony_ci step++; 54762306a36Sopenharmony_ci if (step == max_step) 54862306a36Sopenharmony_ci perf = perf_caps->highest_perf; 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci perf = step * perf_step; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci *KHz = cppc_cpufreq_perf_to_khz(cpu_data, perf); 55462306a36Sopenharmony_ci perf_check = cppc_cpufreq_khz_to_perf(cpu_data, *KHz); 55562306a36Sopenharmony_ci step_check = perf_check / perf_step; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* 55862306a36Sopenharmony_ci * To avoid bad integer approximation, check that new frequency value 55962306a36Sopenharmony_ci * increased and that the new frequency will be converted to the 56062306a36Sopenharmony_ci * desired step value. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci while ((*KHz == prev_freq) || (step_check != step)) { 56362306a36Sopenharmony_ci perf++; 56462306a36Sopenharmony_ci *KHz = cppc_cpufreq_perf_to_khz(cpu_data, perf); 56562306a36Sopenharmony_ci perf_check = cppc_cpufreq_khz_to_perf(cpu_data, *KHz); 56662306a36Sopenharmony_ci step_check = perf_check / perf_step; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * With an artificial EM, only the cost value is used. Still the power 57162306a36Sopenharmony_ci * is populated such as 0 < power < EM_MAX_POWER. This allows to add 57262306a36Sopenharmony_ci * more sense to the artificial performance states. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci *power = compute_cost(cpu_dev->id, step); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int cppc_get_cpu_cost(struct device *cpu_dev, unsigned long KHz, 58062306a36Sopenharmony_ci unsigned long *cost) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci unsigned long perf_step, perf_prev; 58362306a36Sopenharmony_ci struct cppc_perf_caps *perf_caps; 58462306a36Sopenharmony_ci struct cpufreq_policy *policy; 58562306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 58662306a36Sopenharmony_ci unsigned int max_cap; 58762306a36Sopenharmony_ci int step; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci policy = cpufreq_cpu_get_raw(cpu_dev->id); 59062306a36Sopenharmony_ci cpu_data = policy->driver_data; 59162306a36Sopenharmony_ci perf_caps = &cpu_data->perf_caps; 59262306a36Sopenharmony_ci max_cap = arch_scale_cpu_capacity(cpu_dev->id); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci perf_prev = cppc_cpufreq_khz_to_perf(cpu_data, KHz); 59562306a36Sopenharmony_ci perf_step = CPPC_EM_CAP_STEP * perf_caps->highest_perf / max_cap; 59662306a36Sopenharmony_ci step = perf_prev / perf_step; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci *cost = compute_cost(cpu_dev->id, step); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int populate_efficiency_class(void) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct acpi_madt_generic_interrupt *gicc; 60662306a36Sopenharmony_ci DECLARE_BITMAP(used_classes, 256) = {}; 60762306a36Sopenharmony_ci int class, cpu, index; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 61062306a36Sopenharmony_ci gicc = acpi_cpu_get_madt_gicc(cpu); 61162306a36Sopenharmony_ci class = gicc->efficiency_class; 61262306a36Sopenharmony_ci bitmap_set(used_classes, class, 1); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (bitmap_weight(used_classes, 256) <= 1) { 61662306a36Sopenharmony_ci pr_debug("Efficiency classes are all equal (=%d). " 61762306a36Sopenharmony_ci "No EM registered", class); 61862306a36Sopenharmony_ci return -EINVAL; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * Squeeze efficiency class values on [0:#efficiency_class-1]. 62362306a36Sopenharmony_ci * Values are per spec in [0:255]. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci index = 0; 62662306a36Sopenharmony_ci for_each_set_bit(class, used_classes, 256) { 62762306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 62862306a36Sopenharmony_ci gicc = acpi_cpu_get_madt_gicc(cpu); 62962306a36Sopenharmony_ci if (gicc->efficiency_class == class) 63062306a36Sopenharmony_ci per_cpu(efficiency_class, cpu) = index; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci index++; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci cppc_cpufreq_driver.register_em = cppc_cpufreq_register_em; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return 0; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic void cppc_cpufreq_register_em(struct cpufreq_policy *policy) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 64262306a36Sopenharmony_ci struct em_data_callback em_cb = 64362306a36Sopenharmony_ci EM_ADV_DATA_CB(cppc_get_cpu_power, cppc_get_cpu_cost); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci cpu_data = policy->driver_data; 64662306a36Sopenharmony_ci em_dev_register_perf_domain(get_cpu_device(policy->cpu), 64762306a36Sopenharmony_ci get_perf_level_count(policy), &em_cb, 64862306a36Sopenharmony_ci cpu_data->shared_cpu_map, 0); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci#else 65262306a36Sopenharmony_cistatic int populate_efficiency_class(void) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci#endif 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 66162306a36Sopenharmony_ci int ret; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci cpu_data = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL); 66462306a36Sopenharmony_ci if (!cpu_data) 66562306a36Sopenharmony_ci goto out; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (!zalloc_cpumask_var(&cpu_data->shared_cpu_map, GFP_KERNEL)) 66862306a36Sopenharmony_ci goto free_cpu; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = acpi_get_psd_map(cpu, cpu_data); 67162306a36Sopenharmony_ci if (ret) { 67262306a36Sopenharmony_ci pr_debug("Err parsing CPU%d PSD data: ret:%d\n", cpu, ret); 67362306a36Sopenharmony_ci goto free_mask; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = cppc_get_perf_caps(cpu, &cpu_data->perf_caps); 67762306a36Sopenharmony_ci if (ret) { 67862306a36Sopenharmony_ci pr_debug("Err reading CPU%d perf caps: ret:%d\n", cpu, ret); 67962306a36Sopenharmony_ci goto free_mask; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Convert the lowest and nominal freq from MHz to KHz */ 68362306a36Sopenharmony_ci cpu_data->perf_caps.lowest_freq *= 1000; 68462306a36Sopenharmony_ci cpu_data->perf_caps.nominal_freq *= 1000; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci list_add(&cpu_data->node, &cpu_data_list); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return cpu_data; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cifree_mask: 69162306a36Sopenharmony_ci free_cpumask_var(cpu_data->shared_cpu_map); 69262306a36Sopenharmony_cifree_cpu: 69362306a36Sopenharmony_ci kfree(cpu_data); 69462306a36Sopenharmony_ciout: 69562306a36Sopenharmony_ci return NULL; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci list_del(&cpu_data->node); 70362306a36Sopenharmony_ci free_cpumask_var(cpu_data->shared_cpu_map); 70462306a36Sopenharmony_ci kfree(cpu_data); 70562306a36Sopenharmony_ci policy->driver_data = NULL; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci unsigned int cpu = policy->cpu; 71162306a36Sopenharmony_ci struct cppc_cpudata *cpu_data; 71262306a36Sopenharmony_ci struct cppc_perf_caps *caps; 71362306a36Sopenharmony_ci int ret; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci cpu_data = cppc_cpufreq_get_cpu_data(cpu); 71662306a36Sopenharmony_ci if (!cpu_data) { 71762306a36Sopenharmony_ci pr_err("Error in acquiring _CPC/_PSD data for CPU%d.\n", cpu); 71862306a36Sopenharmony_ci return -ENODEV; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci caps = &cpu_data->perf_caps; 72162306a36Sopenharmony_ci policy->driver_data = cpu_data; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * Set min to lowest nonlinear perf to avoid any efficiency penalty (see 72562306a36Sopenharmony_ci * Section 8.4.7.1.1.5 of ACPI 6.1 spec) 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci policy->min = cppc_cpufreq_perf_to_khz(cpu_data, 72862306a36Sopenharmony_ci caps->lowest_nonlinear_perf); 72962306a36Sopenharmony_ci policy->max = cppc_cpufreq_perf_to_khz(cpu_data, 73062306a36Sopenharmony_ci caps->nominal_perf); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* 73362306a36Sopenharmony_ci * Set cpuinfo.min_freq to Lowest to make the full range of performance 73462306a36Sopenharmony_ci * available if userspace wants to use any perf between lowest & lowest 73562306a36Sopenharmony_ci * nonlinear perf 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_ci policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu_data, 73862306a36Sopenharmony_ci caps->lowest_perf); 73962306a36Sopenharmony_ci policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu_data, 74062306a36Sopenharmony_ci caps->nominal_perf); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu); 74362306a36Sopenharmony_ci policy->shared_type = cpu_data->shared_type; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci switch (policy->shared_type) { 74662306a36Sopenharmony_ci case CPUFREQ_SHARED_TYPE_HW: 74762306a36Sopenharmony_ci case CPUFREQ_SHARED_TYPE_NONE: 74862306a36Sopenharmony_ci /* Nothing to be done - we'll have a policy for each CPU */ 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci case CPUFREQ_SHARED_TYPE_ANY: 75162306a36Sopenharmony_ci /* 75262306a36Sopenharmony_ci * All CPUs in the domain will share a policy and all cpufreq 75362306a36Sopenharmony_ci * operations will use a single cppc_cpudata structure stored 75462306a36Sopenharmony_ci * in policy->driver_data. 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci cpumask_copy(policy->cpus, cpu_data->shared_cpu_map); 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci default: 75962306a36Sopenharmony_ci pr_debug("Unsupported CPU co-ord type: %d\n", 76062306a36Sopenharmony_ci policy->shared_type); 76162306a36Sopenharmony_ci ret = -EFAULT; 76262306a36Sopenharmony_ci goto out; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci policy->fast_switch_possible = cppc_allow_fast_switch(); 76662306a36Sopenharmony_ci policy->dvfs_possible_from_any_cpu = true; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* 76962306a36Sopenharmony_ci * If 'highest_perf' is greater than 'nominal_perf', we assume CPU Boost 77062306a36Sopenharmony_ci * is supported. 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_ci if (caps->highest_perf > caps->nominal_perf) 77362306a36Sopenharmony_ci boost_supported = true; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Set policy->cur to max now. The governors will adjust later. */ 77662306a36Sopenharmony_ci policy->cur = cppc_cpufreq_perf_to_khz(cpu_data, caps->highest_perf); 77762306a36Sopenharmony_ci cpu_data->perf_ctrls.desired_perf = caps->highest_perf; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); 78062306a36Sopenharmony_ci if (ret) { 78162306a36Sopenharmony_ci pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", 78262306a36Sopenharmony_ci caps->highest_perf, cpu, ret); 78362306a36Sopenharmony_ci goto out; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci cppc_cpufreq_cpu_fie_init(policy); 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ciout: 79062306a36Sopenharmony_ci cppc_cpufreq_put_cpu_data(policy); 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic int cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 79762306a36Sopenharmony_ci struct cppc_perf_caps *caps = &cpu_data->perf_caps; 79862306a36Sopenharmony_ci unsigned int cpu = policy->cpu; 79962306a36Sopenharmony_ci int ret; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci cppc_cpufreq_cpu_fie_exit(policy); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci cpu_data->perf_ctrls.desired_perf = caps->lowest_perf; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); 80662306a36Sopenharmony_ci if (ret) 80762306a36Sopenharmony_ci pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", 80862306a36Sopenharmony_ci caps->lowest_perf, cpu, ret); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci cppc_cpufreq_put_cpu_data(policy); 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic inline u64 get_delta(u64 t1, u64 t0) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci if (t1 > t0 || t0 > ~(u32)0) 81762306a36Sopenharmony_ci return t1 - t0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return (u32)t1 - (u32)t0; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, 82362306a36Sopenharmony_ci struct cppc_perf_fb_ctrs *fb_ctrs_t0, 82462306a36Sopenharmony_ci struct cppc_perf_fb_ctrs *fb_ctrs_t1) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci u64 delta_reference, delta_delivered; 82762306a36Sopenharmony_ci u64 reference_perf; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci reference_perf = fb_ctrs_t0->reference_perf; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci delta_reference = get_delta(fb_ctrs_t1->reference, 83262306a36Sopenharmony_ci fb_ctrs_t0->reference); 83362306a36Sopenharmony_ci delta_delivered = get_delta(fb_ctrs_t1->delivered, 83462306a36Sopenharmony_ci fb_ctrs_t0->delivered); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* Check to avoid divide-by zero and invalid delivered_perf */ 83762306a36Sopenharmony_ci if (!delta_reference || !delta_delivered) 83862306a36Sopenharmony_ci return cpu_data->perf_ctrls.desired_perf; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return (reference_perf * delta_delivered) / delta_reference; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic unsigned int cppc_cpufreq_get_rate(unsigned int cpu) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; 84662306a36Sopenharmony_ci struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); 84762306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 84862306a36Sopenharmony_ci u64 delivered_perf; 84962306a36Sopenharmony_ci int ret; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci cpufreq_cpu_put(policy); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); 85462306a36Sopenharmony_ci if (ret) 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci udelay(2); /* 2usec delay between sampling */ 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); 86062306a36Sopenharmony_ci if (ret) 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, 86462306a36Sopenharmony_ci &fb_ctrs_t1); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 87262306a36Sopenharmony_ci struct cppc_perf_caps *caps = &cpu_data->perf_caps; 87362306a36Sopenharmony_ci int ret; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (!boost_supported) { 87662306a36Sopenharmony_ci pr_err("BOOST not supported by CPU or firmware\n"); 87762306a36Sopenharmony_ci return -EINVAL; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (state) 88162306a36Sopenharmony_ci policy->max = cppc_cpufreq_perf_to_khz(cpu_data, 88262306a36Sopenharmony_ci caps->highest_perf); 88362306a36Sopenharmony_ci else 88462306a36Sopenharmony_ci policy->max = cppc_cpufreq_perf_to_khz(cpu_data, 88562306a36Sopenharmony_ci caps->nominal_perf); 88662306a36Sopenharmony_ci policy->cpuinfo.max_freq = policy->max; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ret = freq_qos_update_request(policy->max_freq_req, policy->max); 88962306a36Sopenharmony_ci if (ret < 0) 89062306a36Sopenharmony_ci return ret; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return cpufreq_show_cpus(cpu_data->shared_cpu_map, buf); 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_cicpufreq_freq_attr_ro(freqdomain_cpus); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic struct freq_attr *cppc_cpufreq_attr[] = { 90462306a36Sopenharmony_ci &freqdomain_cpus, 90562306a36Sopenharmony_ci NULL, 90662306a36Sopenharmony_ci}; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic struct cpufreq_driver cppc_cpufreq_driver = { 90962306a36Sopenharmony_ci .flags = CPUFREQ_CONST_LOOPS, 91062306a36Sopenharmony_ci .verify = cppc_verify_policy, 91162306a36Sopenharmony_ci .target = cppc_cpufreq_set_target, 91262306a36Sopenharmony_ci .get = cppc_cpufreq_get_rate, 91362306a36Sopenharmony_ci .fast_switch = cppc_cpufreq_fast_switch, 91462306a36Sopenharmony_ci .init = cppc_cpufreq_cpu_init, 91562306a36Sopenharmony_ci .exit = cppc_cpufreq_cpu_exit, 91662306a36Sopenharmony_ci .set_boost = cppc_cpufreq_set_boost, 91762306a36Sopenharmony_ci .attr = cppc_cpufreq_attr, 91862306a36Sopenharmony_ci .name = "cppc_cpufreq", 91962306a36Sopenharmony_ci}; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/* 92262306a36Sopenharmony_ci * HISI platform does not support delivered performance counter and 92362306a36Sopenharmony_ci * reference performance counter. It can calculate the performance using the 92462306a36Sopenharmony_ci * platform specific mechanism. We reuse the desired performance register to 92562306a36Sopenharmony_ci * store the real performance calculated by the platform. 92662306a36Sopenharmony_ci */ 92762306a36Sopenharmony_cistatic unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); 93062306a36Sopenharmony_ci struct cppc_cpudata *cpu_data = policy->driver_data; 93162306a36Sopenharmony_ci u64 desired_perf; 93262306a36Sopenharmony_ci int ret; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci cpufreq_cpu_put(policy); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci ret = cppc_get_desired_perf(cpu, &desired_perf); 93762306a36Sopenharmony_ci if (ret < 0) 93862306a36Sopenharmony_ci return -EIO; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return cppc_cpufreq_perf_to_khz(cpu_data, desired_perf); 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic void cppc_check_hisi_workaround(void) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct acpi_table_header *tbl; 94662306a36Sopenharmony_ci acpi_status status = AE_OK; 94762306a36Sopenharmony_ci int i; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); 95062306a36Sopenharmony_ci if (ACPI_FAILURE(status) || !tbl) 95162306a36Sopenharmony_ci return; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wa_info); i++) { 95462306a36Sopenharmony_ci if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && 95562306a36Sopenharmony_ci !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && 95662306a36Sopenharmony_ci wa_info[i].oem_revision == tbl->oem_revision) { 95762306a36Sopenharmony_ci /* Overwrite the get() callback */ 95862306a36Sopenharmony_ci cppc_cpufreq_driver.get = hisi_cppc_cpufreq_get_rate; 95962306a36Sopenharmony_ci fie_disabled = FIE_DISABLED; 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci acpi_put_table(tbl); 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int __init cppc_cpufreq_init(void) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci int ret; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!acpi_cpc_valid()) 97262306a36Sopenharmony_ci return -ENODEV; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci cppc_check_hisi_workaround(); 97562306a36Sopenharmony_ci cppc_freq_invariance_init(); 97662306a36Sopenharmony_ci populate_efficiency_class(); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci ret = cpufreq_register_driver(&cppc_cpufreq_driver); 97962306a36Sopenharmony_ci if (ret) 98062306a36Sopenharmony_ci cppc_freq_invariance_exit(); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci return ret; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_cistatic inline void free_cpu_data(void) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct cppc_cpudata *iter, *tmp; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci list_for_each_entry_safe(iter, tmp, &cpu_data_list, node) { 99062306a36Sopenharmony_ci free_cpumask_var(iter->shared_cpu_map); 99162306a36Sopenharmony_ci list_del(&iter->node); 99262306a36Sopenharmony_ci kfree(iter); 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic void __exit cppc_cpufreq_exit(void) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci cpufreq_unregister_driver(&cppc_cpufreq_driver); 100062306a36Sopenharmony_ci cppc_freq_invariance_exit(); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci free_cpu_data(); 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cimodule_exit(cppc_cpufreq_exit); 100662306a36Sopenharmony_ciMODULE_AUTHOR("Ashwin Chaugule"); 100762306a36Sopenharmony_ciMODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec"); 100862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cilate_initcall(cppc_cpufreq_init); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic const struct acpi_device_id cppc_acpi_ids[] __used = { 101362306a36Sopenharmony_ci {ACPI_PROCESSOR_DEVICE_HID, }, 101462306a36Sopenharmony_ci {} 101562306a36Sopenharmony_ci}; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, cppc_acpi_ids); 1018