forked from Minki/linux
Merge branch 'pm-cpufreq'
* pm-cpufreq: (31 commits) cpufreq: Fix cpufreq_online() return value on errors cpufreq: Fix up several kerneldoc comments cpufreq: stats: Use local_clock() instead of jiffies cpufreq: schedutil: Simplify sugov_update_next_freq() cpufreq: intel_pstate: Simplify intel_cpufreq_update_pstate() cpufreq: arm_scmi: Discover the power scale in performance protocol firmware: arm_scmi: Add power_scale_mw_get() interface cpufreq: tegra194: Rename tegra194_get_speed_common function cpufreq: tegra194: Remove unnecessary frequency calculation cpufreq: tegra186: Simplify cluster information lookup cpufreq: tegra186: Fix sparse 'incorrect type in assignment' warning cpufreq: imx: fix NVMEM_IMX_OCOTP dependency cpufreq: vexpress-spc: Add missing MODULE_ALIAS cpufreq: scpi: Add missing MODULE_ALIAS cpufreq: loongson1: Add missing MODULE_ALIAS cpufreq: sun50i: Add missing MODULE_DEVICE_TABLE cpufreq: st: Add missing MODULE_DEVICE_TABLE cpufreq: qcom: Add missing MODULE_DEVICE_TABLE cpufreq: mediatek: Add missing MODULE_DEVICE_TABLE cpufreq: highbank: Add missing MODULE_DEVICE_TABLE ...
This commit is contained in:
commit
e1f1320fc0
@ -94,7 +94,7 @@ config ARM_IMX6Q_CPUFREQ
|
||||
tristate "Freescale i.MX6 cpufreq support"
|
||||
depends on ARCH_MXC
|
||||
depends on REGULATOR_ANATOP
|
||||
select NVMEM_IMX_OCOTP
|
||||
depends on NVMEM_IMX_OCOTP || COMPILE_TEST
|
||||
select PM_OPP
|
||||
help
|
||||
This adds cpufreq driver support for Freescale i.MX6 series SoCs.
|
||||
|
@ -204,6 +204,12 @@ static void __exit armada_8k_cpufreq_exit(void)
|
||||
}
|
||||
module_exit(armada_8k_cpufreq_exit);
|
||||
|
||||
static const struct of_device_id __maybe_unused armada_8k_cpufreq_of_match[] = {
|
||||
{ .compatible = "marvell,ap806-cpu-clock" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, armada_8k_cpufreq_of_match);
|
||||
|
||||
MODULE_AUTHOR("Gregory Clement <gregory.clement@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Armada 8K cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -26,8 +26,8 @@
|
||||
/* Minimum struct length needed for the DMI processor entry we want */
|
||||
#define DMI_ENTRY_PROCESSOR_MIN_LENGTH 48
|
||||
|
||||
/* Offest in the DMI processor structure for the max frequency */
|
||||
#define DMI_PROCESSOR_MAX_SPEED 0x14
|
||||
/* Offset in the DMI processor structure for the max frequency */
|
||||
#define DMI_PROCESSOR_MAX_SPEED 0x14
|
||||
|
||||
/*
|
||||
* These structs contain information parsed from per CPU
|
||||
@ -96,11 +96,11 @@ static u64 cppc_get_dmi_max_khz(void)
|
||||
* and extrapolate the rest
|
||||
* For perf/freq > Nominal, we use the ratio perf:freq at Nominal for conversion
|
||||
*/
|
||||
static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
|
||||
unsigned int perf)
|
||||
static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu_data,
|
||||
unsigned int perf)
|
||||
{
|
||||
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
|
||||
static u64 max_khz;
|
||||
struct cppc_perf_caps *caps = &cpu->perf_caps;
|
||||
u64 mul, div;
|
||||
|
||||
if (caps->lowest_freq && caps->nominal_freq) {
|
||||
@ -120,11 +120,11 @@ static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
|
||||
return (u64)perf * mul / div;
|
||||
}
|
||||
|
||||
static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu,
|
||||
unsigned int freq)
|
||||
static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu_data,
|
||||
unsigned int freq)
|
||||
{
|
||||
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
|
||||
static u64 max_khz;
|
||||
struct cppc_perf_caps *caps = &cpu->perf_caps;
|
||||
u64 mul, div;
|
||||
|
||||
if (caps->lowest_freq && caps->nominal_freq) {
|
||||
@ -146,32 +146,30 @@ static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu,
|
||||
}
|
||||
|
||||
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||
unsigned int target_freq,
|
||||
unsigned int relation)
|
||||
unsigned int target_freq,
|
||||
unsigned int relation)
|
||||
{
|
||||
struct cppc_cpudata *cpu;
|
||||
struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu];
|
||||
struct cpufreq_freqs freqs;
|
||||
u32 desired_perf;
|
||||
int ret = 0;
|
||||
|
||||
cpu = all_cpu_data[policy->cpu];
|
||||
|
||||
desired_perf = cppc_cpufreq_khz_to_perf(cpu, target_freq);
|
||||
desired_perf = cppc_cpufreq_khz_to_perf(cpu_data, target_freq);
|
||||
/* Return if it is exactly the same perf */
|
||||
if (desired_perf == cpu->perf_ctrls.desired_perf)
|
||||
if (desired_perf == cpu_data->perf_ctrls.desired_perf)
|
||||
return ret;
|
||||
|
||||
cpu->perf_ctrls.desired_perf = desired_perf;
|
||||
cpu_data->perf_ctrls.desired_perf = desired_perf;
|
||||
freqs.old = policy->cur;
|
||||
freqs.new = target_freq;
|
||||
|
||||
cpufreq_freq_transition_begin(policy, &freqs);
|
||||
ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls);
|
||||
ret = cppc_set_perf(cpu_data->cpu, &cpu_data->perf_ctrls);
|
||||
cpufreq_freq_transition_end(policy, &freqs, ret != 0);
|
||||
|
||||
if (ret)
|
||||
pr_debug("Failed to set target on CPU:%d. ret:%d\n",
|
||||
cpu->cpu, ret);
|
||||
cpu_data->cpu, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -184,28 +182,29 @@ static int cppc_verify_policy(struct cpufreq_policy_data *policy)
|
||||
|
||||
static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
|
||||
{
|
||||
int cpu_num = policy->cpu;
|
||||
struct cppc_cpudata *cpu = all_cpu_data[cpu_num];
|
||||
struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu];
|
||||
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
|
||||
unsigned int cpu = policy->cpu;
|
||||
int ret;
|
||||
|
||||
cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf;
|
||||
cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
|
||||
|
||||
ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
|
||||
ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
|
||||
if (ret)
|
||||
pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
|
||||
cpu->perf_caps.lowest_perf, cpu_num, ret);
|
||||
caps->lowest_perf, cpu, ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* The PCC subspace describes the rate at which platform can accept commands
|
||||
* on the shared PCC channel (including READs which do not count towards freq
|
||||
* trasition requests), so ideally we need to use the PCC values as a fallback
|
||||
* transition requests), so ideally we need to use the PCC values as a fallback
|
||||
* if we don't have a platform specific transition_delay_us
|
||||
*/
|
||||
#ifdef CONFIG_ARM64
|
||||
#include <asm/cputype.h>
|
||||
|
||||
static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu)
|
||||
static unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu)
|
||||
{
|
||||
unsigned long implementor = read_cpuid_implementor();
|
||||
unsigned long part_num = read_cpuid_part_number();
|
||||
@ -233,7 +232,7 @@ static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu)
|
||||
|
||||
#else
|
||||
|
||||
static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu)
|
||||
static unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu)
|
||||
{
|
||||
return cppc_get_transition_latency(cpu) / NSEC_PER_USEC;
|
||||
}
|
||||
@ -241,54 +240,57 @@ static unsigned int cppc_cpufreq_get_transition_delay_us(int cpu)
|
||||
|
||||
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct cppc_cpudata *cpu;
|
||||
unsigned int cpu_num = policy->cpu;
|
||||
struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu];
|
||||
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
|
||||
unsigned int cpu = policy->cpu;
|
||||
int ret = 0;
|
||||
|
||||
cpu = all_cpu_data[policy->cpu];
|
||||
|
||||
cpu->cpu = cpu_num;
|
||||
ret = cppc_get_perf_caps(policy->cpu, &cpu->perf_caps);
|
||||
cpu_data->cpu = cpu;
|
||||
ret = cppc_get_perf_caps(cpu, caps);
|
||||
|
||||
if (ret) {
|
||||
pr_debug("Err reading CPU%d perf capabilities. ret:%d\n",
|
||||
cpu_num, ret);
|
||||
cpu, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Convert the lowest and nominal freq from MHz to KHz */
|
||||
cpu->perf_caps.lowest_freq *= 1000;
|
||||
cpu->perf_caps.nominal_freq *= 1000;
|
||||
caps->lowest_freq *= 1000;
|
||||
caps->nominal_freq *= 1000;
|
||||
|
||||
/*
|
||||
* Set min to lowest nonlinear perf to avoid any efficiency penalty (see
|
||||
* Section 8.4.7.1.1.5 of ACPI 6.1 spec)
|
||||
*/
|
||||
policy->min = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_nonlinear_perf);
|
||||
policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.nominal_perf);
|
||||
policy->min = cppc_cpufreq_perf_to_khz(cpu_data,
|
||||
caps->lowest_nonlinear_perf);
|
||||
policy->max = cppc_cpufreq_perf_to_khz(cpu_data,
|
||||
caps->nominal_perf);
|
||||
|
||||
/*
|
||||
* Set cpuinfo.min_freq to Lowest to make the full range of performance
|
||||
* available if userspace wants to use any perf between lowest & lowest
|
||||
* nonlinear perf
|
||||
*/
|
||||
policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_perf);
|
||||
policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.nominal_perf);
|
||||
policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu_data,
|
||||
caps->lowest_perf);
|
||||
policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu_data,
|
||||
caps->nominal_perf);
|
||||
|
||||
policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu_num);
|
||||
policy->shared_type = cpu->shared_type;
|
||||
policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu);
|
||||
policy->shared_type = cpu_data->shared_type;
|
||||
|
||||
if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
|
||||
int i;
|
||||
|
||||
cpumask_copy(policy->cpus, cpu->shared_cpu_map);
|
||||
cpumask_copy(policy->cpus, cpu_data->shared_cpu_map);
|
||||
|
||||
for_each_cpu(i, policy->cpus) {
|
||||
if (unlikely(i == policy->cpu))
|
||||
if (unlikely(i == cpu))
|
||||
continue;
|
||||
|
||||
memcpy(&all_cpu_data[i]->perf_caps, &cpu->perf_caps,
|
||||
sizeof(cpu->perf_caps));
|
||||
memcpy(&all_cpu_data[i]->perf_caps, caps,
|
||||
sizeof(cpu_data->perf_caps));
|
||||
}
|
||||
} else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) {
|
||||
/* Support only SW_ANY for now. */
|
||||
@ -296,24 +298,23 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
cpu->cur_policy = policy;
|
||||
cpu_data->cur_policy = policy;
|
||||
|
||||
/*
|
||||
* If 'highest_perf' is greater than 'nominal_perf', we assume CPU Boost
|
||||
* is supported.
|
||||
*/
|
||||
if (cpu->perf_caps.highest_perf > cpu->perf_caps.nominal_perf)
|
||||
if (caps->highest_perf > caps->nominal_perf)
|
||||
boost_supported = true;
|
||||
|
||||
/* Set policy->cur to max now. The governors will adjust later. */
|
||||
policy->cur = cppc_cpufreq_perf_to_khz(cpu,
|
||||
cpu->perf_caps.highest_perf);
|
||||
cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
|
||||
policy->cur = cppc_cpufreq_perf_to_khz(cpu_data, caps->highest_perf);
|
||||
cpu_data->perf_ctrls.desired_perf = caps->highest_perf;
|
||||
|
||||
ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
|
||||
ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
|
||||
if (ret)
|
||||
pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
|
||||
cpu->perf_caps.highest_perf, cpu_num, ret);
|
||||
caps->highest_perf, cpu, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -326,7 +327,7 @@ static inline u64 get_delta(u64 t1, u64 t0)
|
||||
return (u32)t1 - (u32)t0;
|
||||
}
|
||||
|
||||
static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu,
|
||||
static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data,
|
||||
struct cppc_perf_fb_ctrs fb_ctrs_t0,
|
||||
struct cppc_perf_fb_ctrs fb_ctrs_t1)
|
||||
{
|
||||
@ -345,33 +346,34 @@ static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu,
|
||||
delivered_perf = (reference_perf * delta_delivered) /
|
||||
delta_reference;
|
||||
else
|
||||
delivered_perf = cpu->perf_ctrls.desired_perf;
|
||||
delivered_perf = cpu_data->perf_ctrls.desired_perf;
|
||||
|
||||
return cppc_cpufreq_perf_to_khz(cpu, delivered_perf);
|
||||
return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf);
|
||||
}
|
||||
|
||||
static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum)
|
||||
static unsigned int cppc_cpufreq_get_rate(unsigned int cpu)
|
||||
{
|
||||
struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0};
|
||||
struct cppc_cpudata *cpu = all_cpu_data[cpunum];
|
||||
struct cppc_cpudata *cpu_data = all_cpu_data[cpu];
|
||||
int ret;
|
||||
|
||||
ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0);
|
||||
ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
udelay(2); /* 2usec delay between sampling */
|
||||
|
||||
ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t1);
|
||||
ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return cppc_get_rate_from_fbctrs(cpu, fb_ctrs_t0, fb_ctrs_t1);
|
||||
return cppc_get_rate_from_fbctrs(cpu_data, fb_ctrs_t0, fb_ctrs_t1);
|
||||
}
|
||||
|
||||
static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state)
|
||||
{
|
||||
struct cppc_cpudata *cpudata;
|
||||
struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu];
|
||||
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
|
||||
int ret;
|
||||
|
||||
if (!boost_supported) {
|
||||
@ -379,13 +381,12 @@ static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpudata = all_cpu_data[policy->cpu];
|
||||
if (state)
|
||||
policy->max = cppc_cpufreq_perf_to_khz(cpudata,
|
||||
cpudata->perf_caps.highest_perf);
|
||||
policy->max = cppc_cpufreq_perf_to_khz(cpu_data,
|
||||
caps->highest_perf);
|
||||
else
|
||||
policy->max = cppc_cpufreq_perf_to_khz(cpudata,
|
||||
cpudata->perf_caps.nominal_perf);
|
||||
policy->max = cppc_cpufreq_perf_to_khz(cpu_data,
|
||||
caps->nominal_perf);
|
||||
policy->cpuinfo.max_freq = policy->max;
|
||||
|
||||
ret = freq_qos_update_request(policy->max_freq_req, policy->max);
|
||||
@ -412,17 +413,17 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
|
||||
* platform specific mechanism. We reuse the desired performance register to
|
||||
* store the real performance calculated by the platform.
|
||||
*/
|
||||
static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum)
|
||||
static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu)
|
||||
{
|
||||
struct cppc_cpudata *cpudata = all_cpu_data[cpunum];
|
||||
struct cppc_cpudata *cpu_data = all_cpu_data[cpu];
|
||||
u64 desired_perf;
|
||||
int ret;
|
||||
|
||||
ret = cppc_get_desired_perf(cpunum, &desired_perf);
|
||||
ret = cppc_get_desired_perf(cpu, &desired_perf);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
|
||||
return cppc_cpufreq_perf_to_khz(cpudata, desired_perf);
|
||||
return cppc_cpufreq_perf_to_khz(cpu_data, desired_perf);
|
||||
}
|
||||
|
||||
static void cppc_check_hisi_workaround(void)
|
||||
@ -450,8 +451,8 @@ static void cppc_check_hisi_workaround(void)
|
||||
|
||||
static int __init cppc_cpufreq_init(void)
|
||||
{
|
||||
struct cppc_cpudata *cpu_data;
|
||||
int i, ret = 0;
|
||||
struct cppc_cpudata *cpu;
|
||||
|
||||
if (acpi_disabled)
|
||||
return -ENODEV;
|
||||
@ -466,8 +467,8 @@ static int __init cppc_cpufreq_init(void)
|
||||
if (!all_cpu_data[i])
|
||||
goto out;
|
||||
|
||||
cpu = all_cpu_data[i];
|
||||
if (!zalloc_cpumask_var(&cpu->shared_cpu_map, GFP_KERNEL))
|
||||
cpu_data = all_cpu_data[i];
|
||||
if (!zalloc_cpumask_var(&cpu_data->shared_cpu_map, GFP_KERNEL))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -487,11 +488,11 @@ static int __init cppc_cpufreq_init(void)
|
||||
|
||||
out:
|
||||
for_each_possible_cpu(i) {
|
||||
cpu = all_cpu_data[i];
|
||||
if (!cpu)
|
||||
cpu_data = all_cpu_data[i];
|
||||
if (!cpu_data)
|
||||
break;
|
||||
free_cpumask_var(cpu->shared_cpu_map);
|
||||
kfree(cpu);
|
||||
free_cpumask_var(cpu_data->shared_cpu_map);
|
||||
kfree(cpu_data);
|
||||
}
|
||||
|
||||
kfree(all_cpu_data);
|
||||
@ -500,15 +501,15 @@ out:
|
||||
|
||||
static void __exit cppc_cpufreq_exit(void)
|
||||
{
|
||||
struct cppc_cpudata *cpu;
|
||||
struct cppc_cpudata *cpu_data;
|
||||
int i;
|
||||
|
||||
cpufreq_unregister_driver(&cppc_cpufreq_driver);
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
cpu = all_cpu_data[i];
|
||||
free_cpumask_var(cpu->shared_cpu_map);
|
||||
kfree(cpu);
|
||||
cpu_data = all_cpu_data[i];
|
||||
free_cpumask_var(cpu_data->shared_cpu_map);
|
||||
kfree(cpu_data);
|
||||
}
|
||||
|
||||
kfree(all_cpu_data);
|
||||
|
@ -119,10 +119,12 @@ static const struct of_device_id blacklist[] __initconst = {
|
||||
{ .compatible = "mediatek,mt2712", },
|
||||
{ .compatible = "mediatek,mt7622", },
|
||||
{ .compatible = "mediatek,mt7623", },
|
||||
{ .compatible = "mediatek,mt8167", },
|
||||
{ .compatible = "mediatek,mt817x", },
|
||||
{ .compatible = "mediatek,mt8173", },
|
||||
{ .compatible = "mediatek,mt8176", },
|
||||
{ .compatible = "mediatek,mt8183", },
|
||||
{ .compatible = "mediatek,mt8516", },
|
||||
|
||||
{ .compatible = "nvidia,tegra20", },
|
||||
{ .compatible = "nvidia,tegra30", },
|
||||
|
@ -298,8 +298,10 @@ struct cpufreq_policy *cpufreq_cpu_acquire(unsigned int cpu)
|
||||
* EXTERNALLY AFFECTING FREQUENCY CHANGES *
|
||||
*********************************************************************/
|
||||
|
||||
/*
|
||||
* adjust_jiffies - adjust the system "loops_per_jiffy"
|
||||
/**
|
||||
* adjust_jiffies - Adjust the system "loops_per_jiffy".
|
||||
* @val: CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
|
||||
* @ci: Frequency change information.
|
||||
*
|
||||
* This function alters the system "loops_per_jiffy" for the clock
|
||||
* speed change. Note that loops_per_jiffy cannot be updated on SMP
|
||||
@ -331,14 +333,14 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
|
||||
}
|
||||
|
||||
/**
|
||||
* cpufreq_notify_transition - Notify frequency transition and adjust_jiffies.
|
||||
* cpufreq_notify_transition - Notify frequency transition and adjust jiffies.
|
||||
* @policy: cpufreq policy to enable fast frequency switching for.
|
||||
* @freqs: contain details of the frequency update.
|
||||
* @state: set to CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
|
||||
*
|
||||
* This function calls the transition notifiers and the "adjust_jiffies"
|
||||
* function. It is called twice on all CPU frequency changes that have
|
||||
* external effects.
|
||||
* This function calls the transition notifiers and adjust_jiffies().
|
||||
*
|
||||
* It is called twice on all CPU frequency changes that have external effects.
|
||||
*/
|
||||
static void cpufreq_notify_transition(struct cpufreq_policy *policy,
|
||||
struct cpufreq_freqs *freqs,
|
||||
@ -1391,8 +1393,10 @@ static int cpufreq_online(unsigned int cpu)
|
||||
|
||||
policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
|
||||
GFP_KERNEL);
|
||||
if (!policy->min_freq_req)
|
||||
if (!policy->min_freq_req) {
|
||||
ret = -ENOMEM;
|
||||
goto out_destroy_policy;
|
||||
}
|
||||
|
||||
ret = freq_qos_add_request(&policy->constraints,
|
||||
policy->min_freq_req, FREQ_QOS_MIN,
|
||||
@ -1429,6 +1433,7 @@ static int cpufreq_online(unsigned int cpu)
|
||||
if (cpufreq_driver->get && has_target()) {
|
||||
policy->cur = cpufreq_driver->get(policy->cpu);
|
||||
if (!policy->cur) {
|
||||
ret = -EIO;
|
||||
pr_err("%s: ->get() failed\n", __func__);
|
||||
goto out_destroy_policy;
|
||||
}
|
||||
@ -1646,13 +1651,12 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
|
||||
}
|
||||
|
||||
/**
|
||||
* cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're
|
||||
* in deep trouble.
|
||||
* @policy: policy managing CPUs
|
||||
* @new_freq: CPU frequency the CPU actually runs at
|
||||
* cpufreq_out_of_sync - Fix up actual and saved CPU frequency difference.
|
||||
* @policy: Policy managing CPUs.
|
||||
* @new_freq: New CPU frequency.
|
||||
*
|
||||
* We adjust to current frequency first, and need to clean up later.
|
||||
* So either call to cpufreq_update_policy() or schedule handle_update()).
|
||||
* Adjust to the current frequency first and clean up later by either calling
|
||||
* cpufreq_update_policy(), or scheduling handle_update().
|
||||
*/
|
||||
static void cpufreq_out_of_sync(struct cpufreq_policy *policy,
|
||||
unsigned int new_freq)
|
||||
@ -1832,7 +1836,7 @@ int cpufreq_generic_suspend(struct cpufreq_policy *policy)
|
||||
EXPORT_SYMBOL(cpufreq_generic_suspend);
|
||||
|
||||
/**
|
||||
* cpufreq_suspend() - Suspend CPUFreq governors
|
||||
* cpufreq_suspend() - Suspend CPUFreq governors.
|
||||
*
|
||||
* Called during system wide Suspend/Hibernate cycles for suspending governors
|
||||
* as some platforms can't change frequency after this point in suspend cycle.
|
||||
@ -1868,7 +1872,7 @@ suspend:
|
||||
}
|
||||
|
||||
/**
|
||||
* cpufreq_resume() - Resume CPUFreq governors
|
||||
* cpufreq_resume() - Resume CPUFreq governors.
|
||||
*
|
||||
* Called during system wide Suspend/Hibernate cycle for resuming governors that
|
||||
* are suspended with cpufreq_suspend().
|
||||
@ -1920,10 +1924,10 @@ bool cpufreq_driver_test_flags(u16 flags)
|
||||
}
|
||||
|
||||
/**
|
||||
* cpufreq_get_current_driver - return current driver's name
|
||||
* cpufreq_get_current_driver - Return the current driver's name.
|
||||
*
|
||||
* Return the name string of the currently loaded cpufreq driver
|
||||
* or NULL, if none.
|
||||
* Return the name string of the currently registered cpufreq driver or NULL if
|
||||
* none.
|
||||
*/
|
||||
const char *cpufreq_get_current_driver(void)
|
||||
{
|
||||
@ -1935,10 +1939,10 @@ const char *cpufreq_get_current_driver(void)
|
||||
EXPORT_SYMBOL_GPL(cpufreq_get_current_driver);
|
||||
|
||||
/**
|
||||
* cpufreq_get_driver_data - return current driver data
|
||||
* cpufreq_get_driver_data - Return current driver data.
|
||||
*
|
||||
* Return the private data of the currently loaded cpufreq
|
||||
* driver, or NULL if no cpufreq driver is loaded.
|
||||
* Return the private data of the currently registered cpufreq driver, or NULL
|
||||
* if no cpufreq driver has been registered.
|
||||
*/
|
||||
void *cpufreq_get_driver_data(void)
|
||||
{
|
||||
@ -1954,17 +1958,16 @@ EXPORT_SYMBOL_GPL(cpufreq_get_driver_data);
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* cpufreq_register_notifier - register a driver with cpufreq
|
||||
* @nb: notifier function to register
|
||||
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
|
||||
* cpufreq_register_notifier - Register a notifier with cpufreq.
|
||||
* @nb: notifier function to register.
|
||||
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER.
|
||||
*
|
||||
* Add a driver to one of two lists: either a list of drivers that
|
||||
* are notified about clock rate changes (once before and once after
|
||||
* the transition), or a list of drivers that are notified about
|
||||
* changes in cpufreq policy.
|
||||
* Add a notifier to one of two lists: either a list of notifiers that run on
|
||||
* clock rate changes (once before and once after every transition), or a list
|
||||
* of notifiers that ron on cpufreq policy changes.
|
||||
*
|
||||
* This function may sleep, and has the same return conditions as
|
||||
* blocking_notifier_chain_register.
|
||||
* This function may sleep and it has the same return values as
|
||||
* blocking_notifier_chain_register().
|
||||
*/
|
||||
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
|
||||
{
|
||||
@ -2001,14 +2004,14 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
|
||||
EXPORT_SYMBOL(cpufreq_register_notifier);
|
||||
|
||||
/**
|
||||
* cpufreq_unregister_notifier - unregister a driver with cpufreq
|
||||
* @nb: notifier block to be unregistered
|
||||
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
|
||||
* cpufreq_unregister_notifier - Unregister a notifier from cpufreq.
|
||||
* @nb: notifier block to be unregistered.
|
||||
* @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER.
|
||||
*
|
||||
* Remove a driver from the CPU frequency notifier list.
|
||||
* Remove a notifier from one of the cpufreq notifier lists.
|
||||
*
|
||||
* This function may sleep, and has the same return conditions as
|
||||
* blocking_notifier_chain_unregister.
|
||||
* This function may sleep and it has the same return values as
|
||||
* blocking_notifier_chain_unregister().
|
||||
*/
|
||||
int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
|
||||
{
|
||||
@ -2123,7 +2126,7 @@ static int __target_intermediate(struct cpufreq_policy *policy,
|
||||
static int __target_index(struct cpufreq_policy *policy, int index)
|
||||
{
|
||||
struct cpufreq_freqs freqs = {.old = policy->cur, .flags = 0};
|
||||
unsigned int intermediate_freq = 0;
|
||||
unsigned int restore_freq, intermediate_freq = 0;
|
||||
unsigned int newfreq = policy->freq_table[index].frequency;
|
||||
int retval = -EINVAL;
|
||||
bool notify;
|
||||
@ -2131,6 +2134,9 @@ static int __target_index(struct cpufreq_policy *policy, int index)
|
||||
if (newfreq == policy->cur)
|
||||
return 0;
|
||||
|
||||
/* Save last value to restore later on errors */
|
||||
restore_freq = policy->cur;
|
||||
|
||||
notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
|
||||
if (notify) {
|
||||
/* Handle switching to intermediate frequency */
|
||||
@ -2168,7 +2174,7 @@ static int __target_index(struct cpufreq_policy *policy, int index)
|
||||
*/
|
||||
if (unlikely(retval && intermediate_freq)) {
|
||||
freqs.old = intermediate_freq;
|
||||
freqs.new = policy->restore_freq;
|
||||
freqs.new = restore_freq;
|
||||
cpufreq_freq_transition_begin(policy, &freqs);
|
||||
cpufreq_freq_transition_end(policy, &freqs, 0);
|
||||
}
|
||||
@ -2203,9 +2209,6 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
|
||||
!(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS))
|
||||
return 0;
|
||||
|
||||
/* Save last value to restore later on errors */
|
||||
policy->restore_freq = policy->cur;
|
||||
|
||||
if (cpufreq_driver->target)
|
||||
return cpufreq_driver->target(policy, target_freq, relation);
|
||||
|
||||
|
@ -9,9 +9,9 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
||||
struct cpufreq_stats {
|
||||
unsigned int total_trans;
|
||||
unsigned long long last_time;
|
||||
@ -30,7 +30,7 @@ struct cpufreq_stats {
|
||||
static void cpufreq_stats_update(struct cpufreq_stats *stats,
|
||||
unsigned long long time)
|
||||
{
|
||||
unsigned long long cur_time = get_jiffies_64();
|
||||
unsigned long long cur_time = local_clock();
|
||||
|
||||
stats->time_in_state[stats->last_index] += cur_time - time;
|
||||
stats->last_time = cur_time;
|
||||
@ -42,7 +42,7 @@ static void cpufreq_stats_reset_table(struct cpufreq_stats *stats)
|
||||
|
||||
memset(stats->time_in_state, 0, count * sizeof(u64));
|
||||
memset(stats->trans_table, 0, count * count * sizeof(int));
|
||||
stats->last_time = get_jiffies_64();
|
||||
stats->last_time = local_clock();
|
||||
stats->total_trans = 0;
|
||||
|
||||
/* Adjust for the time elapsed since reset was requested */
|
||||
@ -82,18 +82,18 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
|
||||
* before the reset_pending read above.
|
||||
*/
|
||||
smp_rmb();
|
||||
time = get_jiffies_64() - READ_ONCE(stats->reset_time);
|
||||
time = local_clock() - READ_ONCE(stats->reset_time);
|
||||
} else {
|
||||
time = 0;
|
||||
}
|
||||
} else {
|
||||
time = stats->time_in_state[i];
|
||||
if (i == stats->last_index)
|
||||
time += get_jiffies_64() - stats->last_time;
|
||||
time += local_clock() - stats->last_time;
|
||||
}
|
||||
|
||||
len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
|
||||
jiffies_64_to_clock_t(time));
|
||||
nsec_to_clock_t(time));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@ -109,7 +109,7 @@ static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
|
||||
* Defer resetting of stats to cpufreq_stats_record_transition() to
|
||||
* avoid races.
|
||||
*/
|
||||
WRITE_ONCE(stats->reset_time, get_jiffies_64());
|
||||
WRITE_ONCE(stats->reset_time, local_clock());
|
||||
/*
|
||||
* The memory barrier below is to prevent the readers of reset_time from
|
||||
* seeing a stale or partially updated value.
|
||||
@ -249,7 +249,7 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy)
|
||||
stats->freq_table[i++] = pos->frequency;
|
||||
|
||||
stats->state_num = i;
|
||||
stats->last_time = get_jiffies_64();
|
||||
stats->last_time = local_clock();
|
||||
stats->last_index = freq_table_get_index(stats, policy->cur);
|
||||
|
||||
policy->stats = stats;
|
||||
|
@ -101,6 +101,13 @@ out_put_node:
|
||||
}
|
||||
module_init(hb_cpufreq_driver_init);
|
||||
|
||||
static const struct of_device_id __maybe_unused hb_cpufreq_of_match[] = {
|
||||
{ .compatible = "calxeda,highbank" },
|
||||
{ .compatible = "calxeda,ecx-2000" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hb_cpufreq_of_match);
|
||||
|
||||
MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>");
|
||||
MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -2569,14 +2569,13 @@ static int intel_cpufreq_update_pstate(struct cpufreq_policy *policy,
|
||||
int old_pstate = cpu->pstate.current_pstate;
|
||||
|
||||
target_pstate = intel_pstate_prepare_request(cpu, target_pstate);
|
||||
if (hwp_active) {
|
||||
if (hwp_active)
|
||||
intel_cpufreq_adjust_hwp(cpu, target_pstate,
|
||||
policy->strict_target, fast_switch);
|
||||
cpu->pstate.current_pstate = target_pstate;
|
||||
} else if (target_pstate != old_pstate) {
|
||||
else if (target_pstate != old_pstate)
|
||||
intel_cpufreq_adjust_perf_ctl(cpu, target_pstate, fast_switch);
|
||||
cpu->pstate.current_pstate = target_pstate;
|
||||
}
|
||||
|
||||
cpu->pstate.current_pstate = target_pstate;
|
||||
|
||||
intel_cpufreq_trace(cpu, fast_switch ? INTEL_PSTATE_TRACE_FAST_SWITCH :
|
||||
INTEL_PSTATE_TRACE_TARGET, old_pstate);
|
||||
|
@ -216,6 +216,7 @@ static struct platform_driver ls1x_cpufreq_platdrv = {
|
||||
|
||||
module_platform_driver(ls1x_cpufreq_platdrv);
|
||||
|
||||
MODULE_ALIAS("platform:ls1x-cpufreq");
|
||||
MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
|
||||
MODULE_DESCRIPTION("Loongson1 CPUFreq driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -532,6 +532,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
|
||||
{ .compatible = "mediatek,mt2712", },
|
||||
{ .compatible = "mediatek,mt7622", },
|
||||
{ .compatible = "mediatek,mt7623", },
|
||||
{ .compatible = "mediatek,mt8167", },
|
||||
{ .compatible = "mediatek,mt817x", },
|
||||
{ .compatible = "mediatek,mt8173", },
|
||||
{ .compatible = "mediatek,mt8176", },
|
||||
@ -540,6 +541,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
|
||||
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines);
|
||||
|
||||
static int __init mtk_cpufreq_driver_init(void)
|
||||
{
|
||||
@ -572,6 +574,7 @@ static int __init mtk_cpufreq_driver_init(void)
|
||||
pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0);
|
||||
if (IS_ERR(pdev)) {
|
||||
pr_err("failed to register mtk-cpufreq platform device\n");
|
||||
platform_driver_unregister(&mtk_cpufreq_platdrv);
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
|
@ -461,6 +461,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
|
||||
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list);
|
||||
|
||||
/*
|
||||
* Since the driver depends on smem and nvmem drivers, which may
|
||||
|
@ -126,6 +126,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
struct scmi_data *priv;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
|
||||
bool power_scale_mw;
|
||||
|
||||
cpu_dev = get_cpu_device(policy->cpu);
|
||||
if (!cpu_dev) {
|
||||
@ -189,7 +190,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
policy->fast_switch_possible =
|
||||
handle->perf_ops->fast_switch_possible(handle, cpu_dev);
|
||||
|
||||
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus);
|
||||
power_scale_mw = handle->perf_ops->power_scale_mw_get(handle);
|
||||
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus,
|
||||
power_scale_mw);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -233,6 +233,7 @@ static struct platform_driver scpi_cpufreq_platdrv = {
|
||||
};
|
||||
module_platform_driver(scpi_cpufreq_platdrv);
|
||||
|
||||
MODULE_ALIAS("platform:scpi-cpufreq");
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -223,7 +223,8 @@ use_defaults:
|
||||
opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
|
||||
if (IS_ERR(opp_table)) {
|
||||
dev_err(dev, "Failed to set supported hardware\n");
|
||||
return PTR_ERR(opp_table);
|
||||
ret = PTR_ERR(opp_table);
|
||||
goto err_put_prop_name;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
|
||||
@ -232,6 +233,10 @@ use_defaults:
|
||||
version[0], version[1], version[2]);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_prop_name:
|
||||
dev_pm_opp_put_prop_name(opp_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sti_cpufreq_fetch_syscon_registers(void)
|
||||
@ -292,6 +297,13 @@ register_cpufreq_dt:
|
||||
}
|
||||
module_init(sti_cpufreq_init);
|
||||
|
||||
static const struct of_device_id __maybe_unused sti_cpufreq_of_match[] = {
|
||||
{ .compatible = "st,stih407" },
|
||||
{ .compatible = "st,stih410" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sti_cpufreq_of_match);
|
||||
|
||||
MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
|
||||
MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
|
||||
MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
|
||||
|
@ -167,6 +167,7 @@ static const struct of_device_id sun50i_cpufreq_match_list[] = {
|
||||
{ .compatible = "allwinner,sun50i-h6" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list);
|
||||
|
||||
static const struct of_device_id *sun50i_cpufreq_match_node(void)
|
||||
{
|
||||
|
@ -12,35 +12,52 @@
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include <soc/tegra/bpmp-abi.h>
|
||||
|
||||
#define EDVD_CORE_VOLT_FREQ(core) (0x20 + (core) * 0x4)
|
||||
#define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
|
||||
#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff
|
||||
#define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
|
||||
#define TEGRA186_NUM_CLUSTERS 2
|
||||
#define EDVD_OFFSET_A57(core) ((SZ_64K * 6) + (0x20 + (core) * 0x4))
|
||||
#define EDVD_OFFSET_DENVER(core) ((SZ_64K * 7) + (0x20 + (core) * 0x4))
|
||||
#define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
|
||||
#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff
|
||||
#define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
|
||||
|
||||
struct tegra186_cpufreq_cluster_info {
|
||||
unsigned long offset;
|
||||
int cpus[4];
|
||||
struct tegra186_cpufreq_cpu {
|
||||
unsigned int bpmp_cluster_id;
|
||||
unsigned int edvd_offset;
|
||||
};
|
||||
|
||||
#define NO_CPU -1
|
||||
static const struct tegra186_cpufreq_cluster_info tegra186_clusters[] = {
|
||||
/* Denver cluster */
|
||||
static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
|
||||
/* CPU0 - A57 Cluster */
|
||||
{
|
||||
.offset = SZ_64K * 7,
|
||||
.cpus = { 1, 2, NO_CPU, NO_CPU },
|
||||
.bpmp_cluster_id = 0,
|
||||
},
|
||||
/* A57 cluster */
|
||||
{
|
||||
.offset = SZ_64K * 6,
|
||||
.cpus = { 0, 3, 4, 5 },
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(0)
|
||||
},
|
||||
/* CPU1 - Denver Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 0,
|
||||
.edvd_offset = EDVD_OFFSET_DENVER(0)
|
||||
},
|
||||
/* CPU2 - Denver Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 0,
|
||||
.edvd_offset = EDVD_OFFSET_DENVER(1)
|
||||
},
|
||||
/* CPU3 - A57 Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(1)
|
||||
},
|
||||
/* CPU4 - A57 Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(2)
|
||||
},
|
||||
/* CPU5 - A57 Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(3)
|
||||
},
|
||||
};
|
||||
|
||||
struct tegra186_cpufreq_cluster {
|
||||
const struct tegra186_cpufreq_cluster_info *info;
|
||||
struct cpufreq_frequency_table *table;
|
||||
u32 ref_clk_khz;
|
||||
u32 div;
|
||||
@ -48,36 +65,18 @@ struct tegra186_cpufreq_cluster {
|
||||
|
||||
struct tegra186_cpufreq_data {
|
||||
void __iomem *regs;
|
||||
|
||||
size_t num_clusters;
|
||||
struct tegra186_cpufreq_cluster *clusters;
|
||||
const struct tegra186_cpufreq_cpu *cpus;
|
||||
};
|
||||
|
||||
static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < data->num_clusters; i++) {
|
||||
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
|
||||
const struct tegra186_cpufreq_cluster_info *info =
|
||||
cluster->info;
|
||||
int core;
|
||||
|
||||
for (core = 0; core < ARRAY_SIZE(info->cpus); core++) {
|
||||
if (info->cpus[core] == policy->cpu)
|
||||
break;
|
||||
}
|
||||
if (core == ARRAY_SIZE(info->cpus))
|
||||
continue;
|
||||
|
||||
policy->driver_data =
|
||||
data->regs + info->offset + EDVD_CORE_VOLT_FREQ(core);
|
||||
policy->freq_table = cluster->table;
|
||||
break;
|
||||
}
|
||||
unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;
|
||||
|
||||
policy->freq_table = data->clusters[cluster].table;
|
||||
policy->cpuinfo.transition_latency = 300 * 1000;
|
||||
policy->driver_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -85,11 +84,12 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
|
||||
static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||
unsigned int index)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct cpufreq_frequency_table *tbl = policy->freq_table + index;
|
||||
void __iomem *edvd_reg = policy->driver_data;
|
||||
unsigned int edvd_offset = data->cpus[policy->cpu].edvd_offset;
|
||||
u32 edvd_val = tbl->driver_data;
|
||||
|
||||
writel(edvd_val, edvd_reg);
|
||||
writel(edvd_val, data->regs + edvd_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -97,35 +97,22 @@ static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||
static unsigned int tegra186_cpufreq_get(unsigned int cpu)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct tegra186_cpufreq_cluster *cluster;
|
||||
struct cpufreq_policy *policy;
|
||||
void __iomem *edvd_reg;
|
||||
unsigned int i, freq = 0;
|
||||
unsigned int edvd_offset, cluster_id;
|
||||
u32 ndiv;
|
||||
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
if (!policy)
|
||||
return 0;
|
||||
|
||||
edvd_reg = policy->driver_data;
|
||||
ndiv = readl(edvd_reg) & EDVD_CORE_VOLT_FREQ_F_MASK;
|
||||
|
||||
for (i = 0; i < data->num_clusters; i++) {
|
||||
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
|
||||
int core;
|
||||
|
||||
for (core = 0; core < ARRAY_SIZE(cluster->info->cpus); core++) {
|
||||
if (cluster->info->cpus[core] != policy->cpu)
|
||||
continue;
|
||||
|
||||
freq = (cluster->ref_clk_khz * ndiv) / cluster->div;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
edvd_offset = data->cpus[policy->cpu].edvd_offset;
|
||||
ndiv = readl(data->regs + edvd_offset) & EDVD_CORE_VOLT_FREQ_F_MASK;
|
||||
cluster_id = data->cpus[policy->cpu].bpmp_cluster_id;
|
||||
cluster = &data->clusters[cluster_id];
|
||||
cpufreq_cpu_put(policy);
|
||||
|
||||
return freq;
|
||||
return (cluster->ref_clk_khz * ndiv) / cluster->div;
|
||||
}
|
||||
|
||||
static struct cpufreq_driver tegra186_cpufreq_driver = {
|
||||
@ -141,7 +128,7 @@ static struct cpufreq_driver tegra186_cpufreq_driver = {
|
||||
|
||||
static struct cpufreq_frequency_table *init_vhint_table(
|
||||
struct platform_device *pdev, struct tegra_bpmp *bpmp,
|
||||
struct tegra186_cpufreq_cluster *cluster)
|
||||
struct tegra186_cpufreq_cluster *cluster, unsigned int cluster_id)
|
||||
{
|
||||
struct cpufreq_frequency_table *table;
|
||||
struct mrq_cpu_vhint_request req;
|
||||
@ -160,7 +147,7 @@ static struct cpufreq_frequency_table *init_vhint_table(
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.addr = phys;
|
||||
req.cluster_id = cluster->info->bpmp_cluster_id;
|
||||
req.cluster_id = cluster_id;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.mrq = MRQ_CPU_VHINT;
|
||||
@ -234,12 +221,12 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->clusters = devm_kcalloc(&pdev->dev, ARRAY_SIZE(tegra186_clusters),
|
||||
data->clusters = devm_kcalloc(&pdev->dev, TEGRA186_NUM_CLUSTERS,
|
||||
sizeof(*data->clusters), GFP_KERNEL);
|
||||
if (!data->clusters)
|
||||
return -ENOMEM;
|
||||
|
||||
data->num_clusters = ARRAY_SIZE(tegra186_clusters);
|
||||
data->cpus = tegra186_cpus;
|
||||
|
||||
bpmp = tegra_bpmp_get(&pdev->dev);
|
||||
if (IS_ERR(bpmp))
|
||||
@ -251,11 +238,10 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->num_clusters; i++) {
|
||||
for (i = 0; i < TEGRA186_NUM_CLUSTERS; i++) {
|
||||
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
|
||||
|
||||
cluster->info = &tegra186_clusters[i];
|
||||
cluster->table = init_vhint_table(pdev, bpmp, cluster);
|
||||
cluster->table = init_vhint_table(pdev, bpmp, cluster, i);
|
||||
if (IS_ERR(cluster->table)) {
|
||||
err = PTR_ERR(cluster->table);
|
||||
goto put_bpmp;
|
||||
|
@ -21,7 +21,6 @@
|
||||
#define KHZ 1000
|
||||
#define REF_CLK_MHZ 408 /* 408 MHz */
|
||||
#define US_DELAY 500
|
||||
#define US_DELAY_MIN 2
|
||||
#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ)
|
||||
#define MAX_CNT ~0U
|
||||
|
||||
@ -44,7 +43,6 @@ struct tegra194_cpufreq_data {
|
||||
|
||||
struct tegra_cpu_ctr {
|
||||
u32 cpu;
|
||||
u32 delay;
|
||||
u32 coreclk_cnt, last_coreclk_cnt;
|
||||
u32 refclk_cnt, last_refclk_cnt;
|
||||
};
|
||||
@ -112,7 +110,7 @@ static void tegra_read_counters(struct work_struct *work)
|
||||
val = read_freq_feedback();
|
||||
c->last_refclk_cnt = lower_32_bits(val);
|
||||
c->last_coreclk_cnt = upper_32_bits(val);
|
||||
udelay(c->delay);
|
||||
udelay(US_DELAY);
|
||||
val = read_freq_feedback();
|
||||
c->refclk_cnt = lower_32_bits(val);
|
||||
c->coreclk_cnt = upper_32_bits(val);
|
||||
@ -139,7 +137,7 @@ static void tegra_read_counters(struct work_struct *work)
|
||||
* @cpu - logical cpu whose freq to be updated
|
||||
* Returns freq in KHz on success, 0 if cpu is offline
|
||||
*/
|
||||
static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
|
||||
static unsigned int tegra194_calculate_speed(u32 cpu)
|
||||
{
|
||||
struct read_counters_work read_counters_work;
|
||||
struct tegra_cpu_ctr c;
|
||||
@ -153,7 +151,6 @@ static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
|
||||
* interrupts enabled.
|
||||
*/
|
||||
read_counters_work.c.cpu = cpu;
|
||||
read_counters_work.c.delay = delay;
|
||||
INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters);
|
||||
queue_work_on(cpu, read_counters_wq, &read_counters_work.work);
|
||||
flush_work(&read_counters_work.work);
|
||||
@ -180,9 +177,61 @@ static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
|
||||
return (rate_mhz * KHZ); /* in KHz */
|
||||
}
|
||||
|
||||
static void get_cpu_ndiv(void *ndiv)
|
||||
{
|
||||
u64 ndiv_val;
|
||||
|
||||
asm volatile("mrs %0, s3_0_c15_c0_4" : "=r" (ndiv_val) : );
|
||||
|
||||
*(u64 *)ndiv = ndiv_val;
|
||||
}
|
||||
|
||||
static void set_cpu_ndiv(void *data)
|
||||
{
|
||||
struct cpufreq_frequency_table *tbl = data;
|
||||
u64 ndiv_val = (u64)tbl->driver_data;
|
||||
|
||||
asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val));
|
||||
}
|
||||
|
||||
static unsigned int tegra194_get_speed(u32 cpu)
|
||||
{
|
||||
return tegra194_get_speed_common(cpu, US_DELAY);
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct cpufreq_frequency_table *pos;
|
||||
unsigned int rate;
|
||||
u64 ndiv;
|
||||
int ret;
|
||||
u32 cl;
|
||||
|
||||
smp_call_function_single(cpu, get_cpu_cluster, &cl, true);
|
||||
|
||||
/* reconstruct actual cpu freq using counters */
|
||||
rate = tegra194_calculate_speed(cpu);
|
||||
|
||||
/* get last written ndiv value */
|
||||
ret = smp_call_function_single(cpu, get_cpu_ndiv, &ndiv, true);
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return rate;
|
||||
|
||||
/*
|
||||
* If the reconstructed frequency has acceptable delta from
|
||||
* the last written value, then return freq corresponding
|
||||
* to the last written ndiv value from freq_table. This is
|
||||
* done to return consistent value.
|
||||
*/
|
||||
cpufreq_for_each_valid_entry(pos, data->tables[cl]) {
|
||||
if (pos->driver_data != ndiv)
|
||||
continue;
|
||||
|
||||
if (abs(pos->frequency - rate) > 115200) {
|
||||
pr_warn("cpufreq: cpu%d,cur:%u,set:%u,set ndiv:%llu\n",
|
||||
cpu, rate, pos->frequency, ndiv);
|
||||
} else {
|
||||
rate = pos->frequency;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
|
||||
@ -196,9 +245,6 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
|
||||
if (cl >= data->num_clusters)
|
||||
return -EINVAL;
|
||||
|
||||
/* boot freq */
|
||||
policy->cur = tegra194_get_speed_common(policy->cpu, US_DELAY_MIN);
|
||||
|
||||
/* set same policy for all cpus in a cluster */
|
||||
for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++)
|
||||
cpumask_set_cpu(cpu, policy->cpus);
|
||||
@ -209,14 +255,6 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_cpu_ndiv(void *data)
|
||||
{
|
||||
struct cpufreq_frequency_table *tbl = data;
|
||||
u64 ndiv_val = (u64)tbl->driver_data;
|
||||
|
||||
asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val));
|
||||
}
|
||||
|
||||
static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||
unsigned int index)
|
||||
{
|
||||
|
@ -591,6 +591,7 @@ static struct platform_driver ve_spc_cpufreq_platdrv = {
|
||||
};
|
||||
module_platform_driver(ve_spc_cpufreq_platdrv);
|
||||
|
||||
MODULE_ALIAS("platform:vexpress-spc-cpufreq");
|
||||
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("Vexpress SPC ARM big LITTLE cpufreq driver");
|
||||
|
@ -750,6 +750,13 @@ static bool scmi_fast_switch_possible(const struct scmi_handle *handle,
|
||||
return dom->fc_info && dom->fc_info->level_set_addr;
|
||||
}
|
||||
|
||||
static bool scmi_power_scale_mw_get(const struct scmi_handle *handle)
|
||||
{
|
||||
struct scmi_perf_info *pi = handle->perf_priv;
|
||||
|
||||
return pi->power_scale_mw;
|
||||
}
|
||||
|
||||
static const struct scmi_perf_ops perf_ops = {
|
||||
.limits_set = scmi_perf_limits_set,
|
||||
.limits_get = scmi_perf_limits_get,
|
||||
@ -762,6 +769,7 @@ static const struct scmi_perf_ops perf_ops = {
|
||||
.freq_get = scmi_dvfs_freq_get,
|
||||
.est_power_get = scmi_dvfs_est_power_get,
|
||||
.fast_switch_possible = scmi_fast_switch_possible,
|
||||
.power_scale_mw_get = scmi_power_scale_mw_get,
|
||||
};
|
||||
|
||||
static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
|
||||
|
@ -1339,7 +1339,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus);
|
||||
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
|
@ -65,7 +65,6 @@ struct cpufreq_policy {
|
||||
unsigned int max; /* in kHz */
|
||||
unsigned int cur; /* in kHz, only needed if cpufreq
|
||||
* governors are used */
|
||||
unsigned int restore_freq; /* = policy->cur before transition */
|
||||
unsigned int suspend_freq; /* freq to set during suspend */
|
||||
|
||||
unsigned int policy; /* see above */
|
||||
@ -314,10 +313,6 @@ struct cpufreq_driver {
|
||||
/* define one out of two */
|
||||
int (*setpolicy)(struct cpufreq_policy *policy);
|
||||
|
||||
/*
|
||||
* On failure, should always restore frequency to policy->restore_freq
|
||||
* (i.e. old freq).
|
||||
*/
|
||||
int (*target)(struct cpufreq_policy *policy,
|
||||
unsigned int target_freq,
|
||||
unsigned int relation); /* Deprecated */
|
||||
|
@ -29,6 +29,8 @@ struct em_perf_state {
|
||||
* em_perf_domain - Performance domain
|
||||
* @table: List of performance states, in ascending order
|
||||
* @nr_perf_states: Number of performance states
|
||||
* @milliwatts: Flag indicating the power values are in milli-Watts
|
||||
* or some other scale.
|
||||
* @cpus: Cpumask covering the CPUs of the domain. It's here
|
||||
* for performance reasons to avoid potential cache
|
||||
* misses during energy calculations in the scheduler
|
||||
@ -43,6 +45,7 @@ struct em_perf_state {
|
||||
struct em_perf_domain {
|
||||
struct em_perf_state *table;
|
||||
int nr_perf_states;
|
||||
int milliwatts;
|
||||
unsigned long cpus[];
|
||||
};
|
||||
|
||||
@ -79,7 +82,8 @@ struct em_data_callback {
|
||||
struct em_perf_domain *em_cpu_get(int cpu);
|
||||
struct em_perf_domain *em_pd_get(struct device *dev);
|
||||
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
||||
struct em_data_callback *cb, cpumask_t *span);
|
||||
struct em_data_callback *cb, cpumask_t *span,
|
||||
bool milliwatts);
|
||||
void em_dev_unregister_perf_domain(struct device *dev);
|
||||
|
||||
/**
|
||||
@ -186,7 +190,8 @@ struct em_data_callback {};
|
||||
|
||||
static inline
|
||||
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
||||
struct em_data_callback *cb, cpumask_t *span)
|
||||
struct em_data_callback *cb, cpumask_t *span,
|
||||
bool milliwatts)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ struct scmi_perf_ops {
|
||||
unsigned long *rate, unsigned long *power);
|
||||
bool (*fast_switch_possible)(const struct scmi_handle *handle,
|
||||
struct device *dev);
|
||||
bool (*power_scale_mw_get)(const struct scmi_handle *handle);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -52,6 +52,17 @@ static int em_debug_cpus_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(em_debug_cpus);
|
||||
|
||||
static int em_debug_units_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct em_perf_domain *pd = s->private;
|
||||
char *units = pd->milliwatts ? "milliWatts" : "bogoWatts";
|
||||
|
||||
seq_printf(s, "%s\n", units);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(em_debug_units);
|
||||
|
||||
static void em_debug_create_pd(struct device *dev)
|
||||
{
|
||||
struct dentry *d;
|
||||
@ -64,6 +75,8 @@ static void em_debug_create_pd(struct device *dev)
|
||||
debugfs_create_file("cpus", 0444, d, dev->em_pd->cpus,
|
||||
&em_debug_cpus_fops);
|
||||
|
||||
debugfs_create_file("units", 0444, d, dev->em_pd, &em_debug_units_fops);
|
||||
|
||||
/* Create a sub-directory for each performance state */
|
||||
for (i = 0; i < dev->em_pd->nr_perf_states; i++)
|
||||
em_debug_create_ps(&dev->em_pd->table[i], d);
|
||||
@ -250,17 +263,24 @@ EXPORT_SYMBOL_GPL(em_cpu_get);
|
||||
* @cpus : Pointer to cpumask_t, which in case of a CPU device is
|
||||
* obligatory. It can be taken from i.e. 'policy->cpus'. For other
|
||||
* type of devices this should be set to NULL.
|
||||
* @milliwatts : Flag indicating that the power values are in milliWatts or
|
||||
* in some other scale. It must be set properly.
|
||||
*
|
||||
* Create Energy Model tables for a performance domain using the callbacks
|
||||
* defined in cb.
|
||||
*
|
||||
* The @milliwatts is important to set with correct value. Some kernel
|
||||
* sub-systems might rely on this flag and check if all devices in the EM are
|
||||
* using the same scale.
|
||||
*
|
||||
* If multiple clients register the same performance domain, all but the first
|
||||
* registration will be ignored.
|
||||
*
|
||||
* Return 0 on success
|
||||
*/
|
||||
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
||||
struct em_data_callback *cb, cpumask_t *cpus)
|
||||
struct em_data_callback *cb, cpumask_t *cpus,
|
||||
bool milliwatts)
|
||||
{
|
||||
unsigned long cap, prev_cap = 0;
|
||||
int cpu, ret;
|
||||
@ -313,6 +333,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
dev->em_pd->milliwatts = milliwatts;
|
||||
|
||||
em_debug_create_pd(dev);
|
||||
dev_info(dev, "EM: created perf domain\n");
|
||||
|
||||
|
@ -102,12 +102,10 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
|
||||
static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time,
|
||||
unsigned int next_freq)
|
||||
{
|
||||
if (!sg_policy->need_freq_update) {
|
||||
if (sg_policy->next_freq == next_freq)
|
||||
return false;
|
||||
} else {
|
||||
if (sg_policy->need_freq_update)
|
||||
sg_policy->need_freq_update = cpufreq_driver_test_flags(CPUFREQ_NEED_UPDATE_LIMITS);
|
||||
}
|
||||
else if (sg_policy->next_freq == next_freq)
|
||||
return false;
|
||||
|
||||
sg_policy->next_freq = next_freq;
|
||||
sg_policy->last_freq_update_time = time;
|
||||
|
Loading…
Reference in New Issue
Block a user