cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dtpm_devfreq.c (4787B)


      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};