summaryrefslogtreecommitdiff
path: root/drivers/cpufreq/cppc_cpufreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq/cppc_cpufreq.c')
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c164
1 files changed, 89 insertions, 75 deletions
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 9eac77c4f294..7e8042efedd1 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -54,31 +54,24 @@ static int cppc_perf_from_fbctrs(struct cppc_perf_fb_ctrs *fb_ctrs_t0,
struct cppc_perf_fb_ctrs *fb_ctrs_t1);
/**
- * cppc_scale_freq_workfn - CPPC arch_freq_scale updater for frequency invariance
- * @work: The work item.
+ * __cppc_scale_freq_tick - CPPC arch_freq_scale updater for frequency invariance
+ * @cppc_fi: per-cpu CPPC FIE data.
*
- * The CPPC driver register itself with the topology core to provide its own
+ * The CPPC driver registers itself with the topology core to provide its own
* implementation (cppc_scale_freq_tick()) of topology_scale_freq_tick() which
* gets called by the scheduler on every tick.
*
* Note that the arch specific counters have higher priority than CPPC counters,
* if available, though the CPPC driver doesn't need to have any special
* handling for that.
- *
- * On an invocation of cppc_scale_freq_tick(), we schedule an irq work (since we
- * reach here from hard-irq context), which then schedules a normal work item
- * and cppc_scale_freq_workfn() updates the per_cpu arch_freq_scale variable
- * based on the counter updates since the last tick.
*/
-static void cppc_scale_freq_workfn(struct kthread_work *work)
+static void __cppc_scale_freq_tick(struct cppc_freq_invariance *cppc_fi)
{
- struct cppc_freq_invariance *cppc_fi;
struct cppc_perf_fb_ctrs fb_ctrs = {0};
struct cppc_cpudata *cpu_data;
unsigned long local_freq_scale;
u64 perf;
- cppc_fi = container_of(work, struct cppc_freq_invariance, work);
cpu_data = cppc_fi->cpu_data;
if (cppc_get_perf_ctrs(cppc_fi->cpu, &fb_ctrs)) {
@@ -102,6 +95,24 @@ static void cppc_scale_freq_workfn(struct kthread_work *work)
per_cpu(arch_freq_scale, cppc_fi->cpu) = local_freq_scale;
}
+static void cppc_scale_freq_tick(void)
+{
+ __cppc_scale_freq_tick(&per_cpu(cppc_freq_inv, smp_processor_id()));
+}
+
+static struct scale_freq_data cppc_sftd = {
+ .source = SCALE_FREQ_SOURCE_CPPC,
+ .set_freq_scale = cppc_scale_freq_tick,
+};
+
+static void cppc_scale_freq_workfn(struct kthread_work *work)
+{
+ struct cppc_freq_invariance *cppc_fi;
+
+ cppc_fi = container_of(work, struct cppc_freq_invariance, work);
+ __cppc_scale_freq_tick(cppc_fi);
+}
+
static void cppc_irq_work(struct irq_work *irq_work)
{
struct cppc_freq_invariance *cppc_fi;
@@ -110,7 +121,14 @@ static void cppc_irq_work(struct irq_work *irq_work)
kthread_queue_work(kworker_fie, &cppc_fi->work);
}
-static void cppc_scale_freq_tick(void)
+/*
+ * Reading perf counters may sleep if the CPC regs are in PCC. Thus, we
+ * schedule an irq work in scale_freq_tick (since we reach here from hard-irq
+ * context), which then schedules a normal work item cppc_scale_freq_workfn()
+ * that updates the per_cpu arch_freq_scale variable based on the counter
+ * updates since the last tick.
+ */
+static void cppc_scale_freq_tick_pcc(void)
{
struct cppc_freq_invariance *cppc_fi = &per_cpu(cppc_freq_inv, smp_processor_id());
@@ -121,13 +139,14 @@ static void cppc_scale_freq_tick(void)
irq_work_queue(&cppc_fi->irq_work);
}
-static struct scale_freq_data cppc_sftd = {
+static struct scale_freq_data cppc_sftd_pcc = {
.source = SCALE_FREQ_SOURCE_CPPC,
- .set_freq_scale = cppc_scale_freq_tick,
+ .set_freq_scale = cppc_scale_freq_tick_pcc,
};
static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
{
+ struct scale_freq_data *sftd = &cppc_sftd;
struct cppc_freq_invariance *cppc_fi;
int cpu, ret;
@@ -138,8 +157,11 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
cppc_fi = &per_cpu(cppc_freq_inv, cpu);
cppc_fi->cpu = cpu;
cppc_fi->cpu_data = policy->driver_data;
- kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn);
- init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
+ if (cppc_perf_ctrs_in_pcc_cpu(cpu)) {
+ kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn);
+ init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
+ sftd = &cppc_sftd_pcc;
+ }
ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
@@ -155,7 +177,7 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
}
/* Register for freq-invariance */
- topology_set_scale_freq_source(&cppc_sftd, policy->cpus);
+ topology_set_scale_freq_source(sftd, policy->cpus);
}
/*
@@ -178,13 +200,15 @@ static void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy)
topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_CPPC, policy->related_cpus);
for_each_cpu(cpu, policy->related_cpus) {
+ if (!cppc_perf_ctrs_in_pcc_cpu(cpu))
+ continue;
cppc_fi = &per_cpu(cppc_freq_inv, cpu);
irq_work_sync(&cppc_fi->irq_work);
kthread_cancel_work_sync(&cppc_fi->work);
}
}
-static void __init cppc_freq_invariance_init(void)
+static void cppc_fie_kworker_init(void)
{
struct sched_attr attr = {
.size = sizeof(struct sched_attr),
@@ -201,22 +225,12 @@ static void __init cppc_freq_invariance_init(void)
};
int ret;
- if (fie_disabled != FIE_ENABLED && fie_disabled != FIE_DISABLED) {
- fie_disabled = FIE_ENABLED;
- if (cppc_perf_ctrs_in_pcc()) {
- pr_info("FIE not enabled on systems with registers in PCC\n");
- fie_disabled = FIE_DISABLED;
- }
- }
-
- if (fie_disabled)
- return;
-
kworker_fie = kthread_run_worker(0, "cppc_fie");
if (IS_ERR(kworker_fie)) {
pr_warn("%s: failed to create kworker_fie: %ld\n", __func__,
PTR_ERR(kworker_fie));
fie_disabled = FIE_DISABLED;
+ kworker_fie = NULL;
return;
}
@@ -226,15 +240,33 @@ static void __init cppc_freq_invariance_init(void)
ret);
kthread_destroy_worker(kworker_fie);
fie_disabled = FIE_DISABLED;
+ kworker_fie = NULL;
}
}
-static void cppc_freq_invariance_exit(void)
+static void __init cppc_freq_invariance_init(void)
{
- if (fie_disabled)
+ bool perf_ctrs_in_pcc = cppc_perf_ctrs_in_pcc();
+
+ if (fie_disabled == FIE_UNSET) {
+ if (perf_ctrs_in_pcc) {
+ pr_info("FIE not enabled on systems with registers in PCC\n");
+ fie_disabled = FIE_DISABLED;
+ } else {
+ fie_disabled = FIE_ENABLED;
+ }
+ }
+
+ if (fie_disabled || !perf_ctrs_in_pcc)
return;
- kthread_destroy_worker(kworker_fie);
+ cppc_fie_kworker_init();
+}
+
+static void cppc_freq_invariance_exit(void)
+{
+ if (kworker_fie)
+ kthread_destroy_worker(kworker_fie);
}
#else
@@ -831,14 +863,13 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
return count;
}
-static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
+static ssize_t cppc_cpufreq_sysfs_show_u64(unsigned int cpu,
+ int (*get_func)(int, u64 *),
+ char *buf)
{
u64 val;
- int ret;
+ int ret = get_func((int)cpu, &val);
- ret = cppc_get_auto_act_window(policy->cpu, &val);
-
- /* show "<unsupported>" when this register is not supported by cpc */
if (ret == -EOPNOTSUPP)
return sysfs_emit(buf, "<unsupported>\n");
@@ -848,56 +879,39 @@ static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
return sysfs_emit(buf, "%llu\n", val);
}
-static ssize_t store_auto_act_window(struct cpufreq_policy *policy,
- const char *buf, size_t count)
+static ssize_t cppc_cpufreq_sysfs_store_u64(unsigned int cpu,
+ int (*set_func)(int, u64),
+ const char *buf, size_t count)
{
- u64 usec;
+ u64 val;
int ret;
- ret = kstrtou64(buf, 0, &usec);
+ ret = kstrtou64(buf, 0, &val);
if (ret)
return ret;
- ret = cppc_set_auto_act_window(policy->cpu, usec);
- if (ret)
- return ret;
+ ret = set_func((int)cpu, val);
- return count;
+ return ret ? ret : count;
}
-static ssize_t show_energy_performance_preference_val(struct cpufreq_policy *policy, char *buf)
-{
- u64 val;
- int ret;
-
- ret = cppc_get_epp_perf(policy->cpu, &val);
-
- /* show "<unsupported>" when this register is not supported by cpc */
- if (ret == -EOPNOTSUPP)
- return sysfs_emit(buf, "<unsupported>\n");
-
- if (ret)
- return ret;
-
- return sysfs_emit(buf, "%llu\n", val);
+#define CPPC_CPUFREQ_ATTR_RW_U64(_name, _get_func, _set_func) \
+static ssize_t show_##_name(struct cpufreq_policy *policy, char *buf) \
+{ \
+ return cppc_cpufreq_sysfs_show_u64(policy->cpu, _get_func, buf);\
+} \
+static ssize_t store_##_name(struct cpufreq_policy *policy, \
+ const char *buf, size_t count) \
+{ \
+ return cppc_cpufreq_sysfs_store_u64(policy->cpu, _set_func, \
+ buf, count); \
}
-static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
- const char *buf, size_t count)
-{
- u64 val;
- int ret;
-
- ret = kstrtou64(buf, 0, &val);
- if (ret)
- return ret;
-
- ret = cppc_set_epp(policy->cpu, val);
- if (ret)
- return ret;
+CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
+ cppc_set_auto_act_window)
- return count;
-}
+CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
+ cppc_get_epp_perf, cppc_set_epp)
cpufreq_freq_attr_ro(freqdomain_cpus);
cpufreq_freq_attr_rw(auto_select);