^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright 2020 Linaro Limited
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * The powercap based Dynamic Thermal Power Management framework
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * provides to the userspace a consistent API to set the power limit
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * on some devices.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * DTPM defines the functions to create a tree of constraints. Each
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * parent node is a virtual description of the aggregation of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * children. It propagates the constraints set at its level to its
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * children and collect the children power information. The leaves of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * the tree are the real devices which have the ability to get their
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * current power consumption and set their power limit.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/dtpm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/powercap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define DTPM_POWER_LIMIT_FLAG 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static const char *constraint_name[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) "Instantaneous",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) static DEFINE_MUTEX(dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static struct powercap_control_type *pct;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static struct dtpm *root;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static int get_time_window_us(struct powercap_zone *pcz, int cid, u64 *window)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) return -ENOSYS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static int set_time_window_us(struct powercap_zone *pcz, int cid, u64 window)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) return -ENOSYS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) static int get_max_power_range_uw(struct powercap_zone *pcz, u64 *max_power_uw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) struct dtpm *dtpm = to_dtpm(pcz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) *max_power_uw = dtpm->power_max - dtpm->power_min;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) static int __get_power_uw(struct dtpm *dtpm, u64 *power_uw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) struct dtpm *child;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) u64 power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) if (dtpm->ops) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) *power_uw = dtpm->ops->get_power_uw(dtpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) *power_uw = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) list_for_each_entry(child, &dtpm->children, sibling) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) ret = __get_power_uw(child, &power);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) *power_uw += power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) static int get_power_uw(struct powercap_zone *pcz, u64 *power_uw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) struct dtpm *dtpm = to_dtpm(pcz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) ret = __get_power_uw(dtpm, power_uw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) static void __dtpm_rebalance_weight(struct dtpm *dtpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) struct dtpm *child;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) list_for_each_entry(child, &dtpm->children, sibling) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) pr_debug("Setting weight '%d' for '%s'\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) child->weight, child->zone.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) child->weight = DIV64_U64_ROUND_CLOSEST(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) child->power_max * 1024, dtpm->power_max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) __dtpm_rebalance_weight(child);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static void __dtpm_sub_power(struct dtpm *dtpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) struct dtpm *parent = dtpm->parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) while (parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) parent->power_min -= dtpm->power_min;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) parent->power_max -= dtpm->power_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) parent->power_limit -= dtpm->power_limit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) parent = parent->parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) __dtpm_rebalance_weight(root);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) static void __dtpm_add_power(struct dtpm *dtpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) struct dtpm *parent = dtpm->parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) while (parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) parent->power_min += dtpm->power_min;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) parent->power_max += dtpm->power_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) parent->power_limit += dtpm->power_limit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) parent = parent->parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) __dtpm_rebalance_weight(root);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) * dtpm_update_power - Update the power on the dtpm
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) * @dtpm: a pointer to a dtpm structure to update
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) * @power_min: a u64 representing the new power_min value
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) * @power_max: a u64 representing the new power_max value
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) * Function to update the power values of the dtpm node specified in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) * parameter. These new values will be propagated to the tree.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) * Return: zero on success, -EINVAL if the values are inconsistent
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) int dtpm_update_power(struct dtpm *dtpm, u64 power_min, u64 power_max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (power_min == dtpm->power_min && power_max == dtpm->power_max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) goto unlock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) if (power_max < power_min) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) goto unlock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) __dtpm_sub_power(dtpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) dtpm->power_min = power_min;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) dtpm->power_max = power_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) if (!test_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) dtpm->power_limit = power_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) __dtpm_add_power(dtpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) unlock:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) * dtpm_release_zone - Cleanup when the node is released
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) * @pcz: a pointer to a powercap_zone structure
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) * Do some housecleaning and update the weight on the tree. The
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) * release will be denied if the node has children. This function must
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) * be called by the specific release callback of the different
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) * backends.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) * Return: 0 on success, -EBUSY if there are children
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) int dtpm_release_zone(struct powercap_zone *pcz)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) struct dtpm *dtpm = to_dtpm(pcz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) struct dtpm *parent = dtpm->parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) if (!list_empty(&dtpm->children)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) if (parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) list_del(&dtpm->sibling);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) __dtpm_sub_power(dtpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) if (dtpm->ops)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) dtpm->ops->release(dtpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) if (root == dtpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) root = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) kfree(dtpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) static int __get_power_limit_uw(struct dtpm *dtpm, int cid, u64 *power_limit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) *power_limit = dtpm->power_limit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) static int get_power_limit_uw(struct powercap_zone *pcz,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) int cid, u64 *power_limit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) struct dtpm *dtpm = to_dtpm(pcz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) ret = __get_power_limit_uw(dtpm, cid, power_limit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) * Set the power limit on the nodes, the power limit is distributed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) * given the weight of the children.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) * The dtpm node lock must be held when calling this function.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) static int __set_power_limit_uw(struct dtpm *dtpm, int cid, u64 power_limit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) struct dtpm *child;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) u64 power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) * A max power limitation means we remove the power limit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) * otherwise we set a constraint and flag the dtpm node.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (power_limit == dtpm->power_max) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) clear_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) set_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) pr_debug("Setting power limit for '%s': %llu uW\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) dtpm->zone.name, power_limit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) * Only leaves of the dtpm tree has ops to get/set the power
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) if (dtpm->ops) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) dtpm->power_limit = dtpm->ops->set_power_uw(dtpm, power_limit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) dtpm->power_limit = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) list_for_each_entry(child, &dtpm->children, sibling) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) * Integer division rounding will inevitably
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) * lead to a different min or max value when
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) * set several times. In order to restore the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) * initial value, we force the child's min or
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) * max power every time if the constraint is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) * at the boundaries.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) if (power_limit == dtpm->power_max) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) power = child->power_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) } else if (power_limit == dtpm->power_min) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) power = child->power_min;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) power = DIV_ROUND_CLOSEST_ULL(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) power_limit * child->weight, 1024);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) pr_debug("Setting power limit for '%s': %llu uW\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) child->zone.name, power);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) ret = __set_power_limit_uw(child, cid, power);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) if (!ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) ret = __get_power_limit_uw(child, cid, &power);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) dtpm->power_limit += power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) static int set_power_limit_uw(struct powercap_zone *pcz,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) int cid, u64 power_limit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) struct dtpm *dtpm = to_dtpm(pcz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) * Don't allow values outside of the power range previously
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) * set when initializing the power numbers.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) power_limit = clamp_val(power_limit, dtpm->power_min, dtpm->power_max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) ret = __set_power_limit_uw(dtpm, cid, power_limit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) pr_debug("%s: power limit: %llu uW, power max: %llu uW\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) dtpm->zone.name, dtpm->power_limit, dtpm->power_max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) static const char *get_constraint_name(struct powercap_zone *pcz, int cid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) return constraint_name[cid];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) static int get_max_power_uw(struct powercap_zone *pcz, int id, u64 *max_power)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) struct dtpm *dtpm = to_dtpm(pcz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) *max_power = dtpm->power_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) static struct powercap_zone_constraint_ops constraint_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) .set_power_limit_uw = set_power_limit_uw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) .get_power_limit_uw = get_power_limit_uw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) .set_time_window_us = set_time_window_us,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) .get_time_window_us = get_time_window_us,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) .get_max_power_uw = get_max_power_uw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) .get_name = get_constraint_name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) static struct powercap_zone_ops zone_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) .get_max_power_range_uw = get_max_power_range_uw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) .get_power_uw = get_power_uw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) .release = dtpm_release_zone,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) * dtpm_alloc - Allocate and initialize a dtpm struct
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) * @name: a string specifying the name of the node
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) * Return: a struct dtpm pointer, NULL in case of error
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) struct dtpm *dtpm_alloc(struct dtpm_ops *ops)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) struct dtpm *dtpm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) if (dtpm) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) INIT_LIST_HEAD(&dtpm->children);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) INIT_LIST_HEAD(&dtpm->sibling);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) dtpm->weight = 1024;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) dtpm->ops = ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) return dtpm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) * dtpm_unregister - Unregister a dtpm node from the hierarchy tree
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) * @dtpm: a pointer to a dtpm structure corresponding to the node to be removed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) * Call the underlying powercap unregister function. That will call
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) * the release callback of the powercap zone.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) void dtpm_unregister(struct dtpm *dtpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) powercap_unregister_zone(pct, &dtpm->zone);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) pr_info("Unregistered dtpm node '%s'\n", dtpm->zone.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) * dtpm_register - Register a dtpm node in the hierarchy tree
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) * @name: a string specifying the name of the node
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) * @dtpm: a pointer to a dtpm structure corresponding to the new node
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) * @parent: a pointer to a dtpm structure corresponding to the parent node
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) * Create a dtpm node in the tree. If no parent is specified, the node
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) * is the root node of the hierarchy. If the root node already exists,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) * then the registration will fail. The powercap controller must be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) * initialized before calling this function.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) * The dtpm structure must be initialized with the power numbers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) * before calling this function.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) * Return: zero on success, a negative value in case of error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) * -EAGAIN: the function is called before the framework is initialized.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) * -EBUSY: the root node is already inserted
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) * -EINVAL: * there is no root node yet and @parent is specified
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) * * no all ops are defined
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) * * parent have ops which are reserved for leaves
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) * Other negative values are reported back from the powercap framework
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) struct powercap_zone *pcz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) if (!pct)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) return -EAGAIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) if (root && !parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) if (!root && parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) if (parent && parent->ops)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) if (!dtpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) if (dtpm->ops && !(dtpm->ops->set_power_uw &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) dtpm->ops->get_power_uw &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) dtpm->ops->release))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) pcz = powercap_register_zone(&dtpm->zone, pct, name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) parent ? &parent->zone : NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) &zone_ops, MAX_DTPM_CONSTRAINTS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) &constraint_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) if (IS_ERR(pcz))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) return PTR_ERR(pcz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) mutex_lock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) if (parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) list_add_tail(&dtpm->sibling, &parent->children);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) dtpm->parent = parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) root = dtpm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) __dtpm_add_power(dtpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) pr_info("Registered dtpm node '%s' / %llu-%llu uW, \n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) dtpm->zone.name, dtpm->power_min, dtpm->power_max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) mutex_unlock(&dtpm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) static int __init dtpm_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) struct dtpm_descr **dtpm_descr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) pct = powercap_register_control_type(NULL, "dtpm", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) if (IS_ERR(pct)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) pr_err("Failed to register control type\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) return PTR_ERR(pct);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) for_each_dtpm_table(dtpm_descr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) (*dtpm_descr)->init(*dtpm_descr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) late_initcall(dtpm_init);