Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2021 Linaro Limited
0004  *
0005  * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
0006  *
0007  * The devfreq device combined with the energy model and the load can
0008  * give an estimation of the power consumption as well as limiting the
0009  * power.
0010  *
0011  */
0012 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0013 
0014 #include <linux/cpumask.h>
0015 #include <linux/devfreq.h>
0016 #include <linux/dtpm.h>
0017 #include <linux/energy_model.h>
0018 #include <linux/of.h>
0019 #include <linux/pm_qos.h>
0020 #include <linux/slab.h>
0021 #include <linux/units.h>
0022 
0023 struct dtpm_devfreq {
0024     struct dtpm dtpm;
0025     struct dev_pm_qos_request qos_req;
0026     struct devfreq *devfreq;
0027 };
0028 
0029 static struct dtpm_devfreq *to_dtpm_devfreq(struct dtpm *dtpm)
0030 {
0031     return container_of(dtpm, struct dtpm_devfreq, dtpm);
0032 }
0033 
0034 static int update_pd_power_uw(struct dtpm *dtpm)
0035 {
0036     struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
0037     struct devfreq *devfreq = dtpm_devfreq->devfreq;
0038     struct device *dev = devfreq->dev.parent;
0039     struct em_perf_domain *pd = em_pd_get(dev);
0040 
0041     dtpm->power_min = pd->table[0].power;
0042     dtpm->power_min *= MICROWATT_PER_MILLIWATT;
0043 
0044     dtpm->power_max = pd->table[pd->nr_perf_states - 1].power;
0045     dtpm->power_max *= MICROWATT_PER_MILLIWATT;
0046 
0047     return 0;
0048 }
0049 
0050 static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
0051 {
0052     struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
0053     struct devfreq *devfreq = dtpm_devfreq->devfreq;
0054     struct device *dev = devfreq->dev.parent;
0055     struct em_perf_domain *pd = em_pd_get(dev);
0056     unsigned long freq;
0057     u64 power;
0058     int i;
0059 
0060     for (i = 0; i < pd->nr_perf_states; i++) {
0061 
0062         power = pd->table[i].power * MICROWATT_PER_MILLIWATT;
0063         if (power > power_limit)
0064             break;
0065     }
0066 
0067     freq = pd->table[i - 1].frequency;
0068 
0069     dev_pm_qos_update_request(&dtpm_devfreq->qos_req, freq);
0070 
0071     power_limit = pd->table[i - 1].power * MICROWATT_PER_MILLIWATT;
0072 
0073     return power_limit;
0074 }
0075 
0076 static void _normalize_load(struct devfreq_dev_status *status)
0077 {
0078     if (status->total_time > 0xfffff) {
0079         status->total_time >>= 10;
0080         status->busy_time >>= 10;
0081     }
0082 
0083     status->busy_time <<= 10;
0084     status->busy_time /= status->total_time ? : 1;
0085 
0086     status->busy_time = status->busy_time ? : 1;
0087     status->total_time = 1024;
0088 }
0089 
0090 static u64 get_pd_power_uw(struct dtpm *dtpm)
0091 {
0092     struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
0093     struct devfreq *devfreq = dtpm_devfreq->devfreq;
0094     struct device *dev = devfreq->dev.parent;
0095     struct em_perf_domain *pd = em_pd_get(dev);
0096     struct devfreq_dev_status status;
0097     unsigned long freq;
0098     u64 power;
0099     int i;
0100 
0101     mutex_lock(&devfreq->lock);
0102     status = devfreq->last_status;
0103     mutex_unlock(&devfreq->lock);
0104 
0105     freq = DIV_ROUND_UP(status.current_frequency, HZ_PER_KHZ);
0106     _normalize_load(&status);
0107 
0108     for (i = 0; i < pd->nr_perf_states; i++) {
0109 
0110         if (pd->table[i].frequency < freq)
0111             continue;
0112 
0113         power = pd->table[i].power * MICROWATT_PER_MILLIWATT;
0114         power *= status.busy_time;
0115         power >>= 10;
0116 
0117         return power;
0118     }
0119 
0120     return 0;
0121 }
0122 
0123 static void pd_release(struct dtpm *dtpm)
0124 {
0125     struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
0126 
0127     if (dev_pm_qos_request_active(&dtpm_devfreq->qos_req))
0128         dev_pm_qos_remove_request(&dtpm_devfreq->qos_req);
0129 
0130     kfree(dtpm_devfreq);
0131 }
0132 
0133 static struct dtpm_ops dtpm_ops = {
0134     .set_power_uw = set_pd_power_limit,
0135     .get_power_uw = get_pd_power_uw,
0136     .update_power_uw = update_pd_power_uw,
0137     .release = pd_release,
0138 };
0139 
0140 static int __dtpm_devfreq_setup(struct devfreq *devfreq, struct dtpm *parent)
0141 {
0142     struct device *dev = devfreq->dev.parent;
0143     struct dtpm_devfreq *dtpm_devfreq;
0144     struct em_perf_domain *pd;
0145     int ret = -ENOMEM;
0146 
0147     pd = em_pd_get(dev);
0148     if (!pd) {
0149         ret = dev_pm_opp_of_register_em(dev, NULL);
0150         if (ret) {
0151             pr_err("No energy model available for '%s'\n", dev_name(dev));
0152             return -EINVAL;
0153         }
0154     }
0155 
0156     dtpm_devfreq = kzalloc(sizeof(*dtpm_devfreq), GFP_KERNEL);
0157     if (!dtpm_devfreq)
0158         return -ENOMEM;
0159 
0160     dtpm_init(&dtpm_devfreq->dtpm, &dtpm_ops);
0161 
0162     dtpm_devfreq->devfreq = devfreq;
0163 
0164     ret = dtpm_register(dev_name(dev), &dtpm_devfreq->dtpm, parent);
0165     if (ret) {
0166         pr_err("Failed to register '%s': %d\n", dev_name(dev), ret);
0167         kfree(dtpm_devfreq);
0168         return ret;
0169     }
0170 
0171     ret = dev_pm_qos_add_request(dev, &dtpm_devfreq->qos_req,
0172                      DEV_PM_QOS_MAX_FREQUENCY,
0173                      PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
0174     if (ret) {
0175         pr_err("Failed to add QoS request: %d\n", ret);
0176         goto out_dtpm_unregister;
0177     }
0178 
0179     dtpm_update_power(&dtpm_devfreq->dtpm);
0180 
0181     return 0;
0182 
0183 out_dtpm_unregister:
0184     dtpm_unregister(&dtpm_devfreq->dtpm);
0185 
0186     return ret;
0187 }
0188 
0189 static int dtpm_devfreq_setup(struct dtpm *dtpm, struct device_node *np)
0190 {
0191     struct devfreq *devfreq;
0192 
0193     devfreq = devfreq_get_devfreq_by_node(np);
0194     if (IS_ERR(devfreq))
0195         return 0;
0196 
0197     return __dtpm_devfreq_setup(devfreq, dtpm);
0198 }
0199 
0200 struct dtpm_subsys_ops dtpm_devfreq_ops = {
0201     .name = KBUILD_MODNAME,
0202     .setup = dtpm_devfreq_setup,
0203 };