Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powercap/drivers/dtpm: Add hierarchy creation

The DTPM framework is available but without a way to configure it.

This change provides a way to create a hierarchy of DTPM node where
the power consumption reflects the sum of the children's power
consumption.

It is up to the platform to specify an array of dtpm nodes where each
element has a pointer to its parent, except the top most one. The type
of the node gives the indication of which initialization callback to
call. At this time, we can create a virtual node, where its purpose is
to be a parent in the hierarchy, and a DT node where the name
describes its path.

In order to ensure a nice self-encapsulation, the DTPM subsys array
contains a couple of initialization functions, one to setup the DTPM
backend and one to initialize it up. With this approach, the DTPM
framework has a very few material to export.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Link: https://lore.kernel.org/r/20220128163537.212248-3-daniel.lezcano@linaro.org

+206 -6
+1
drivers/powercap/Kconfig
··· 46 46 47 47 config DTPM 48 48 bool "Power capping for Dynamic Thermal Power Management (EXPERIMENTAL)" 49 + depends on OF 49 50 help 50 51 This enables support for the power capping for the dynamic 51 52 thermal power management userspace engine.
+190 -6
drivers/powercap/dtpm.c
··· 23 23 #include <linux/powercap.h> 24 24 #include <linux/slab.h> 25 25 #include <linux/mutex.h> 26 + #include <linux/of.h> 26 27 27 28 #include "dtpm_subsys.h" 28 29 ··· 464 463 return 0; 465 464 } 466 465 467 - static int __init init_dtpm(void) 466 + static struct dtpm *dtpm_setup_virtual(const struct dtpm_node *hierarchy, 467 + struct dtpm *parent) 468 468 { 469 - pct = powercap_register_control_type(NULL, "dtpm", NULL); 470 - if (IS_ERR(pct)) { 471 - pr_err("Failed to register control type\n"); 472 - return PTR_ERR(pct); 469 + struct dtpm *dtpm; 470 + int ret; 471 + 472 + dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL); 473 + if (!dtpm) 474 + return ERR_PTR(-ENOMEM); 475 + dtpm_init(dtpm, NULL); 476 + 477 + ret = dtpm_register(hierarchy->name, dtpm, parent); 478 + if (ret) { 479 + pr_err("Failed to register dtpm node '%s': %d\n", 480 + hierarchy->name, ret); 481 + kfree(dtpm); 482 + return ERR_PTR(ret); 483 + } 484 + 485 + return dtpm; 486 + } 487 + 488 + static struct dtpm *dtpm_setup_dt(const struct dtpm_node *hierarchy, 489 + struct dtpm *parent) 490 + { 491 + struct device_node *np; 492 + int i, ret; 493 + 494 + np = of_find_node_by_path(hierarchy->name); 495 + if (!np) { 496 + pr_err("Failed to find '%s'\n", hierarchy->name); 497 + return ERR_PTR(-ENXIO); 498 + } 499 + 500 + for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) { 501 + 502 + if (!dtpm_subsys[i]->setup) 503 + continue; 504 + 505 + ret = dtpm_subsys[i]->setup(parent, np); 506 + if (ret) { 507 + pr_err("Failed to setup '%s': %d\n", dtpm_subsys[i]->name, ret); 508 + of_node_put(np); 509 + return ERR_PTR(ret); 510 + } 511 + } 512 + 513 + of_node_put(np); 514 + 515 + /* 516 + * By returning a NULL pointer, we let know the caller there 517 + * is no child for us as we are a leaf of the tree 518 + */ 519 + return NULL; 520 + } 521 + 522 + typedef struct dtpm * (*dtpm_node_callback_t)(const struct dtpm_node *, struct dtpm *); 523 + 524 + dtpm_node_callback_t dtpm_node_callback[] = { 525 + [DTPM_NODE_VIRTUAL] = dtpm_setup_virtual, 526 + [DTPM_NODE_DT] = dtpm_setup_dt, 527 + }; 528 + 529 + static int dtpm_for_each_child(const struct dtpm_node *hierarchy, 530 + const struct dtpm_node *it, struct dtpm *parent) 531 + { 532 + struct dtpm *dtpm; 533 + int i, ret; 534 + 535 + for (i = 0; hierarchy[i].name; i++) { 536 + 537 + if (hierarchy[i].parent != it) 538 + continue; 539 + 540 + dtpm = dtpm_node_callback[hierarchy[i].type](&hierarchy[i], parent); 541 + 542 + /* 543 + * A NULL pointer means there is no children, hence we 544 + * continue without going deeper in the recursivity. 545 + */ 546 + if (!dtpm) 547 + continue; 548 + 549 + /* 550 + * There are multiple reasons why the callback could 551 + * fail. The generic glue is abstracting the backend 552 + * and therefore it is not possible to report back or 553 + * take a decision based on the error. In any case, 554 + * if this call fails, it is not critical in the 555 + * hierarchy creation, we can assume the underlying 556 + * service is not found, so we continue without this 557 + * branch in the tree but with a warning to log the 558 + * information the node was not created. 559 + */ 560 + if (IS_ERR(dtpm)) { 561 + pr_warn("Failed to create '%s' in the hierarchy\n", 562 + hierarchy[i].name); 563 + continue; 564 + } 565 + 566 + ret = dtpm_for_each_child(hierarchy, &hierarchy[i], dtpm); 567 + if (ret) 568 + return ret; 473 569 } 474 570 475 571 return 0; 476 572 } 477 - late_initcall(init_dtpm); 573 + 574 + /** 575 + * dtpm_create_hierarchy - Create the dtpm hierarchy 576 + * @hierarchy: An array of struct dtpm_node describing the hierarchy 577 + * 578 + * The function is called by the platform specific code with the 579 + * description of the different node in the hierarchy. It creates the 580 + * tree in the sysfs filesystem under the powercap dtpm entry. 581 + * 582 + * The expected tree has the format: 583 + * 584 + * struct dtpm_node hierarchy[] = { 585 + * [0] { .name = "topmost", type = DTPM_NODE_VIRTUAL }, 586 + * [1] { .name = "package", .type = DTPM_NODE_VIRTUAL, .parent = &hierarchy[0] }, 587 + * [2] { .name = "/cpus/cpu0", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, 588 + * [3] { .name = "/cpus/cpu1", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, 589 + * [4] { .name = "/cpus/cpu2", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, 590 + * [5] { .name = "/cpus/cpu3", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, 591 + * [6] { } 592 + * }; 593 + * 594 + * The last element is always an empty one and marks the end of the 595 + * array. 596 + * 597 + * Return: zero on success, a negative value in case of error. Errors 598 + * are reported back from the underlying functions. 599 + */ 600 + int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table) 601 + { 602 + const struct of_device_id *match; 603 + const struct dtpm_node *hierarchy; 604 + struct device_node *np; 605 + int i, ret; 606 + 607 + if (pct) 608 + return -EBUSY; 609 + 610 + pct = powercap_register_control_type(NULL, "dtpm", NULL); 611 + if (IS_ERR(pct)) { 612 + pr_err("Failed to register control type\n"); 613 + ret = PTR_ERR(pct); 614 + goto out_pct; 615 + } 616 + 617 + ret = -ENODEV; 618 + np = of_find_node_by_path("/"); 619 + if (!np) 620 + goto out_err; 621 + 622 + match = of_match_node(dtpm_match_table, np); 623 + 624 + of_node_put(np); 625 + 626 + if (!match) 627 + goto out_err; 628 + 629 + hierarchy = match->data; 630 + if (!hierarchy) { 631 + ret = -EFAULT; 632 + goto out_err; 633 + } 634 + 635 + ret = dtpm_for_each_child(hierarchy, NULL, NULL); 636 + if (ret) 637 + goto out_err; 638 + 639 + for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) { 640 + 641 + if (!dtpm_subsys[i]->init) 642 + continue; 643 + 644 + ret = dtpm_subsys[i]->init(); 645 + if (ret) 646 + pr_info("Failed to initialze '%s': %d", 647 + dtpm_subsys[i]->name, ret); 648 + } 649 + 650 + return 0; 651 + 652 + out_err: 653 + powercap_unregister_control_type(pct); 654 + out_pct: 655 + pct = NULL; 656 + 657 + return ret; 658 + } 659 + EXPORT_SYMBOL_GPL(dtpm_create_hierarchy);
+15
include/linux/dtpm.h
··· 32 32 void (*release)(struct dtpm *); 33 33 }; 34 34 35 + struct device_node; 36 + 35 37 struct dtpm_subsys_ops { 36 38 const char *name; 37 39 int (*init)(void); 40 + int (*setup)(struct dtpm *, struct device_node *); 41 + }; 42 + 43 + enum DTPM_NODE_TYPE { 44 + DTPM_NODE_VIRTUAL = 0, 45 + DTPM_NODE_DT, 46 + }; 47 + 48 + struct dtpm_node { 49 + enum DTPM_NODE_TYPE type; 50 + const char *name; 51 + struct dtpm_node *parent; 38 52 }; 39 53 40 54 static inline struct dtpm *to_dtpm(struct powercap_zone *zone) ··· 66 52 67 53 int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent); 68 54 55 + int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table); 69 56 #endif