Commit 34059ed0 authored by Sibi Sankar's avatar Sibi Sankar Committed by Viresh Kumar
Browse files

cpufreq: scmi: Register for limit change notifications



Register for limit change notifications if supported and use the throttled
frequency from the notification to apply HW pressure.

Signed-off-by: default avatarSibi Sankar <quic_sibis@quicinc.com>
Tested-by: default avatarMike Tipton <quic_mdtipton@quicinc.com>
Reviewed-by: default avatarCristian Marussi <cristian.marussi@arm.com>
Reviewed-by: default avatarLukasz Luba <lukasz.luba@arm.com>
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
parent 40384c84
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/export.h>
#include <linux/module.h>
#include <linux/pm_opp.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/scmi_protocol.h>
#include <linux/types.h>
@@ -26,6 +27,8 @@ struct scmi_data {
	int nr_opp;
	struct device *cpu_dev;
	cpumask_var_t opp_shared_cpus;
	struct notifier_block limit_notify_nb;
	struct freq_qos_request	limits_freq_req;
};

static struct scmi_protocol_handle *ph;
@@ -174,6 +177,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = {
	NULL,
};

static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data)
{
	struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb);
	struct scmi_perf_limits_report *limit_notify = data;
	unsigned int limit_freq_khz;
	int ret;

	limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ;

	ret = freq_qos_update_request(&priv->limits_freq_req, limit_freq_khz);
	if (ret < 0)
		pr_warn("failed to update freq constraint: %d\n", ret);

	return NOTIFY_OK;
}

static int scmi_cpufreq_init(struct cpufreq_policy *policy)
{
	int ret, nr_opp, domain;
@@ -181,6 +200,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
	struct device *cpu_dev;
	struct scmi_data *priv;
	struct cpufreq_frequency_table *freq_table;
	struct scmi_device *sdev = cpufreq_get_driver_data();

	cpu_dev = get_cpu_device(policy->cpu);
	if (!cpu_dev) {
@@ -294,6 +314,23 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
		}
	}

	ret = freq_qos_add_request(&policy->constraints, &priv->limits_freq_req, FREQ_QOS_MAX,
				   FREQ_QOS_MAX_DEFAULT_VALUE);
	if (ret < 0) {
		dev_err(cpu_dev, "failed to add qos limits request: %d\n", ret);
		goto out_free_table;
	}

	priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb;
	ret = sdev->handle->notify_ops->event_notifier_register(sdev->handle, SCMI_PROTOCOL_PERF,
							SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED,
							&priv->domain_id,
							&priv->limit_notify_nb);
	if (ret)
		dev_warn(&sdev->dev,
			 "failed to register for limits change notifier for domain %d\n",
			 priv->domain_id);

	return 0;

out_free_table:
@@ -313,7 +350,13 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
static void scmi_cpufreq_exit(struct cpufreq_policy *policy)
{
	struct scmi_data *priv = policy->driver_data;
	struct scmi_device *sdev = cpufreq_get_driver_data();

	sdev->handle->notify_ops->event_notifier_unregister(sdev->handle, SCMI_PROTOCOL_PERF,
							    SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED,
							    &priv->domain_id,
							    &priv->limit_notify_nb);
	freq_qos_remove_request(&priv->limits_freq_req);
	dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
	dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
	free_cpumask_var(priv->opp_shared_cpus);
@@ -372,6 +415,8 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
	if (!handle)
		return -ENODEV;

	scmi_cpufreq_driver.driver_data = sdev;

	perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph);
	if (IS_ERR(perf_ops))
		return PTR_ERR(perf_ops);