Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright 2021 Linaro Limited
  4 *
  5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6 *
  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
  9 * power.
 10 *
 11 */
 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 13
 14#include <linux/cpumask.h>
 15#include <linux/devfreq.h>
 16#include <linux/dtpm.h>
 17#include <linux/energy_model.h>
 18#include <linux/of.h>
 19#include <linux/pm_qos.h>
 20#include <linux/slab.h>
 21#include <linux/units.h>
 22
 23struct dtpm_devfreq {
 24	struct dtpm dtpm;
 25	struct dev_pm_qos_request qos_req;
 26	struct devfreq *devfreq;
 27};
 28
 29static struct dtpm_devfreq *to_dtpm_devfreq(struct dtpm *dtpm)
 30{
 31	return container_of(dtpm, struct dtpm_devfreq, dtpm);
 32}
 33
 34static int update_pd_power_uw(struct dtpm *dtpm)
 35{
 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);
 40	struct em_perf_state *table;
 41
 42	rcu_read_lock();
 43	table = em_perf_state_from_pd(pd);
 44
 45	dtpm->power_min = table[0].power;
 
 46
 47	dtpm->power_max = table[pd->nr_perf_states - 1].power;
 48
 49	rcu_read_unlock();
 50	return 0;
 51}
 52
 53static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
 54{
 55	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
 56	struct devfreq *devfreq = dtpm_devfreq->devfreq;
 57	struct device *dev = devfreq->dev.parent;
 58	struct em_perf_domain *pd = em_pd_get(dev);
 59	struct em_perf_state *table;
 60	unsigned long freq;
 
 61	int i;
 62
 63	rcu_read_lock();
 64	table = em_perf_state_from_pd(pd);
 65	for (i = 0; i < pd->nr_perf_states; i++) {
 66		if (table[i].power > power_limit)
 
 
 67			break;
 68	}
 69
 70	freq = table[i - 1].frequency;
 71	power_limit = table[i - 1].power;
 72	rcu_read_unlock();
 73
 74	dev_pm_qos_update_request(&dtpm_devfreq->qos_req, freq);
 75
 
 
 76	return power_limit;
 77}
 78
 79static void _normalize_load(struct devfreq_dev_status *status)
 80{
 81	if (status->total_time > 0xfffff) {
 82		status->total_time >>= 10;
 83		status->busy_time >>= 10;
 84	}
 85
 86	status->busy_time <<= 10;
 87	status->busy_time /= status->total_time ? : 1;
 88
 89	status->busy_time = status->busy_time ? : 1;
 90	status->total_time = 1024;
 91}
 92
 93static u64 get_pd_power_uw(struct dtpm *dtpm)
 94{
 95	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
 96	struct devfreq *devfreq = dtpm_devfreq->devfreq;
 97	struct device *dev = devfreq->dev.parent;
 98	struct em_perf_domain *pd = em_pd_get(dev);
 99	struct devfreq_dev_status status;
100	struct em_perf_state *table;
101	unsigned long freq;
102	u64 power = 0;
103	int i;
104
105	mutex_lock(&devfreq->lock);
106	status = devfreq->last_status;
107	mutex_unlock(&devfreq->lock);
108
109	freq = DIV_ROUND_UP(status.current_frequency, HZ_PER_KHZ);
110	_normalize_load(&status);
111
112	rcu_read_lock();
113	table = em_perf_state_from_pd(pd);
114	for (i = 0; i < pd->nr_perf_states; i++) {
115
116		if (table[i].frequency < freq)
117			continue;
118
119		power = table[i].power;
120		power *= status.busy_time;
121		power >>= 10;
122
123		break;
124	}
125	rcu_read_unlock();
126
127	return power;
128}
129
130static void pd_release(struct dtpm *dtpm)
131{
132	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
133
134	if (dev_pm_qos_request_active(&dtpm_devfreq->qos_req))
135		dev_pm_qos_remove_request(&dtpm_devfreq->qos_req);
136
137	kfree(dtpm_devfreq);
138}
139
140static struct dtpm_ops dtpm_ops = {
141	.set_power_uw = set_pd_power_limit,
142	.get_power_uw = get_pd_power_uw,
143	.update_power_uw = update_pd_power_uw,
144	.release = pd_release,
145};
146
147static int __dtpm_devfreq_setup(struct devfreq *devfreq, struct dtpm *parent)
148{
149	struct device *dev = devfreq->dev.parent;
150	struct dtpm_devfreq *dtpm_devfreq;
151	struct em_perf_domain *pd;
152	int ret = -ENOMEM;
153
154	pd = em_pd_get(dev);
155	if (!pd) {
156		ret = dev_pm_opp_of_register_em(dev, NULL);
157		if (ret) {
158			pr_err("No energy model available for '%s'\n", dev_name(dev));
159			return -EINVAL;
160		}
161	}
162
163	dtpm_devfreq = kzalloc(sizeof(*dtpm_devfreq), GFP_KERNEL);
164	if (!dtpm_devfreq)
165		return -ENOMEM;
166
167	dtpm_init(&dtpm_devfreq->dtpm, &dtpm_ops);
168
169	dtpm_devfreq->devfreq = devfreq;
170
171	ret = dtpm_register(dev_name(dev), &dtpm_devfreq->dtpm, parent);
172	if (ret) {
173		pr_err("Failed to register '%s': %d\n", dev_name(dev), ret);
174		kfree(dtpm_devfreq);
175		return ret;
176	}
177
178	ret = dev_pm_qos_add_request(dev, &dtpm_devfreq->qos_req,
179				     DEV_PM_QOS_MAX_FREQUENCY,
180				     PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
181	if (ret < 0) {
182		pr_err("Failed to add QoS request: %d\n", ret);
183		goto out_dtpm_unregister;
184	}
185
186	dtpm_update_power(&dtpm_devfreq->dtpm);
187
188	return 0;
189
190out_dtpm_unregister:
191	dtpm_unregister(&dtpm_devfreq->dtpm);
192
193	return ret;
194}
195
196static int dtpm_devfreq_setup(struct dtpm *dtpm, struct device_node *np)
197{
198	struct devfreq *devfreq;
199
200	devfreq = devfreq_get_devfreq_by_node(np);
201	if (IS_ERR(devfreq))
202		return 0;
203
204	return __dtpm_devfreq_setup(devfreq, dtpm);
205}
206
207struct dtpm_subsys_ops dtpm_devfreq_ops = {
208	.name = KBUILD_MODNAME,
209	.setup = dtpm_devfreq_setup,
210};
v6.2
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright 2021 Linaro Limited
  4 *
  5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6 *
  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
  9 * power.
 10 *
 11 */
 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 13
 14#include <linux/cpumask.h>
 15#include <linux/devfreq.h>
 16#include <linux/dtpm.h>
 17#include <linux/energy_model.h>
 18#include <linux/of.h>
 19#include <linux/pm_qos.h>
 20#include <linux/slab.h>
 21#include <linux/units.h>
 22
 23struct dtpm_devfreq {
 24	struct dtpm dtpm;
 25	struct dev_pm_qos_request qos_req;
 26	struct devfreq *devfreq;
 27};
 28
 29static struct dtpm_devfreq *to_dtpm_devfreq(struct dtpm *dtpm)
 30{
 31	return container_of(dtpm, struct dtpm_devfreq, dtpm);
 32}
 33
 34static int update_pd_power_uw(struct dtpm *dtpm)
 35{
 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);
 
 40
 41	dtpm->power_min = pd->table[0].power;
 42	dtpm->power_min *= MICROWATT_PER_MILLIWATT;
 43
 44	dtpm->power_max = pd->table[pd->nr_perf_states - 1].power;
 45	dtpm->power_max *= MICROWATT_PER_MILLIWATT;
 46
 
 
 
 47	return 0;
 48}
 49
 50static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
 51{
 52	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
 53	struct devfreq *devfreq = dtpm_devfreq->devfreq;
 54	struct device *dev = devfreq->dev.parent;
 55	struct em_perf_domain *pd = em_pd_get(dev);
 
 56	unsigned long freq;
 57	u64 power;
 58	int i;
 59
 
 
 60	for (i = 0; i < pd->nr_perf_states; i++) {
 61
 62		power = pd->table[i].power * MICROWATT_PER_MILLIWATT;
 63		if (power > power_limit)
 64			break;
 65	}
 66
 67	freq = pd->table[i - 1].frequency;
 
 
 68
 69	dev_pm_qos_update_request(&dtpm_devfreq->qos_req, freq);
 70
 71	power_limit = pd->table[i - 1].power * MICROWATT_PER_MILLIWATT;
 72
 73	return power_limit;
 74}
 75
 76static void _normalize_load(struct devfreq_dev_status *status)
 77{
 78	if (status->total_time > 0xfffff) {
 79		status->total_time >>= 10;
 80		status->busy_time >>= 10;
 81	}
 82
 83	status->busy_time <<= 10;
 84	status->busy_time /= status->total_time ? : 1;
 85
 86	status->busy_time = status->busy_time ? : 1;
 87	status->total_time = 1024;
 88}
 89
 90static u64 get_pd_power_uw(struct dtpm *dtpm)
 91{
 92	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
 93	struct devfreq *devfreq = dtpm_devfreq->devfreq;
 94	struct device *dev = devfreq->dev.parent;
 95	struct em_perf_domain *pd = em_pd_get(dev);
 96	struct devfreq_dev_status status;
 
 97	unsigned long freq;
 98	u64 power;
 99	int i;
100
101	mutex_lock(&devfreq->lock);
102	status = devfreq->last_status;
103	mutex_unlock(&devfreq->lock);
104
105	freq = DIV_ROUND_UP(status.current_frequency, HZ_PER_KHZ);
106	_normalize_load(&status);
107
 
 
108	for (i = 0; i < pd->nr_perf_states; i++) {
109
110		if (pd->table[i].frequency < freq)
111			continue;
112
113		power = pd->table[i].power * MICROWATT_PER_MILLIWATT;
114		power *= status.busy_time;
115		power >>= 10;
116
117		return power;
118	}
 
119
120	return 0;
121}
122
123static void pd_release(struct dtpm *dtpm)
124{
125	struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
126
127	if (dev_pm_qos_request_active(&dtpm_devfreq->qos_req))
128		dev_pm_qos_remove_request(&dtpm_devfreq->qos_req);
129
130	kfree(dtpm_devfreq);
131}
132
133static struct dtpm_ops dtpm_ops = {
134	.set_power_uw = set_pd_power_limit,
135	.get_power_uw = get_pd_power_uw,
136	.update_power_uw = update_pd_power_uw,
137	.release = pd_release,
138};
139
140static int __dtpm_devfreq_setup(struct devfreq *devfreq, struct dtpm *parent)
141{
142	struct device *dev = devfreq->dev.parent;
143	struct dtpm_devfreq *dtpm_devfreq;
144	struct em_perf_domain *pd;
145	int ret = -ENOMEM;
146
147	pd = em_pd_get(dev);
148	if (!pd) {
149		ret = dev_pm_opp_of_register_em(dev, NULL);
150		if (ret) {
151			pr_err("No energy model available for '%s'\n", dev_name(dev));
152			return -EINVAL;
153		}
154	}
155
156	dtpm_devfreq = kzalloc(sizeof(*dtpm_devfreq), GFP_KERNEL);
157	if (!dtpm_devfreq)
158		return -ENOMEM;
159
160	dtpm_init(&dtpm_devfreq->dtpm, &dtpm_ops);
161
162	dtpm_devfreq->devfreq = devfreq;
163
164	ret = dtpm_register(dev_name(dev), &dtpm_devfreq->dtpm, parent);
165	if (ret) {
166		pr_err("Failed to register '%s': %d\n", dev_name(dev), ret);
167		kfree(dtpm_devfreq);
168		return ret;
169	}
170
171	ret = dev_pm_qos_add_request(dev, &dtpm_devfreq->qos_req,
172				     DEV_PM_QOS_MAX_FREQUENCY,
173				     PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
174	if (ret) {
175		pr_err("Failed to add QoS request: %d\n", ret);
176		goto out_dtpm_unregister;
177	}
178
179	dtpm_update_power(&dtpm_devfreq->dtpm);
180
181	return 0;
182
183out_dtpm_unregister:
184	dtpm_unregister(&dtpm_devfreq->dtpm);
185
186	return ret;
187}
188
189static int dtpm_devfreq_setup(struct dtpm *dtpm, struct device_node *np)
190{
191	struct devfreq *devfreq;
192
193	devfreq = devfreq_get_devfreq_by_node(np);
194	if (IS_ERR(devfreq))
195		return 0;
196
197	return __dtpm_devfreq_setup(devfreq, dtpm);
198}
199
200struct dtpm_subsys_ops dtpm_devfreq_ops = {
201	.name = KBUILD_MODNAME,
202	.setup = dtpm_devfreq_setup,
203};