1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2021 Linaro Limited
5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
7 * The devfreq device combined with the energy model and the load can
8 * give an estimation of the power consumption as well as limiting the
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 #include <linux/cpumask.h>
15 #include <linux/devfreq.h>
16 #include <linux/dtpm.h>
17 #include <linux/energy_model.h>
19 #include <linux/pm_qos.h>
20 #include <linux/slab.h>
21 #include <linux/units.h>
25 struct dev_pm_qos_request qos_req;
26 struct devfreq *devfreq;
29 static struct dtpm_devfreq *to_dtpm_devfreq(struct dtpm *dtpm)
31 return container_of(dtpm, struct dtpm_devfreq, dtpm);
34 static int update_pd_power_uw(struct dtpm *dtpm)
36 struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
37 struct devfreq *devfreq = dtpm_devfreq->devfreq;
38 struct device *dev = devfreq->dev.parent;
39 struct em_perf_domain *pd = em_pd_get(dev);
41 dtpm->power_min = pd->table[0].power;
43 dtpm->power_max = pd->table[pd->nr_perf_states - 1].power;
48 static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
50 struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
51 struct devfreq *devfreq = dtpm_devfreq->devfreq;
52 struct device *dev = devfreq->dev.parent;
53 struct em_perf_domain *pd = em_pd_get(dev);
57 for (i = 0; i < pd->nr_perf_states; i++) {
58 if (pd->table[i].power > power_limit)
62 freq = pd->table[i - 1].frequency;
64 dev_pm_qos_update_request(&dtpm_devfreq->qos_req, freq);
66 power_limit = pd->table[i - 1].power;
71 static void _normalize_load(struct devfreq_dev_status *status)
73 if (status->total_time > 0xfffff) {
74 status->total_time >>= 10;
75 status->busy_time >>= 10;
78 status->busy_time <<= 10;
79 status->busy_time /= status->total_time ? : 1;
81 status->busy_time = status->busy_time ? : 1;
82 status->total_time = 1024;
85 static u64 get_pd_power_uw(struct dtpm *dtpm)
87 struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
88 struct devfreq *devfreq = dtpm_devfreq->devfreq;
89 struct device *dev = devfreq->dev.parent;
90 struct em_perf_domain *pd = em_pd_get(dev);
91 struct devfreq_dev_status status;
96 mutex_lock(&devfreq->lock);
97 status = devfreq->last_status;
98 mutex_unlock(&devfreq->lock);
100 freq = DIV_ROUND_UP(status.current_frequency, HZ_PER_KHZ);
101 _normalize_load(&status);
103 for (i = 0; i < pd->nr_perf_states; i++) {
105 if (pd->table[i].frequency < freq)
108 power = pd->table[i].power;
109 power *= status.busy_time;
118 static void pd_release(struct dtpm *dtpm)
120 struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
122 if (dev_pm_qos_request_active(&dtpm_devfreq->qos_req))
123 dev_pm_qos_remove_request(&dtpm_devfreq->qos_req);
128 static struct dtpm_ops dtpm_ops = {
129 .set_power_uw = set_pd_power_limit,
130 .get_power_uw = get_pd_power_uw,
131 .update_power_uw = update_pd_power_uw,
132 .release = pd_release,
135 static int __dtpm_devfreq_setup(struct devfreq *devfreq, struct dtpm *parent)
137 struct device *dev = devfreq->dev.parent;
138 struct dtpm_devfreq *dtpm_devfreq;
139 struct em_perf_domain *pd;
144 ret = dev_pm_opp_of_register_em(dev, NULL);
146 pr_err("No energy model available for '%s'\n", dev_name(dev));
151 dtpm_devfreq = kzalloc(sizeof(*dtpm_devfreq), GFP_KERNEL);
155 dtpm_init(&dtpm_devfreq->dtpm, &dtpm_ops);
157 dtpm_devfreq->devfreq = devfreq;
159 ret = dtpm_register(dev_name(dev), &dtpm_devfreq->dtpm, parent);
161 pr_err("Failed to register '%s': %d\n", dev_name(dev), ret);
166 ret = dev_pm_qos_add_request(dev, &dtpm_devfreq->qos_req,
167 DEV_PM_QOS_MAX_FREQUENCY,
168 PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
170 pr_err("Failed to add QoS request: %d\n", ret);
171 goto out_dtpm_unregister;
174 dtpm_update_power(&dtpm_devfreq->dtpm);
179 dtpm_unregister(&dtpm_devfreq->dtpm);
184 static int dtpm_devfreq_setup(struct dtpm *dtpm, struct device_node *np)
186 struct devfreq *devfreq;
188 devfreq = devfreq_get_devfreq_by_node(np);
192 return __dtpm_devfreq_setup(devfreq, dtpm);
195 struct dtpm_subsys_ops dtpm_devfreq_ops = {
196 .name = KBUILD_MODNAME,
197 .setup = dtpm_devfreq_setup,