162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2021 Linaro Limited
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * The devfreq device combined with the energy model and the load can
862306a36Sopenharmony_ci * give an estimation of the power consumption as well as limiting the
962306a36Sopenharmony_ci * power.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/cpumask.h>
1562306a36Sopenharmony_ci#include <linux/devfreq.h>
1662306a36Sopenharmony_ci#include <linux/dtpm.h>
1762306a36Sopenharmony_ci#include <linux/energy_model.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/pm_qos.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/units.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct dtpm_devfreq {
2462306a36Sopenharmony_ci	struct dtpm dtpm;
2562306a36Sopenharmony_ci	struct dev_pm_qos_request qos_req;
2662306a36Sopenharmony_ci	struct devfreq *devfreq;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic struct dtpm_devfreq *to_dtpm_devfreq(struct dtpm *dtpm)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	return container_of(dtpm, struct dtpm_devfreq, dtpm);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int update_pd_power_uw(struct dtpm *dtpm)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
3762306a36Sopenharmony_ci	struct devfreq *devfreq = dtpm_devfreq->devfreq;
3862306a36Sopenharmony_ci	struct device *dev = devfreq->dev.parent;
3962306a36Sopenharmony_ci	struct em_perf_domain *pd = em_pd_get(dev);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	dtpm->power_min = pd->table[0].power;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	dtpm->power_max = pd->table[pd->nr_perf_states - 1].power;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return 0;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
5162306a36Sopenharmony_ci	struct devfreq *devfreq = dtpm_devfreq->devfreq;
5262306a36Sopenharmony_ci	struct device *dev = devfreq->dev.parent;
5362306a36Sopenharmony_ci	struct em_perf_domain *pd = em_pd_get(dev);
5462306a36Sopenharmony_ci	unsigned long freq;
5562306a36Sopenharmony_ci	int i;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	for (i = 0; i < pd->nr_perf_states; i++) {
5862306a36Sopenharmony_ci		if (pd->table[i].power > power_limit)
5962306a36Sopenharmony_ci			break;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	freq = pd->table[i - 1].frequency;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	dev_pm_qos_update_request(&dtpm_devfreq->qos_req, freq);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	power_limit = pd->table[i - 1].power;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return power_limit;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void _normalize_load(struct devfreq_dev_status *status)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (status->total_time > 0xfffff) {
7462306a36Sopenharmony_ci		status->total_time >>= 10;
7562306a36Sopenharmony_ci		status->busy_time >>= 10;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	status->busy_time <<= 10;
7962306a36Sopenharmony_ci	status->busy_time /= status->total_time ? : 1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	status->busy_time = status->busy_time ? : 1;
8262306a36Sopenharmony_ci	status->total_time = 1024;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic u64 get_pd_power_uw(struct dtpm *dtpm)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
8862306a36Sopenharmony_ci	struct devfreq *devfreq = dtpm_devfreq->devfreq;
8962306a36Sopenharmony_ci	struct device *dev = devfreq->dev.parent;
9062306a36Sopenharmony_ci	struct em_perf_domain *pd = em_pd_get(dev);
9162306a36Sopenharmony_ci	struct devfreq_dev_status status;
9262306a36Sopenharmony_ci	unsigned long freq;
9362306a36Sopenharmony_ci	u64 power;
9462306a36Sopenharmony_ci	int i;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	mutex_lock(&devfreq->lock);
9762306a36Sopenharmony_ci	status = devfreq->last_status;
9862306a36Sopenharmony_ci	mutex_unlock(&devfreq->lock);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	freq = DIV_ROUND_UP(status.current_frequency, HZ_PER_KHZ);
10162306a36Sopenharmony_ci	_normalize_load(&status);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for (i = 0; i < pd->nr_perf_states; i++) {
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		if (pd->table[i].frequency < freq)
10662306a36Sopenharmony_ci			continue;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		power = pd->table[i].power;
10962306a36Sopenharmony_ci		power *= status.busy_time;
11062306a36Sopenharmony_ci		power >>= 10;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		return power;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void pd_release(struct dtpm *dtpm)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (dev_pm_qos_request_active(&dtpm_devfreq->qos_req))
12362306a36Sopenharmony_ci		dev_pm_qos_remove_request(&dtpm_devfreq->qos_req);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	kfree(dtpm_devfreq);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic struct dtpm_ops dtpm_ops = {
12962306a36Sopenharmony_ci	.set_power_uw = set_pd_power_limit,
13062306a36Sopenharmony_ci	.get_power_uw = get_pd_power_uw,
13162306a36Sopenharmony_ci	.update_power_uw = update_pd_power_uw,
13262306a36Sopenharmony_ci	.release = pd_release,
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int __dtpm_devfreq_setup(struct devfreq *devfreq, struct dtpm *parent)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct device *dev = devfreq->dev.parent;
13862306a36Sopenharmony_ci	struct dtpm_devfreq *dtpm_devfreq;
13962306a36Sopenharmony_ci	struct em_perf_domain *pd;
14062306a36Sopenharmony_ci	int ret = -ENOMEM;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	pd = em_pd_get(dev);
14362306a36Sopenharmony_ci	if (!pd) {
14462306a36Sopenharmony_ci		ret = dev_pm_opp_of_register_em(dev, NULL);
14562306a36Sopenharmony_ci		if (ret) {
14662306a36Sopenharmony_ci			pr_err("No energy model available for '%s'\n", dev_name(dev));
14762306a36Sopenharmony_ci			return -EINVAL;
14862306a36Sopenharmony_ci		}
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	dtpm_devfreq = kzalloc(sizeof(*dtpm_devfreq), GFP_KERNEL);
15262306a36Sopenharmony_ci	if (!dtpm_devfreq)
15362306a36Sopenharmony_ci		return -ENOMEM;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	dtpm_init(&dtpm_devfreq->dtpm, &dtpm_ops);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	dtpm_devfreq->devfreq = devfreq;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = dtpm_register(dev_name(dev), &dtpm_devfreq->dtpm, parent);
16062306a36Sopenharmony_ci	if (ret) {
16162306a36Sopenharmony_ci		pr_err("Failed to register '%s': %d\n", dev_name(dev), ret);
16262306a36Sopenharmony_ci		kfree(dtpm_devfreq);
16362306a36Sopenharmony_ci		return ret;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ret = dev_pm_qos_add_request(dev, &dtpm_devfreq->qos_req,
16762306a36Sopenharmony_ci				     DEV_PM_QOS_MAX_FREQUENCY,
16862306a36Sopenharmony_ci				     PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
16962306a36Sopenharmony_ci	if (ret) {
17062306a36Sopenharmony_ci		pr_err("Failed to add QoS request: %d\n", ret);
17162306a36Sopenharmony_ci		goto out_dtpm_unregister;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	dtpm_update_power(&dtpm_devfreq->dtpm);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciout_dtpm_unregister:
17962306a36Sopenharmony_ci	dtpm_unregister(&dtpm_devfreq->dtpm);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return ret;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int dtpm_devfreq_setup(struct dtpm *dtpm, struct device_node *np)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct devfreq *devfreq;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	devfreq = devfreq_get_devfreq_by_node(np);
18962306a36Sopenharmony_ci	if (IS_ERR(devfreq))
19062306a36Sopenharmony_ci		return 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return __dtpm_devfreq_setup(devfreq, dtpm);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistruct dtpm_subsys_ops dtpm_devfreq_ops = {
19662306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
19762306a36Sopenharmony_ci	.setup = dtpm_devfreq_setup,
19862306a36Sopenharmony_ci};
199