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