From 2ee5f8f05949735fa2f4c463a5e13fcb3660c719 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 8 Dec 2020 17:41:42 +0100 Subject: [PATCH 01/89] units: Add Watt units As there are the temperature units, let's add the Watt macros definition. Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- include/linux/units.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/units.h b/include/linux/units.h index aaf716364ec3..92c234e71cab 100644 --- a/include/linux/units.h +++ b/include/linux/units.h @@ -4,6 +4,10 @@ #include +#define MILLIWATT_PER_WATT 1000L +#define MICROWATT_PER_MILLIWATT 1000L +#define MICROWATT_PER_WATT 1000000L + #define ABSOLUTE_ZERO_MILLICELSIUS -273150 static inline long milli_kelvin_to_millicelsius(long t) From f5ad1c747956d501516610ee7900f4a6d57ee2f5 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 8 Dec 2020 17:41:43 +0100 Subject: [PATCH 02/89] Documentation/powercap/dtpm: Add documentation for dtpm The dynamic thermal and power management is a technique to dynamically adjust the power consumption of different devices in order to ensure a global thermal constraint. An userspace daemon is usually monitoring the temperature and the power to take immediate action on the device. The DTPM framework provides an unified API to userspace to act on the power. Document this framework. Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- Documentation/power/index.rst | 1 + Documentation/power/powercap/dtpm.rst | 212 ++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 Documentation/power/powercap/dtpm.rst diff --git a/Documentation/power/index.rst b/Documentation/power/index.rst index ced8a8007434..a0f5244fb427 100644 --- a/Documentation/power/index.rst +++ b/Documentation/power/index.rst @@ -30,6 +30,7 @@ Power Management userland-swsusp powercap/powercap + powercap/dtpm regulator/consumer regulator/design diff --git a/Documentation/power/powercap/dtpm.rst b/Documentation/power/powercap/dtpm.rst new file mode 100644 index 000000000000..a38dee3d815b --- /dev/null +++ b/Documentation/power/powercap/dtpm.rst @@ -0,0 +1,212 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================== +Dynamic Thermal Power Management framework +========================================== + +On the embedded world, the complexity of the SoC leads to an +increasing number of hotspots which need to be monitored and mitigated +as a whole in order to prevent the temperature to go above the +normative and legally stated 'skin temperature'. + +Another aspect is to sustain the performance for a given power budget, +for example virtual reality where the user can feel dizziness if the +performance is capped while a big CPU is processing something else. Or +reduce the battery charging because the dissipated power is too high +compared with the power consumed by other devices. + +The user space is the most adequate place to dynamically act on the +different devices by limiting their power given an application +profile: it has the knowledge of the platform. + +The Dynamic Thermal Power Management (DTPM) is a technique acting on +the device power by limiting and/or balancing a power budget among +different devices. + +The DTPM framework provides an unified interface to act on the +device power. + +Overview +======== + +The DTPM framework relies on the powercap framework to create the +powercap entries in the sysfs directory and implement the backend +driver to do the connection with the power manageable device. + +The DTPM is a tree representation describing the power constraints +shared between devices, not their physical positions. + +The nodes of the tree are a virtual description aggregating the power +characteristics of the children nodes and their power limitations. + +The leaves of the tree are the real power manageable devices. + +For instance:: + + SoC + | + `-- pkg + | + |-- pd0 (cpu0-3) + | + `-- pd1 (cpu4-5) + +The pkg power will be the sum of pd0 and pd1 power numbers:: + + SoC (400mW - 3100mW) + | + `-- pkg (400mW - 3100mW) + | + |-- pd0 (100mW - 700mW) + | + `-- pd1 (300mW - 2400mW) + +When the nodes are inserted in the tree, their power characteristics are propagated to the parents:: + + SoC (600mW - 5900mW) + | + |-- pkg (400mW - 3100mW) + | | + | |-- pd0 (100mW - 700mW) + | | + | `-- pd1 (300mW - 2400mW) + | + `-- pd2 (200mW - 2800mW) + +Each node have a weight on a 2^10 basis reflecting the percentage of power consumption along the siblings:: + + SoC (w=1024) + | + |-- pkg (w=538) + | | + | |-- pd0 (w=231) + | | + | `-- pd1 (w=794) + | + `-- pd2 (w=486) + + Note the sum of weights at the same level are equal to 1024. + +When a power limitation is applied to a node, then it is distributed along the children given their weights. For example, if we set a power limitation of 3200mW at the 'SoC' root node, the resulting tree will be:: + + SoC (w=1024) <--- power_limit = 3200mW + | + |-- pkg (w=538) --> power_limit = 1681mW + | | + | |-- pd0 (w=231) --> power_limit = 378mW + | | + | `-- pd1 (w=794) --> power_limit = 1303mW + | + `-- pd2 (w=486) --> power_limit = 1519mW + + +Flat description +---------------- + +A root node is created and it is the parent of all the nodes. This +description is the simplest one and it is supposed to give to user +space a flat representation of all the devices supporting the power +limitation without any power limitation distribution. + +Hierarchical description +------------------------ + +The different devices supporting the power limitation are represented +hierarchically. There is one root node, all intermediate nodes are +grouping the child nodes which can be intermediate nodes also or real +devices. + +The intermediate nodes aggregate the power information and allows to +set the power limit given the weight of the nodes. + +User space API +============== + +As stated in the overview, the DTPM framework is built on top of the +powercap framework. Thus the sysfs interface is the same, please refer +to the powercap documentation for further details. + + * power_uw: Instantaneous power consumption. If the node is an + intermediate node, then the power consumption will be the sum of all + children power consumption. + + * max_power_range_uw: The power range resulting of the maximum power + minus the minimum power. + + * name: The name of the node. This is implementation dependent. Even + if it is not recommended for the user space, several nodes can have + the same name. + + * constraint_X_name: The name of the constraint. + + * constraint_X_max_power_uw: The maximum power limit to be applicable + to the node. + + * constraint_X_power_limit_uw: The power limit to be applied to the + node. If the value contained in constraint_X_max_power_uw is set, + the constraint will be removed. + + * constraint_X_time_window_us: The meaning of this file will depend + on the constraint number. + +Constraints +----------- + + * Constraint 0: The power limitation is immediately applied, without + limitation in time. + +Kernel API +========== + +Overview +-------- + +The DTPM framework has no power limiting backend support. It is +generic and provides a set of API to let the different drivers to +implement the backend part for the power limitation and create the +power constraints tree. + +It is up to the platform to provide the initialization function to +allocate and link the different nodes of the tree. + +A special macro has the role of declaring a node and the corresponding +initialization function via a description structure. This one contains +an optional parent field allowing to hook different devices to an +already existing tree at boot time. + +For instance:: + + struct dtpm_descr my_descr = { + .name = "my_name", + .init = my_init_func, + }; + + DTPM_DECLARE(my_descr); + +The nodes of the DTPM tree are described with dtpm structure. The +steps to add a new power limitable device is done in three steps: + + * Allocate the dtpm node + * Set the power number of the dtpm node + * Register the dtpm node + +The registration of the dtpm node is done with the powercap +ops. Basically, it must implements the callbacks to get and set the +power and the limit. + +Alternatively, if the node to be inserted is an intermediate one, then +a simple function to insert it as a future parent is available. + +If a device has its power characteristics changing, then the tree must +be updated with the new power numbers and weights. + +Nomenclature +------------ + + * dtpm_alloc() : Allocate and initialize a dtpm structure + + * dtpm_register() : Add the dtpm node to the tree + + * dtpm_unregister() : Remove the dtpm node from the tree + + * dtpm_update_power() : Update the power characteristics of the dtpm node From a20d0ef97abf486a917aff066c457bdb930425af Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 8 Dec 2020 17:41:44 +0100 Subject: [PATCH 03/89] powercap/drivers/dtpm: Add API for dynamic thermal power management On the embedded world, the complexity of the SoC leads to an increasing number of hotspots which need to be monitored and mitigated as a whole in order to prevent the temperature to go above the normative and legally stated 'skin temperature'. Another aspect is to sustain the performance for a given power budget, for example virtual reality where the user can feel dizziness if the GPU performance is capped while a big CPU is processing something else. Or reduce the battery charging because the dissipated power is too high compared with the power consumed by other devices. The userspace is the most adequate place to dynamically act on the different devices by limiting their power given an application profile: it has the knowledge of the platform. These userspace daemons are in charge of the Dynamic Thermal Power Management (DTPM). Nowadays, the dtpm daemons are abusing the thermal framework as they act on the cooling device state to force a specific and arbitrary state without taking care of the governor decisions. Given the closed loop of some governors that can confuse the logic or directly enter in a decision conflict. As the number of cooling device support is limited today to the CPU and the GPU, the dtpm daemons have little control on the power dissipation of the system. The out of tree solutions are hacking around here and there in the drivers, in the frameworks to have control on the devices. The common solution is to declare them as cooling devices. There is no unification of the power limitation unit, opaque states are used. This patch provides a way to create a hierarchy of constraints using the powercap framework. The devices which are registered as power limit-able devices are represented in this hierarchy as a tree. They are linked together with intermediate nodes which are just there to propagate the constraint to the children. The leaves of the tree are the real devices, the intermediate nodes are virtual, aggregating the children constraints and power characteristics. Each node have a weight on a 2^10 basis, in order to reflect the percentage of power distribution of the children's node. This percentage is used to dispatch the power limit to the children. The weight is computed against the max power of the siblings. This simple approach allows to do a fair distribution of the power limit. Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- drivers/powercap/Kconfig | 6 + drivers/powercap/Makefile | 1 + drivers/powercap/dtpm.c | 473 ++++++++++++++++++++++++++++++ include/asm-generic/vmlinux.lds.h | 11 + include/linux/dtpm.h | 75 +++++ 5 files changed, 566 insertions(+) create mode 100644 drivers/powercap/dtpm.c create mode 100644 include/linux/dtpm.h diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index bc228725346b..cc1953bd8bed 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -43,4 +43,10 @@ config IDLE_INJECT CPUs for power capping. Idle period can be injected synchronously on a set of specified CPUs or alternatively on a per CPU basis. + +config DTPM + bool "Power capping for Dynamic Thermal Power Management" + help + This enables support for the power capping for the dynamic + thermal power management userspace engine. endif diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile index 7255c94ec61c..6482ac52054d 100644 --- a/drivers/powercap/Makefile +++ b/drivers/powercap/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_DTPM) += dtpm.o obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c new file mode 100644 index 000000000000..5b6857e9b064 --- /dev/null +++ b/drivers/powercap/dtpm.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Linaro Limited + * + * Author: Daniel Lezcano + * + * The powercap based Dynamic Thermal Power Management framework + * provides to the userspace a consistent API to set the power limit + * on some devices. + * + * DTPM defines the functions to create a tree of constraints. Each + * parent node is a virtual description of the aggregation of the + * children. It propagates the constraints set at its level to its + * children and collect the children power information. The leaves of + * the tree are the real devices which have the ability to get their + * current power consumption and set their power limit. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#define DTPM_POWER_LIMIT_FLAG BIT(0) + +static const char *constraint_name[] = { + "Instantaneous", +}; + +static DEFINE_MUTEX(dtpm_lock); +static struct powercap_control_type *pct; +static struct dtpm *root; + +static int get_time_window_us(struct powercap_zone *pcz, int cid, u64 *window) +{ + return -ENOSYS; +} + +static int set_time_window_us(struct powercap_zone *pcz, int cid, u64 window) +{ + return -ENOSYS; +} + +static int get_max_power_range_uw(struct powercap_zone *pcz, u64 *max_power_uw) +{ + struct dtpm *dtpm = to_dtpm(pcz); + + mutex_lock(&dtpm_lock); + *max_power_uw = dtpm->power_max - dtpm->power_min; + mutex_unlock(&dtpm_lock); + + return 0; +} + +static int __get_power_uw(struct dtpm *dtpm, u64 *power_uw) +{ + struct dtpm *child; + u64 power; + int ret = 0; + + if (dtpm->ops) { + *power_uw = dtpm->ops->get_power_uw(dtpm); + return 0; + } + + *power_uw = 0; + + list_for_each_entry(child, &dtpm->children, sibling) { + ret = __get_power_uw(child, &power); + if (ret) + break; + *power_uw += power; + } + + return ret; +} + +static int get_power_uw(struct powercap_zone *pcz, u64 *power_uw) +{ + struct dtpm *dtpm = to_dtpm(pcz); + int ret; + + mutex_lock(&dtpm_lock); + ret = __get_power_uw(dtpm, power_uw); + mutex_unlock(&dtpm_lock); + + return ret; +} + +static void __dtpm_rebalance_weight(struct dtpm *dtpm) +{ + struct dtpm *child; + + list_for_each_entry(child, &dtpm->children, sibling) { + + pr_debug("Setting weight '%d' for '%s'\n", + child->weight, child->zone.name); + + child->weight = DIV_ROUND_CLOSEST(child->power_max * 1024, + dtpm->power_max); + + __dtpm_rebalance_weight(child); + } +} + +static void __dtpm_sub_power(struct dtpm *dtpm) +{ + struct dtpm *parent = dtpm->parent; + + while (parent) { + parent->power_min -= dtpm->power_min; + parent->power_max -= dtpm->power_max; + parent->power_limit -= dtpm->power_limit; + parent = parent->parent; + } + + __dtpm_rebalance_weight(root); +} + +static void __dtpm_add_power(struct dtpm *dtpm) +{ + struct dtpm *parent = dtpm->parent; + + while (parent) { + parent->power_min += dtpm->power_min; + parent->power_max += dtpm->power_max; + parent->power_limit += dtpm->power_limit; + parent = parent->parent; + } + + __dtpm_rebalance_weight(root); +} + +/** + * dtpm_update_power - Update the power on the dtpm + * @dtpm: a pointer to a dtpm structure to update + * @power_min: a u64 representing the new power_min value + * @power_max: a u64 representing the new power_max value + * + * Function to update the power values of the dtpm node specified in + * parameter. These new values will be propagated to the tree. + * + * Return: zero on success, -EINVAL if the values are inconsistent + */ +int dtpm_update_power(struct dtpm *dtpm, u64 power_min, u64 power_max) +{ + mutex_lock(&dtpm_lock); + + if (power_min == dtpm->power_min && power_max == dtpm->power_max) + return 0; + + if (power_max < power_min) + return -EINVAL; + + __dtpm_sub_power(dtpm); + + dtpm->power_min = power_min; + dtpm->power_max = power_max; + if (!test_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags)) + dtpm->power_limit = power_max; + + __dtpm_add_power(dtpm); + + mutex_unlock(&dtpm_lock); + + return 0; +} + +/** + * dtpm_release_zone - Cleanup when the node is released + * @pcz: a pointer to a powercap_zone structure + * + * Do some housecleaning and update the weight on the tree. The + * release will be denied if the node has children. This function must + * be called by the specific release callback of the different + * backends. + * + * Return: 0 on success, -EBUSY if there are children + */ +int dtpm_release_zone(struct powercap_zone *pcz) +{ + struct dtpm *dtpm = to_dtpm(pcz); + struct dtpm *parent = dtpm->parent; + + mutex_lock(&dtpm_lock); + + if (!list_empty(&dtpm->children)) + return -EBUSY; + + if (parent) + list_del(&dtpm->sibling); + + __dtpm_sub_power(dtpm); + + mutex_unlock(&dtpm_lock); + + if (dtpm->ops) + dtpm->ops->release(dtpm); + + kfree(dtpm); + + return 0; +} + +static int __get_power_limit_uw(struct dtpm *dtpm, int cid, u64 *power_limit) +{ + *power_limit = dtpm->power_limit; + return 0; +} + +static int get_power_limit_uw(struct powercap_zone *pcz, + int cid, u64 *power_limit) +{ + struct dtpm *dtpm = to_dtpm(pcz); + int ret; + + mutex_lock(&dtpm_lock); + ret = __get_power_limit_uw(dtpm, cid, power_limit); + mutex_unlock(&dtpm_lock); + + return ret; +} + +/* + * Set the power limit on the nodes, the power limit is distributed + * given the weight of the children. + * + * The dtpm node lock must be held when calling this function. + */ +static int __set_power_limit_uw(struct dtpm *dtpm, int cid, u64 power_limit) +{ + struct dtpm *child; + int ret = 0; + u64 power; + + /* + * A max power limitation means we remove the power limit, + * otherwise we set a constraint and flag the dtpm node. + */ + if (power_limit == dtpm->power_max) { + clear_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags); + } else { + set_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags); + } + + pr_debug("Setting power limit for '%s': %llu uW\n", + dtpm->zone.name, power_limit); + + /* + * Only leaves of the dtpm tree has ops to get/set the power + */ + if (dtpm->ops) { + dtpm->power_limit = dtpm->ops->set_power_uw(dtpm, power_limit); + } else { + dtpm->power_limit = 0; + + list_for_each_entry(child, &dtpm->children, sibling) { + + /* + * Integer division rounding will inevitably + * lead to a different min or max value when + * set several times. In order to restore the + * initial value, we force the child's min or + * max power every time if the constraint is + * at the boundaries. + */ + if (power_limit == dtpm->power_max) { + power = child->power_max; + } else if (power_limit == dtpm->power_min) { + power = child->power_min; + } else { + power = DIV_ROUND_CLOSEST( + power_limit * child->weight, 1024); + } + + pr_debug("Setting power limit for '%s': %llu uW\n", + child->zone.name, power); + + ret = __set_power_limit_uw(child, cid, power); + if (!ret) + ret = __get_power_limit_uw(child, cid, &power); + + if (ret) + break; + + dtpm->power_limit += power; + } + } + + return ret; +} + +static int set_power_limit_uw(struct powercap_zone *pcz, + int cid, u64 power_limit) +{ + struct dtpm *dtpm = to_dtpm(pcz); + int ret; + + mutex_lock(&dtpm_lock); + + /* + * Don't allow values outside of the power range previously + * set when initializing the power numbers. + */ + power_limit = clamp_val(power_limit, dtpm->power_min, dtpm->power_max); + + ret = __set_power_limit_uw(dtpm, cid, power_limit); + + pr_debug("%s: power limit: %llu uW, power max: %llu uW\n", + dtpm->zone.name, dtpm->power_limit, dtpm->power_max); + + mutex_unlock(&dtpm_lock); + + return ret; +} + +static const char *get_constraint_name(struct powercap_zone *pcz, int cid) +{ + return constraint_name[cid]; +} + +static int get_max_power_uw(struct powercap_zone *pcz, int id, u64 *max_power) +{ + struct dtpm *dtpm = to_dtpm(pcz); + + mutex_lock(&dtpm_lock); + *max_power = dtpm->power_max; + mutex_unlock(&dtpm_lock); + + return 0; +} + +static struct powercap_zone_constraint_ops constraint_ops = { + .set_power_limit_uw = set_power_limit_uw, + .get_power_limit_uw = get_power_limit_uw, + .set_time_window_us = set_time_window_us, + .get_time_window_us = get_time_window_us, + .get_max_power_uw = get_max_power_uw, + .get_name = get_constraint_name, +}; + +static struct powercap_zone_ops zone_ops = { + .get_max_power_range_uw = get_max_power_range_uw, + .get_power_uw = get_power_uw, + .release = dtpm_release_zone, +}; + +/** + * dtpm_alloc - Allocate and initialize a dtpm struct + * @name: a string specifying the name of the node + * + * Return: a struct dtpm pointer, NULL in case of error + */ +struct dtpm *dtpm_alloc(struct dtpm_ops *ops) +{ + struct dtpm *dtpm; + + dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL); + if (dtpm) { + INIT_LIST_HEAD(&dtpm->children); + INIT_LIST_HEAD(&dtpm->sibling); + dtpm->weight = 1024; + dtpm->ops = ops; + } + + return dtpm; +} + +/** + * dtpm_unregister - Unregister a dtpm node from the hierarchy tree + * @dtpm: a pointer to a dtpm structure corresponding to the node to be removed + * + * Call the underlying powercap unregister function. That will call + * the release callback of the powercap zone. + */ +void dtpm_unregister(struct dtpm *dtpm) +{ + powercap_unregister_zone(pct, &dtpm->zone); + + pr_info("Unregistered dtpm node '%s'\n", dtpm->zone.name); +} + +/** + * dtpm_register - Register a dtpm node in the hierarchy tree + * @name: a string specifying the name of the node + * @dtpm: a pointer to a dtpm structure corresponding to the new node + * @parent: a pointer to a dtpm structure corresponding to the parent node + * + * Create a dtpm node in the tree. If no parent is specified, the node + * is the root node of the hierarchy. If the root node already exists, + * then the registration will fail. The powercap controller must be + * initialized before calling this function. + * + * The dtpm structure must be initialized with the power numbers + * before calling this function. + * + * Return: zero on success, a negative value in case of error: + * -EAGAIN: the function is called before the framework is initialized. + * -EBUSY: the root node is already inserted + * -EINVAL: * there is no root node yet and @parent is specified + * * no all ops are defined + * * parent have ops which are reserved for leaves + * Other negative values are reported back from the powercap framework + */ +int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) +{ + struct powercap_zone *pcz; + + if (!pct) + return -EAGAIN; + + if (root && !parent) + return -EBUSY; + + if (!root && parent) + return -EINVAL; + + if (parent && parent->ops) + return -EINVAL; + + if (!dtpm) + return -EINVAL; + + if (dtpm->ops && !(dtpm->ops->set_power_uw && + dtpm->ops->get_power_uw && + dtpm->ops->release)) + return -EINVAL; + + pcz = powercap_register_zone(&dtpm->zone, pct, name, + parent ? &parent->zone : NULL, + &zone_ops, MAX_DTPM_CONSTRAINTS, + &constraint_ops); + if (IS_ERR(pcz)) + return PTR_ERR(pcz); + + mutex_lock(&dtpm_lock); + + if (parent) { + list_add_tail(&dtpm->sibling, &parent->children); + dtpm->parent = parent; + } else { + root = dtpm; + } + + __dtpm_add_power(dtpm); + + pr_info("Registered dtpm node '%s' / %llu-%llu uW, \n", + dtpm->zone.name, dtpm->power_min, dtpm->power_max); + + mutex_unlock(&dtpm_lock); + + return 0; +} + +static int __init dtpm_init(void) +{ + struct dtpm_descr **dtpm_descr; + + pct = powercap_register_control_type(NULL, "dtpm", NULL); + if (!pct) { + pr_err("Failed to register control type\n"); + return -EINVAL; + } + + for_each_dtpm_table(dtpm_descr) + (*dtpm_descr)->init(*dtpm_descr); + + return 0; +} +late_initcall(dtpm_init); diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index b2b3d81b1535..b3e4e0740089 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -316,6 +316,16 @@ #define THERMAL_TABLE(name) #endif +#ifdef CONFIG_DTPM +#define DTPM_TABLE() \ + . = ALIGN(8); \ + __dtpm_table = .; \ + KEEP(*(__dtpm_table)) \ + __dtpm_table_end = .; +#else +#define DTPM_TABLE() +#endif + #define KERNEL_DTB() \ STRUCT_ALIGN(); \ __dtb_start = .; \ @@ -733,6 +743,7 @@ ACPI_PROBE_TABLE(irqchip) \ ACPI_PROBE_TABLE(timer) \ THERMAL_TABLE(governor) \ + DTPM_TABLE() \ EARLYCON_TABLE() \ LSM_TABLE() \ EARLY_LSM_TABLE() \ diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h new file mode 100644 index 000000000000..7a1d0b50e334 --- /dev/null +++ b/include/linux/dtpm.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 Linaro Ltd + * + * Author: Daniel Lezcano + */ +#ifndef ___DTPM_H__ +#define ___DTPM_H__ + +#include + +#define MAX_DTPM_DESCR 8 +#define MAX_DTPM_CONSTRAINTS 1 + +struct dtpm { + struct powercap_zone zone; + struct dtpm *parent; + struct list_head sibling; + struct list_head children; + struct dtpm_ops *ops; + unsigned long flags; + u64 power_limit; + u64 power_max; + u64 power_min; + int weight; + void *private; +}; + +struct dtpm_ops { + u64 (*set_power_uw)(struct dtpm *, u64); + u64 (*get_power_uw)(struct dtpm *); + void (*release)(struct dtpm *); +}; + +struct dtpm_descr; + +typedef int (*dtpm_init_t)(struct dtpm_descr *); + +struct dtpm_descr { + struct dtpm *parent; + const char *name; + dtpm_init_t init; +}; + +/* Init section thermal table */ +extern struct dtpm_descr *__dtpm_table[]; +extern struct dtpm_descr *__dtpm_table_end[]; + +#define DTPM_TABLE_ENTRY(name) \ + static typeof(name) *__dtpm_table_entry_##name \ + __used __section("__dtpm_table") = &name + +#define DTPM_DECLARE(name) DTPM_TABLE_ENTRY(name) + +#define for_each_dtpm_table(__dtpm) \ + for (__dtpm = __dtpm_table; \ + __dtpm < __dtpm_table_end; \ + __dtpm++) + +static inline struct dtpm *to_dtpm(struct powercap_zone *zone) +{ + return container_of(zone, struct dtpm, zone); +} + +int dtpm_update_power(struct dtpm *dtpm, u64 power_min, u64 power_max); + +int dtpm_release_zone(struct powercap_zone *pcz); + +struct dtpm *dtpm_alloc(struct dtpm_ops *ops); + +void dtpm_unregister(struct dtpm *dtpm); + +int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent); + +#endif From 0e8f68d7f04856a9e2ad4817b477fa35124888bd Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 8 Dec 2020 17:41:45 +0100 Subject: [PATCH 04/89] powercap/drivers/dtpm: Add CPU energy model based support With the powercap dtpm controller, we are able to plug devices with power limitation features in the tree. The following patch introduces the CPU power limitation based on the energy model and the performance states. The power limitation is done at the performance domain level. If some CPUs are unplugged, the corresponding power will be subtracted from the performance domain total power. It is up to the platform to initialize the dtpm tree and add the CPU. Here is an example to create a simple tree with one root node called "pkg" and the CPU's performance domains. static int dtpm_register_pkg(struct dtpm_descr *descr) { struct dtpm *pkg; int ret; pkg = dtpm_alloc(NULL); if (!pkg) return -ENOMEM; ret = dtpm_register(descr->name, pkg, descr->parent); if (ret) return ret; return dtpm_register_cpu(pkg); } static struct dtpm_descr descr = { .name = "pkg", .init = dtpm_register_pkg, }; DTPM_DECLARE(descr); Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- drivers/powercap/Kconfig | 7 + drivers/powercap/Makefile | 1 + drivers/powercap/dtpm_cpu.c | 257 ++++++++++++++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + include/linux/dtpm.h | 2 + 5 files changed, 268 insertions(+) create mode 100644 drivers/powercap/dtpm_cpu.c diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index cc1953bd8bed..20b4325c6161 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -49,4 +49,11 @@ config DTPM help This enables support for the power capping for the dynamic thermal power management userspace engine. + +config DTPM_CPU + bool "Add CPU power capping based on the energy model" + depends on DTPM && ENERGY_MODEL + help + This enables support for CPU power limitation based on + energy model. endif diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile index 6482ac52054d..fabcf388a8d3 100644 --- a/drivers/powercap/Makefile +++ b/drivers/powercap/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DTPM) += dtpm.o +obj-$(CONFIG_DTPM_CPU) += dtpm_cpu.o obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c new file mode 100644 index 000000000000..6933c783c6b4 --- /dev/null +++ b/drivers/powercap/dtpm_cpu.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Linaro Limited + * + * Author: Daniel Lezcano + * + * The DTPM CPU is based on the energy model. It hooks the CPU in the + * DTPM tree which in turns update the power number by propagating the + * power number from the CPU energy model information to the parents. + * + * The association between the power and the performance state, allows + * to set the power of the CPU at the OPP granularity. + * + * The CPU hotplug is supported and the power numbers will be updated + * if a CPU is hot plugged / unplugged. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static struct dtpm *__parent; + +static DEFINE_PER_CPU(struct dtpm *, dtpm_per_cpu); + +struct dtpm_cpu { + struct freq_qos_request qos_req; + int cpu; +}; + +/* + * When a new CPU is inserted at hotplug or boot time, add the power + * contribution and update the dtpm tree. + */ +static int power_add(struct dtpm *dtpm, struct em_perf_domain *em) +{ + u64 power_min, power_max; + + power_min = em->table[0].power; + power_min *= MICROWATT_PER_MILLIWATT; + power_min += dtpm->power_min; + + power_max = em->table[em->nr_perf_states - 1].power; + power_max *= MICROWATT_PER_MILLIWATT; + power_max += dtpm->power_max; + + return dtpm_update_power(dtpm, power_min, power_max); +} + +/* + * When a CPU is unplugged, remove its power contribution from the + * dtpm tree. + */ +static int power_sub(struct dtpm *dtpm, struct em_perf_domain *em) +{ + u64 power_min, power_max; + + power_min = em->table[0].power; + power_min *= MICROWATT_PER_MILLIWATT; + power_min = dtpm->power_min - power_min; + + power_max = em->table[em->nr_perf_states - 1].power; + power_max *= MICROWATT_PER_MILLIWATT; + power_max = dtpm->power_max - power_max; + + return dtpm_update_power(dtpm, power_min, power_max); +} + +static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) +{ + struct dtpm_cpu *dtpm_cpu = dtpm->private; + struct em_perf_domain *pd; + struct cpumask cpus; + unsigned long freq; + u64 power; + int i, nr_cpus; + + pd = em_cpu_get(dtpm_cpu->cpu); + + cpumask_and(&cpus, cpu_online_mask, to_cpumask(pd->cpus)); + + nr_cpus = cpumask_weight(&cpus); + + for (i = 0; i < pd->nr_perf_states; i++) { + + power = pd->table[i].power * MICROWATT_PER_MILLIWATT * nr_cpus; + + if (power > power_limit) + break; + } + + freq = pd->table[i - 1].frequency; + + freq_qos_update_request(&dtpm_cpu->qos_req, freq); + + power_limit = pd->table[i - 1].power * + MICROWATT_PER_MILLIWATT * nr_cpus; + + return power_limit; +} + +static u64 get_pd_power_uw(struct dtpm *dtpm) +{ + struct dtpm_cpu *dtpm_cpu = dtpm->private; + struct em_perf_domain *pd; + struct cpumask cpus; + unsigned long freq; + int i, nr_cpus; + + pd = em_cpu_get(dtpm_cpu->cpu); + freq = cpufreq_quick_get(dtpm_cpu->cpu); + cpumask_and(&cpus, cpu_online_mask, to_cpumask(pd->cpus)); + nr_cpus = cpumask_weight(&cpus); + + for (i = 0; i < pd->nr_perf_states; i++) { + + if (pd->table[i].frequency < freq) + continue; + + return pd->table[i].power * + MICROWATT_PER_MILLIWATT * nr_cpus; + } + + return 0; +} + +static void pd_release(struct dtpm *dtpm) +{ + struct dtpm_cpu *dtpm_cpu = dtpm->private; + + if (freq_qos_request_active(&dtpm_cpu->qos_req)) + freq_qos_remove_request(&dtpm_cpu->qos_req); + + kfree(dtpm_cpu); +} + +static struct dtpm_ops dtpm_ops = { + .set_power_uw = set_pd_power_limit, + .get_power_uw = get_pd_power_uw, + .release = pd_release, +}; + +static int cpuhp_dtpm_cpu_offline(unsigned int cpu) +{ + struct cpufreq_policy *policy; + struct em_perf_domain *pd; + struct dtpm *dtpm; + + policy = cpufreq_cpu_get(cpu); + + if (!policy) + return 0; + + pd = em_cpu_get(cpu); + if (!pd) + return -EINVAL; + + dtpm = per_cpu(dtpm_per_cpu, cpu); + + power_sub(dtpm, pd); + + if (cpumask_weight(policy->cpus) != 1) + return 0; + + for_each_cpu(cpu, policy->related_cpus) + per_cpu(dtpm_per_cpu, cpu) = NULL; + + dtpm_unregister(dtpm); + + return 0; +} + +static int cpuhp_dtpm_cpu_online(unsigned int cpu) +{ + struct dtpm *dtpm; + struct dtpm_cpu *dtpm_cpu; + struct cpufreq_policy *policy; + struct em_perf_domain *pd; + char name[CPUFREQ_NAME_LEN]; + int ret = -ENOMEM; + + policy = cpufreq_cpu_get(cpu); + + if (!policy) + return 0; + + pd = em_cpu_get(cpu); + if (!pd) + return -EINVAL; + + dtpm = per_cpu(dtpm_per_cpu, cpu); + if (dtpm) + return power_add(dtpm, pd); + + dtpm = dtpm_alloc(&dtpm_ops); + if (!dtpm) + return -EINVAL; + + dtpm_cpu = kzalloc(sizeof(dtpm_cpu), GFP_KERNEL); + if (!dtpm_cpu) + goto out_kfree_dtpm; + + dtpm->private = dtpm_cpu; + dtpm_cpu->cpu = cpu; + + for_each_cpu(cpu, policy->related_cpus) + per_cpu(dtpm_per_cpu, cpu) = dtpm; + + sprintf(name, "cpu%d", dtpm_cpu->cpu); + + ret = dtpm_register(name, dtpm, __parent); + if (ret) + goto out_kfree_dtpm_cpu; + + ret = power_add(dtpm, pd); + if (ret) + goto out_dtpm_unregister; + + ret = freq_qos_add_request(&policy->constraints, + &dtpm_cpu->qos_req, FREQ_QOS_MAX, + pd->table[pd->nr_perf_states - 1].frequency); + if (ret) + goto out_power_sub; + + return 0; + +out_power_sub: + power_sub(dtpm, pd); + +out_dtpm_unregister: + dtpm_unregister(dtpm); + dtpm_cpu = NULL; + dtpm = NULL; + +out_kfree_dtpm_cpu: + for_each_cpu(cpu, policy->related_cpus) + per_cpu(dtpm_per_cpu, cpu) = NULL; + kfree(dtpm_cpu); + +out_kfree_dtpm: + kfree(dtpm); + return ret; +} + +int dtpm_register_cpu(struct dtpm *parent) +{ + __parent = parent; + + return cpuhp_setup_state(CPUHP_AP_DTPM_CPU_ONLINE, + "dtpm_cpu:online", + cpuhp_dtpm_cpu_online, + cpuhp_dtpm_cpu_offline); +} diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 0042ef362511..ee09a39627d6 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -193,6 +193,7 @@ enum cpuhp_state { CPUHP_AP_ONLINE_DYN_END = CPUHP_AP_ONLINE_DYN + 30, CPUHP_AP_X86_HPET_ONLINE, CPUHP_AP_X86_KVM_CLK_ONLINE, + CPUHP_AP_DTPM_CPU_ONLINE, CPUHP_AP_ACTIVE, CPUHP_ONLINE, }; diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h index 7a1d0b50e334..e80a332e3d8a 100644 --- a/include/linux/dtpm.h +++ b/include/linux/dtpm.h @@ -72,4 +72,6 @@ void dtpm_unregister(struct dtpm *dtpm); int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent); +int dtpm_register_cpu(struct dtpm *parent); + #endif From 8f50db4b5c79af2ba54f5fbe8a5173fd7f37a493 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 30 Dec 2020 16:37:44 +0100 Subject: [PATCH 05/89] powercap/drivers/dtpm: Fix __udivdi3 and __aeabi_uldivmod unresolved symbols 32-bit architectures do not support u64 divisions, so the macro DIV_ROUND_CLOSEST is not adequate as the compiler will replace the call to an unexisting function for the platform, leading to unresolved references to symbols. Fix this by using the compatible macros: DIV64_U64_ROUND_CLOSEST and DIV_ROUND_CLOSEST_ULL. Fixes: a20d0ef97abf ("powercap/drivers/dtpm: Add API for dynamic thermal power management") Reported-by: kernel test robot Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/powercap/dtpm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 5b6857e9b064..0abcc439d728 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -99,8 +99,8 @@ static void __dtpm_rebalance_weight(struct dtpm *dtpm) pr_debug("Setting weight '%d' for '%s'\n", child->weight, child->zone.name); - child->weight = DIV_ROUND_CLOSEST(child->power_max * 1024, - dtpm->power_max); + child->weight = DIV64_U64_ROUND_CLOSEST( + child->power_max * 1024, dtpm->power_max); __dtpm_rebalance_weight(child); } @@ -272,7 +272,7 @@ static int __set_power_limit_uw(struct dtpm *dtpm, int cid, u64 power_limit) } else if (power_limit == dtpm->power_min) { power = child->power_min; } else { - power = DIV_ROUND_CLOSEST( + power = DIV_ROUND_CLOSEST_ULL( power_limit * child->weight, 1024); } From f40d81231b2ddfac41d5bf09462b260b256e15ba Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Mon, 4 Jan 2021 13:59:12 +0000 Subject: [PATCH 06/89] PM / devfreq: Correct spelling in a comment The device attribute exposed in sysfs is called 'polling_interval'. Align the comment. Signed-off-by: Lukasz Luba Signed-off-by: Chanwoo Choi --- drivers/devfreq/governor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index 2a52f97b542d..70f44b3ca42e 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -40,7 +40,7 @@ /* * Definition of governor attribute flags except for common sysfs attributes * - DEVFREQ_GOV_ATTR_POLLING_INTERVAL - * : Indicate polling_interal sysfs attribute + * : Indicate polling_interval sysfs attribute * - DEVFREQ_GOV_ATTR_TIMER * : Indicate timer sysfs attribute */ From ec894883de5336e28313e531e2f3a8b86f1a8a1a Mon Sep 17 00:00:00 2001 From: pierre Kuo Date: Wed, 16 Dec 2020 10:25:38 +0800 Subject: [PATCH 07/89] PM / devfreq: Replace devfreq->dev.parent as dev in devfreq_add_device In devfreq_add_device, replace devfreq->dev.parent as dev to keep code simple. Signed-off-by: pierre Kuo Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 6aa10de792b3..94cc25fd68da 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -893,13 +893,13 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_devfreq; devfreq->nb_min.notifier_call = qos_min_notifier_call; - err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min, + err = dev_pm_qos_add_notifier(dev, &devfreq->nb_min, DEV_PM_QOS_MIN_FREQUENCY); if (err) goto err_devfreq; devfreq->nb_max.notifier_call = qos_max_notifier_call; - err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max, + err = dev_pm_qos_add_notifier(dev, &devfreq->nb_max, DEV_PM_QOS_MAX_FREQUENCY); if (err) goto err_devfreq; From fb7791e213a64495ec2336869b868fcd8af14346 Mon Sep 17 00:00:00 2001 From: Ivan Babrou Date: Mon, 4 Jan 2021 15:57:18 -0800 Subject: [PATCH 08/89] cpupower: add Makefile dependencies for install targets This allows building cpupower in parallel rather than serially. Signed-off-by: Ivan Babrou Signed-off-by: Shuah Khan --- tools/power/cpupower/Makefile | 8 ++++---- tools/power/cpupower/bench/Makefile | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index c7bcddbd486d..3b1594447f29 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -270,14 +270,14 @@ clean: $(MAKE) -C bench O=$(OUTPUT) clean -install-lib: +install-lib: libcpupower $(INSTALL) -d $(DESTDIR)${libdir} $(CP) $(OUTPUT)libcpupower.so* $(DESTDIR)${libdir}/ $(INSTALL) -d $(DESTDIR)${includedir} $(INSTALL_DATA) lib/cpufreq.h $(DESTDIR)${includedir}/cpufreq.h $(INSTALL_DATA) lib/cpuidle.h $(DESTDIR)${includedir}/cpuidle.h -install-tools: +install-tools: $(OUTPUT)cpupower $(INSTALL) -d $(DESTDIR)${bindir} $(INSTALL_PROGRAM) $(OUTPUT)cpupower $(DESTDIR)${bindir} $(INSTALL) -d $(DESTDIR)${bash_completion_dir} @@ -293,14 +293,14 @@ install-man: $(INSTALL_DATA) -D man/cpupower-info.1 $(DESTDIR)${mandir}/man1/cpupower-info.1 $(INSTALL_DATA) -D man/cpupower-monitor.1 $(DESTDIR)${mandir}/man1/cpupower-monitor.1 -install-gmo: +install-gmo: create-gmo $(INSTALL) -d $(DESTDIR)${localedir} for HLANG in $(LANGUAGES); do \ echo '$(INSTALL_DATA) -D $(OUTPUT)po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo'; \ $(INSTALL_DATA) -D $(OUTPUT)po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \ done; -install-bench: +install-bench: compile-bench @#DESTDIR must be set from outside to survive @sbindir=$(sbindir) bindir=$(bindir) docdir=$(docdir) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT) install diff --git a/tools/power/cpupower/bench/Makefile b/tools/power/cpupower/bench/Makefile index f68b4bc55273..d9d9923af85c 100644 --- a/tools/power/cpupower/bench/Makefile +++ b/tools/power/cpupower/bench/Makefile @@ -27,7 +27,7 @@ $(OUTPUT)cpufreq-bench: $(OBJS) all: $(OUTPUT)cpufreq-bench -install: +install: $(OUTPUT)cpufreq-bench mkdir -p $(DESTDIR)/$(sbindir) mkdir -p $(DESTDIR)/$(bindir) mkdir -p $(DESTDIR)/$(docdir) From 2185c23071e2c1f26fbccb323aa831732540cfcc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 Jan 2021 11:36:35 +0300 Subject: [PATCH 09/89] powercap/drivers/dtpm: Fix a double shift bug The DTPM_POWER_LIMIT_FLAG is used for test_bit() etc which take a bit number so it should be bit 0. But currently it's set to BIT(0) then that is double shifted equivalent to BIT(BIT(0)). This doesn't cause a run time problem because it's done consistently. Fixes: a20d0ef97abf ("powercap/drivers/dtpm: Add API for dynamic thermal power management") Signed-off-by: Dan Carpenter Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/powercap/dtpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 0abcc439d728..d49df0569cd4 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -24,7 +24,7 @@ #include #include -#define DTPM_POWER_LIMIT_FLAG BIT(0) +#define DTPM_POWER_LIMIT_FLAG 0 static const char *constraint_name[] = { "Instantaneous", From 0fe1329b7b518f67c8f1760711eb0eaf90433fd3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 Jan 2021 11:41:09 +0300 Subject: [PATCH 10/89] powercap/drivers/dtpm: Fix some missing unlock bugs We need to unlock on these paths before returning. Fixes: a20d0ef97abf ("powercap/drivers/dtpm: Add API for dynamic thermal power management") Signed-off-by: Dan Carpenter Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/powercap/dtpm.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index d49df0569cd4..470a1182b868 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -147,13 +147,17 @@ static void __dtpm_add_power(struct dtpm *dtpm) */ int dtpm_update_power(struct dtpm *dtpm, u64 power_min, u64 power_max) { + int ret = 0; + mutex_lock(&dtpm_lock); if (power_min == dtpm->power_min && power_max == dtpm->power_max) - return 0; + goto unlock; - if (power_max < power_min) - return -EINVAL; + if (power_max < power_min) { + ret = -EINVAL; + goto unlock; + } __dtpm_sub_power(dtpm); @@ -164,9 +168,10 @@ int dtpm_update_power(struct dtpm *dtpm, u64 power_min, u64 power_max) __dtpm_add_power(dtpm); +unlock: mutex_unlock(&dtpm_lock); - return 0; + return ret; } /** @@ -187,8 +192,10 @@ int dtpm_release_zone(struct powercap_zone *pcz) mutex_lock(&dtpm_lock); - if (!list_empty(&dtpm->children)) + if (!list_empty(&dtpm->children)) { + mutex_unlock(&dtpm_lock); return -EBUSY; + } if (parent) list_del(&dtpm->sibling); From f8f706ad75abbc65fee365853e7b24731223fd6d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 Jan 2021 12:03:08 +0300 Subject: [PATCH 11/89] powercap/drivers/dtpm: Fix an IS_ERR() vs NULL check The powercap_register_control_type() function never returns NULL, it returns error pointers on error so update this check. Fixes: a20d0ef97abf ("powercap/drivers/dtpm: Add API for dynamic thermal power management") Signed-off-by: Dan Carpenter Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/powercap/dtpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 470a1182b868..5a51cd34a7e8 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -467,9 +467,9 @@ static int __init dtpm_init(void) struct dtpm_descr **dtpm_descr; pct = powercap_register_control_type(NULL, "dtpm", NULL); - if (!pct) { + if (IS_ERR(pct)) { pr_err("Failed to register control type\n"); - return -EINVAL; + return PTR_ERR(pct); } for_each_dtpm_table(dtpm_descr) From 66e713fbbbc6c259559d4937a3b016d36ab529ff Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 4 Jan 2021 12:10:53 +0000 Subject: [PATCH 12/89] powercap/drivers/dtpm: Fix size of object being allocated The kzalloc allocation for dtpm_cpu is currently allocating the size of the pointer and not the size of the structure. Fix this by using the correct sizeof argument. Addresses-Coverity: ("Wrong sizeof argument") Fixes: 0e8f68d7f048 ("powercap/drivers/dtpm: Add CPU energy model based support") Signed-off-by: Colin Ian King Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/powercap/dtpm_cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c index 6933c783c6b4..51c366938acd 100644 --- a/drivers/powercap/dtpm_cpu.c +++ b/drivers/powercap/dtpm_cpu.c @@ -200,7 +200,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu) if (!dtpm) return -EINVAL; - dtpm_cpu = kzalloc(sizeof(dtpm_cpu), GFP_KERNEL); + dtpm_cpu = kzalloc(sizeof(*dtpm_cpu), GFP_KERNEL); if (!dtpm_cpu) goto out_kfree_dtpm; From 9dd04ec6bc6fa7b310e5595f2ad9bef13eacd3a0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 7 Jan 2021 19:42:15 +0100 Subject: [PATCH 13/89] cpufreq: intel_pstate: Always read hwp_cap_cached with READ_ONCE() Because intel_pstate_get_hwp_max() which updates hwp_cap_cached may run in parallel with the readers of it, annotate all of the read accesses to it with READ_ONCE(). Signed-off-by: Rafael J. Wysocki Tested-by: Chen Yu --- drivers/cpufreq/intel_pstate.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index be05e038d956..74bf54e6c993 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -914,7 +914,7 @@ static void intel_pstate_hwp_offline(struct cpudata *cpu) } value &= ~GENMASK_ULL(31, 0); - min_perf = HWP_LOWEST_PERF(cpu->hwp_cap_cached); + min_perf = HWP_LOWEST_PERF(READ_ONCE(cpu->hwp_cap_cached)); /* Set hwp_max = hwp_min */ value |= HWP_MAX_PERF(min_perf); @@ -1750,6 +1750,7 @@ static int hwp_boost_hold_time_ns = 3 * NSEC_PER_MSEC; static inline void intel_pstate_hwp_boost_up(struct cpudata *cpu) { u64 hwp_req = READ_ONCE(cpu->hwp_req_cached); + u64 hwp_cap = READ_ONCE(cpu->hwp_cap_cached); u32 max_limit = (hwp_req & 0xff00) >> 8; u32 min_limit = (hwp_req & 0xff); u32 boost_level1; @@ -1776,14 +1777,14 @@ static inline void intel_pstate_hwp_boost_up(struct cpudata *cpu) cpu->hwp_boost_min = min_limit; /* level at half way mark between min and guranteed */ - boost_level1 = (HWP_GUARANTEED_PERF(cpu->hwp_cap_cached) + min_limit) >> 1; + boost_level1 = (HWP_GUARANTEED_PERF(hwp_cap) + min_limit) >> 1; if (cpu->hwp_boost_min < boost_level1) cpu->hwp_boost_min = boost_level1; - else if (cpu->hwp_boost_min < HWP_GUARANTEED_PERF(cpu->hwp_cap_cached)) - cpu->hwp_boost_min = HWP_GUARANTEED_PERF(cpu->hwp_cap_cached); - else if (cpu->hwp_boost_min == HWP_GUARANTEED_PERF(cpu->hwp_cap_cached) && - max_limit != HWP_GUARANTEED_PERF(cpu->hwp_cap_cached)) + else if (cpu->hwp_boost_min < HWP_GUARANTEED_PERF(hwp_cap)) + cpu->hwp_boost_min = HWP_GUARANTEED_PERF(hwp_cap); + else if (cpu->hwp_boost_min == HWP_GUARANTEED_PERF(hwp_cap) && + max_limit != HWP_GUARANTEED_PERF(hwp_cap)) cpu->hwp_boost_min = max_limit; else return; From a45ee4d4e13b0e35a8ec7ea0bf9267243d57b302 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 7 Jan 2021 19:43:30 +0100 Subject: [PATCH 14/89] cpufreq: intel_pstate: Change intel_pstate_get_hwp_max() argument All of the callers of intel_pstate_get_hwp_max() access the struct cpudata object that corresponds to the given CPU already and the function itself needs to access that object (in order to update hwp_cap_cached), so modify the code to pass a struct cpudata pointer to it instead of the CPU number. Signed-off-by: Rafael J. Wysocki Tested-by: Chen Yu --- drivers/cpufreq/intel_pstate.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 74bf54e6c993..3eb63daf2523 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -819,13 +819,13 @@ static struct freq_attr *hwp_cpufreq_attrs[] = { NULL, }; -static void intel_pstate_get_hwp_max(unsigned int cpu, int *phy_max, +static void intel_pstate_get_hwp_max(struct cpudata *cpu, int *phy_max, int *current_max) { u64 cap; - rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap); - WRITE_ONCE(all_cpu_data[cpu]->hwp_cap_cached, cap); + rdmsrl_on_cpu(cpu->cpu, MSR_HWP_CAPABILITIES, &cap); + WRITE_ONCE(cpu->hwp_cap_cached, cap); if (global.no_turbo || global.turbo_disabled) *current_max = HWP_GUARANTEED_PERF(cap); else @@ -1213,7 +1213,7 @@ static void update_qos_request(enum freq_qos_req_type type) continue; if (hwp_active) - intel_pstate_get_hwp_max(i, &turbo_max, &max_state); + intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); else turbo_max = cpu->pstate.turbo_pstate; @@ -1723,7 +1723,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) if (hwp_active && !hwp_mode_bdw) { unsigned int phy_max, current_max; - intel_pstate_get_hwp_max(cpu->cpu, &phy_max, ¤t_max); + intel_pstate_get_hwp_max(cpu, &phy_max, ¤t_max); cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling; cpu->pstate.turbo_pstate = phy_max; } else { @@ -2208,7 +2208,7 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, * rather than pure ratios. */ if (hwp_active) { - intel_pstate_get_hwp_max(cpu->cpu, &turbo_max, &max_state); + intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); } else { max_state = global.no_turbo || global.turbo_disabled ? cpu->pstate.max_pstate : cpu->pstate.turbo_pstate; @@ -2323,7 +2323,7 @@ static void intel_pstate_verify_cpu_policy(struct cpudata *cpu, if (hwp_active) { int max_state, turbo_max; - intel_pstate_get_hwp_max(cpu->cpu, &turbo_max, &max_state); + intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); max_freq = max_state * cpu->pstate.scaling; } else { max_freq = intel_pstate_get_max_freq(cpu); @@ -2710,7 +2710,7 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) if (hwp_active) { u64 value; - intel_pstate_get_hwp_max(policy->cpu, &turbo_max, &max_state); + intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY_HWP; rdmsrl_on_cpu(cpu->cpu, MSR_HWP_REQUEST, &value); WRITE_ONCE(cpu->hwp_req_cached, value); From 597ffbc8d085870e071807b514a6ed45809f81a5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 7 Jan 2021 19:44:18 +0100 Subject: [PATCH 15/89] cpufreq: intel_pstate: Rename two functions Rename intel_cpufreq_adjust_hwp() and intel_cpufreq_adjust_perf_ctl() to intel_cpufreq_hwp_update() and intel_cpufreq_perf_ctl_update(), respectively, to avoid possible confusion with the ->adjist_perf() callback function, intel_cpufreq_adjust_perf(). Signed-off-by: Rafael J. Wysocki Tested-by: Chen Yu --- drivers/cpufreq/intel_pstate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 3eb63daf2523..86873f4c6a72 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2527,7 +2527,7 @@ static void intel_cpufreq_trace(struct cpudata *cpu, unsigned int trace_type, in fp_toint(cpu->iowait_boost * 100)); } -static void intel_cpufreq_adjust_hwp(struct cpudata *cpu, u32 min, u32 max, +static void intel_cpufreq_hwp_update(struct cpudata *cpu, u32 min, u32 max, u32 desired, bool fast_switch) { u64 prev = READ_ONCE(cpu->hwp_req_cached), value = prev; @@ -2551,7 +2551,7 @@ static void intel_cpufreq_adjust_hwp(struct cpudata *cpu, u32 min, u32 max, wrmsrl_on_cpu(cpu->cpu, MSR_HWP_REQUEST, value); } -static void intel_cpufreq_adjust_perf_ctl(struct cpudata *cpu, +static void intel_cpufreq_perf_ctl_update(struct cpudata *cpu, u32 target_pstate, bool fast_switch) { if (fast_switch) @@ -2573,10 +2573,10 @@ static int intel_cpufreq_update_pstate(struct cpufreq_policy *policy, int max_pstate = policy->strict_target ? target_pstate : cpu->max_perf_ratio; - intel_cpufreq_adjust_hwp(cpu, target_pstate, max_pstate, 0, + intel_cpufreq_hwp_update(cpu, target_pstate, max_pstate, 0, fast_switch); } else if (target_pstate != old_pstate) { - intel_cpufreq_adjust_perf_ctl(cpu, target_pstate, fast_switch); + intel_cpufreq_perf_ctl_update(cpu, target_pstate, fast_switch); } cpu->pstate.current_pstate = target_pstate; @@ -2674,7 +2674,7 @@ static void intel_cpufreq_adjust_perf(unsigned int cpunum, target_pstate = clamp_t(int, target_pstate, min_pstate, max_pstate); - intel_cpufreq_adjust_hwp(cpu, min_pstate, max_pstate, target_pstate, true); + intel_cpufreq_hwp_update(cpu, min_pstate, max_pstate, target_pstate, true); cpu->pstate.current_pstate = target_pstate; intel_cpufreq_trace(cpu, INTEL_PSTATE_TRACE_FAST_SWITCH, old_pstate); From 6f67e060083a84a4cc364eab6ae40c717165fb0c Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Tue, 12 Jan 2021 13:21:27 +0800 Subject: [PATCH 16/89] cpufreq: intel_pstate: Get per-CPU max freq via MSR_HWP_CAPABILITIES if available Currently, when turbo is disabled (either by BIOS or by the user), the intel_pstate driver reads the max non-turbo frequency from the package-wide MSR_PLATFORM_INFO(0xce) register. However, on asymmetric platforms it is possible in theory that small and big core with HWP enabled might have different max non-turbo CPU frequency, because MSR_HWP_CAPABILITIES is per-CPU scope according to Intel Software Developer Manual. The turbo max freq is already per-CPU in current code, so make similar change to the max non-turbo frequency as well. Reported-by: Wendy Wang Signed-off-by: Chen Yu [ rjw: Subject and changelog edits ] Cc: 4.18+ # 4.18+: a45ee4d4e13b: cpufreq: intel_pstate: Change intel_pstate_get_hwp_max() argument Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 86873f4c6a72..6f2ff2775664 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1714,11 +1714,9 @@ static void intel_pstate_max_within_limits(struct cpudata *cpu) static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) { cpu->pstate.min_pstate = pstate_funcs.get_min(); - cpu->pstate.max_pstate = pstate_funcs.get_max(); cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(); cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); cpu->pstate.scaling = pstate_funcs.get_scaling(); - cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling; if (hwp_active && !hwp_mode_bdw) { unsigned int phy_max, current_max; @@ -1726,9 +1724,12 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) intel_pstate_get_hwp_max(cpu, &phy_max, ¤t_max); cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling; cpu->pstate.turbo_pstate = phy_max; + cpu->pstate.max_pstate = HWP_GUARANTEED_PERF(READ_ONCE(cpu->hwp_cap_cached)); } else { cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; + cpu->pstate.max_pstate = pstate_funcs.get_max(); } + cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling; if (pstate_funcs.get_aperf_mperf_shift) cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); From 763ec5daaea835e5604d08364c9081e7304b7c2b Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 02:18:25 +0300 Subject: [PATCH 17/89] cpufreq: tegra20: Use resource-managed API Switch cpufreq-tegra20 driver to use resource-managed API. This removes the need to get opp_table pointer using dev_pm_opp_get_opp_table() in order to release OPP table that was requested by dev_pm_opp_set_supported_hw(), making the code a bit more straightforward. Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/cpufreq/tegra20-cpufreq.c | 45 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/cpufreq/tegra20-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c index 8c893043953e..e8db3d75be25 100644 --- a/drivers/cpufreq/tegra20-cpufreq.c +++ b/drivers/cpufreq/tegra20-cpufreq.c @@ -32,6 +32,16 @@ static bool cpu0_node_has_opp_v2_prop(void) return ret; } +static void tegra20_cpufreq_put_supported_hw(void *opp_table) +{ + dev_pm_opp_put_supported_hw(opp_table); +} + +static void tegra20_cpufreq_dt_unregister(void *cpufreq_dt) +{ + platform_device_unregister(cpufreq_dt); +} + static int tegra20_cpufreq_probe(struct platform_device *pdev) { struct platform_device *cpufreq_dt; @@ -68,42 +78,31 @@ static int tegra20_cpufreq_probe(struct platform_device *pdev) return err; } + err = devm_add_action_or_reset(&pdev->dev, + tegra20_cpufreq_put_supported_hw, + opp_table); + if (err) + return err; + cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); err = PTR_ERR_OR_ZERO(cpufreq_dt); if (err) { dev_err(&pdev->dev, "failed to create cpufreq-dt device: %d\n", err); - goto err_put_supported_hw; + return err; } - platform_set_drvdata(pdev, cpufreq_dt); - - return 0; - -err_put_supported_hw: - dev_pm_opp_put_supported_hw(opp_table); - - return err; -} - -static int tegra20_cpufreq_remove(struct platform_device *pdev) -{ - struct platform_device *cpufreq_dt; - struct opp_table *opp_table; - - cpufreq_dt = platform_get_drvdata(pdev); - platform_device_unregister(cpufreq_dt); - - opp_table = dev_pm_opp_get_opp_table(get_cpu_device(0)); - dev_pm_opp_put_supported_hw(opp_table); - dev_pm_opp_put_opp_table(opp_table); + err = devm_add_action_or_reset(&pdev->dev, + tegra20_cpufreq_dt_unregister, + cpufreq_dt); + if (err) + return err; return 0; } static struct platform_driver tegra20_cpufreq_driver = { .probe = tegra20_cpufreq_probe, - .remove = tegra20_cpufreq_remove, .driver = { .name = "tegra20-cpufreq", }, From 266991721c15f9feb5c4b839cb1bdde4a2b20030 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 13 Jan 2021 14:52:41 +0800 Subject: [PATCH 18/89] cpufreq: qcom-hw: enable boost support At least on sdm850, the 2956800 khz is detected as a boost frequency in function qcom_cpufreq_hw_read_lut(). Let's enable boost support by calling cpufreq_enable_boost_support(), so that we can get the boost frequency by switching it on via 'boost' sysfs entry like below. $ echo 1 > /sys/devices/system/cpu/cpufreq/boost Signed-off-by: Shawn Guo Tested-by: Steev Klimaszewski Reviewed-by: Bjorn Andersson Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 9ed5341dc515..acc645b85e79 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -347,6 +347,12 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) dev_pm_opp_of_register_em(cpu_dev, policy->cpus); + if (policy_has_boost_freq(policy)) { + ret = cpufreq_enable_boost_support(); + if (ret) + dev_warn(cpu_dev, "failed to enable boost: %d\n", ret); + } + return 0; error: devm_iounmap(dev, base); From 05f456286fd489558c72a4711d22a5612c965685 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 17 Jan 2021 15:26:35 +0100 Subject: [PATCH 19/89] cpufreq: brcmstb-avs-cpufreq: Free resources in error path If 'cpufreq_register_driver()' fails, we must release the resources allocated in 'brcm_avs_prepare_init()' as already done in the remove function. To do that, introduce a new function 'brcm_avs_prepare_uninit()' in order to avoid code duplication. This also makes the code more readable (IMHO). Fixes: de322e085995 ("cpufreq: brcmstb-avs-cpufreq: AVS CPUfreq driver for Broadcom STB SoCs") Signed-off-by: Christophe JAILLET [ Viresh: Updated Subject ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/brcmstb-avs-cpufreq.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 3e31e5d28b79..e25ccb744187 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -597,6 +597,16 @@ unmap_base: return ret; } +static void brcm_avs_prepare_uninit(struct platform_device *pdev) +{ + struct private_data *priv; + + priv = platform_get_drvdata(pdev); + + iounmap(priv->avs_intr_base); + iounmap(priv->base); +} + static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy) { struct cpufreq_frequency_table *freq_table; @@ -732,21 +742,22 @@ static int brcm_avs_cpufreq_probe(struct platform_device *pdev) brcm_avs_driver.driver_data = pdev; - return cpufreq_register_driver(&brcm_avs_driver); + ret = cpufreq_register_driver(&brcm_avs_driver); + if (ret) + brcm_avs_prepare_uninit(pdev); + + return ret; } static int brcm_avs_cpufreq_remove(struct platform_device *pdev) { - struct private_data *priv; int ret; ret = cpufreq_unregister_driver(&brcm_avs_driver); if (ret) return ret; - priv = platform_get_drvdata(pdev); - iounmap(priv->base); - iounmap(priv->avs_intr_base); + brcm_avs_prepare_uninit(pdev); return 0; } From 3657f729b6fb5f2c0bf693742de2dcd49c572aa1 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 17 Jan 2021 15:26:44 +0100 Subject: [PATCH 20/89] cpufreq: brcmstb-avs-cpufreq: Fix resource leaks in ->remove() If 'cpufreq_unregister_driver()' fails, just WARN and continue, so that other resources are freed. Fixes: de322e085995 ("cpufreq: brcmstb-avs-cpufreq: AVS CPUfreq driver for Broadcom STB SoCs") Signed-off-by: Christophe JAILLET [ Viresh: Updated Subject ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/brcmstb-avs-cpufreq.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index e25ccb744187..4153150e20db 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -754,8 +754,7 @@ static int brcm_avs_cpufreq_remove(struct platform_device *pdev) int ret; ret = cpufreq_unregister_driver(&brcm_avs_driver); - if (ret) - return ret; + WARN_ON(ret); brcm_avs_prepare_uninit(pdev); From 7114ebffd330bfc5a95b9832a70b6bd857d26fd8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 Jan 2021 14:16:44 +0100 Subject: [PATCH 21/89] cpufreq: remove tango driver The tango platform is getting removed, so the driver is no longer needed. Cc: Marc Gonzalez Cc: Mans Rullgard Signed-off-by: Arnd Bergmann [ Viresh: Update cpufreq-dt-platdev.c as well ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 5 ---- drivers/cpufreq/Makefile | 1 - drivers/cpufreq/cpufreq-dt-platdev.c | 2 -- drivers/cpufreq/tango-cpufreq.c | 38 ---------------------------- 4 files changed, 46 deletions(-) delete mode 100644 drivers/cpufreq/tango-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 1f73fa75b1a0..e65e0a43be64 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -289,11 +289,6 @@ config ARM_STI_CPUFREQ this config option if you wish to add CPUFreq support for STi based SoCs. -config ARM_TANGO_CPUFREQ - bool - depends on CPUFREQ_DT && ARCH_TANGO - default y - config ARM_TEGRA20_CPUFREQ tristate "Tegra20/30 CPUFreq support" depends on ARCH_TEGRA && CPUFREQ_DT diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index f1b7e3dd6e5d..1ab9b1536304 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -79,7 +79,6 @@ obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o obj-$(CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM) += sun50i-cpufreq-nvmem.o -obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index bd2db0188cbb..3ba2f716fe97 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -141,8 +141,6 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "st,stih410", }, { .compatible = "st,stih418", }, - { .compatible = "sigma,tango4", }, - { .compatible = "ti,am33xx", }, { .compatible = "ti,am43", }, { .compatible = "ti,dra7", }, diff --git a/drivers/cpufreq/tango-cpufreq.c b/drivers/cpufreq/tango-cpufreq.c deleted file mode 100644 index 89a7f860bfe8..000000000000 --- a/drivers/cpufreq/tango-cpufreq.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include -#include - -static const struct of_device_id machines[] __initconst = { - { .compatible = "sigma,tango4" }, - { /* sentinel */ } -}; - -static int __init tango_cpufreq_init(void) -{ - struct device *cpu_dev = get_cpu_device(0); - unsigned long max_freq; - struct clk *cpu_clk; - void *res; - - if (!of_match_node(machines, of_root)) - return -ENODEV; - - cpu_clk = clk_get(cpu_dev, NULL); - if (IS_ERR(cpu_clk)) - return -ENODEV; - - max_freq = clk_get_rate(cpu_clk); - - dev_pm_opp_add(cpu_dev, max_freq / 1, 0); - dev_pm_opp_add(cpu_dev, max_freq / 2, 0); - dev_pm_opp_add(cpu_dev, max_freq / 3, 0); - dev_pm_opp_add(cpu_dev, max_freq / 5, 0); - dev_pm_opp_add(cpu_dev, max_freq / 9, 0); - - res = platform_device_register_data(NULL, "cpufreq-dt", -1, NULL, 0); - - return PTR_ERR_OR_ZERO(res); -} -device_initcall(tango_cpufreq_init); From 651bc5816c39e57833fea4478c8ecfb72ad47e44 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Fri, 15 Jan 2021 15:56:46 -0800 Subject: [PATCH 22/89] intel_idle: remove definition of DEBUG Defining DEBUG should only be done in development. So remove DEBUG. Signed-off-by: Tom Rix Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 28f93b9aa51b..3273360f30f7 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -37,7 +37,7 @@ */ /* un-comment DEBUG to enable pr_debug() statements */ -#define DEBUG +/* #define DEBUG */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt From 10aa694ea0d0adfbd97400fb39ea237a273c335f Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Wed, 20 Jan 2021 20:03:12 +0530 Subject: [PATCH 23/89] PM: runtime: Fix resposible -> responsible in runtime.c s/resposible/responsible/ Signed-off-by: Bhaskar Chowdhury Acked-by: Randy Dunlap [ rjw: Subject edit ] Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index bfda153b1a41..a46a7e30881b 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1100,7 +1100,7 @@ EXPORT_SYMBOL_GPL(__pm_runtime_resume); * suspending the device when both its runtime PM status is %RPM_ACTIVE and its * runtime PM usage counter is not zero. * - * The caller is resposible for decrementing the runtime PM usage counter of + * The caller is responsible for decrementing the runtime PM usage counter of * @dev after this function has returned a positive value for it. */ int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count) From 75a8d877d65732b9669a0ebaa36311f12011fdcd Mon Sep 17 00:00:00 2001 From: Nigel Christian Date: Sat, 16 Jan 2021 19:47:05 -0500 Subject: [PATCH 24/89] cpufreq: intel_pstate: Remove repeated word In the comment for trace in passive mode there is an unnecessary "the". Eradicate it. Signed-off-by: Nigel Christian Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6f2ff2775664..5175ae3cac44 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2498,7 +2498,7 @@ static int intel_cpufreq_verify_policy(struct cpufreq_policy_data *policy) * driver call was via the normal or fast switch path. Various graphs * output from the intel_pstate_tracer.py utility that include core_busy * (or performance or core_avg_perf) have a fixed y-axis from 0 to 100%, - * so we use 10 to indicate the the normal path through the driver, and + * so we use 10 to indicate the normal path through the driver, and * 90 to indicate the fast switch path through the driver. * The scaled_busy field is not used, and is set to 0. */ From 67e3242ee28052daa90e1fa193efc601939fde58 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Wed, 20 Jan 2021 08:50:41 -0700 Subject: [PATCH 25/89] PM: domains: inform PM domain of a device's next wakeup Some devices may have a predictable interrupt pattern while executing usecases. An example would be the VSYNC interrupt associated with display devices. A 60 Hz display could cause a interrupt every 16 ms. If the device were in a PM domain, the domain would need to be powered up for device to resume and handle the interrupt. Entering a domain idle state saves power, only if the residency of the idle state is met. Without knowing the idle duration of the domain, the governor would just choose the deepest idle state that matches the QoS requirements. The domain might be powered off just as the device is expecting to wake up. If devices could inform PM frameworks of their next event, the parent PM domain's idle duration can be determined. So let's add the dev_pm_genpd_set_next_wakeup() API for the device to inform PM domains of the impending wakeup. This information will be the domain governor to determine the best idle state given the wakeup. Signed-off-by: Lina Iyer Reviewed-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 30 ++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 9a14eedacb92..014033c7c287 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -423,6 +423,35 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) } EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state); +/** + * dev_pm_genpd_set_next_wakeup - Notify PM framework of an impending wakeup. + * + * @dev: Device to handle + * @next: impending interrupt/wakeup for the device + * + * + * Allow devices to inform of the next wakeup. It's assumed that the users + * guarantee that the genpd wouldn't be detached while this routine is getting + * called. Additionally, it's also assumed that @dev isn't runtime suspended + * (RPM_SUSPENDED)." + * Although devices are expected to update the next_wakeup after the end of + * their usecase as well, it is possible the devices themselves may not know + * about that, so stale @next will be ignored when powering off the domain. + */ +void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next) +{ + struct generic_pm_domain_data *gpd_data; + struct generic_pm_domain *genpd; + + genpd = dev_to_genpd_safe(dev); + if (!genpd) + return; + + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + gpd_data->next_wakeup = next; +} +EXPORT_SYMBOL_GPL(dev_pm_genpd_set_next_wakeup); + static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) { unsigned int state_idx = genpd->state_idx; @@ -1465,6 +1494,7 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev) gpd_data->td.constraint_changed = true; gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; + gpd_data->next_wakeup = KTIME_MAX; spin_lock_irq(&dev->power.lock); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 2ca919ae8d36..735583c0bc6d 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -9,6 +9,7 @@ #define _LINUX_PM_DOMAIN_H #include +#include #include #include #include @@ -191,6 +192,7 @@ struct generic_pm_domain_data { struct notifier_block *power_nb; int cpu; unsigned int performance_state; + ktime_t next_wakeup; void *data; }; @@ -217,6 +219,7 @@ int pm_genpd_remove(struct generic_pm_domain *genpd); int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state); int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb); int dev_pm_genpd_remove_notifier(struct device *dev); +void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -275,6 +278,9 @@ static inline int dev_pm_genpd_remove_notifier(struct device *dev) return -EOPNOTSUPP; } +static inline void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next) +{ } + #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) #endif From c79aa080fb0f60a0e24c87014dc9c2f373e1379b Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Wed, 20 Jan 2021 08:50:42 -0700 Subject: [PATCH 26/89] PM: domains: use device's next wakeup to determine domain idle state Currently, a PM domain's idle state is determined based on whether the QoS requirements are met. However, even entering an idle state may waste power if the minimum residency requirements aren't fulfilled. CPU PM domains use the next timer wakeup for the CPUs in the domain to determine the sleep duration of the domain. This is compared with the idle state residencies to determine the optimal idle state. For other PM domains, determining the sleep length is not that straight forward. But if the device's next_event is available, we can use that to determine the sleep duration of the PM domain. Let's update the domain governor logic to check for idle state residency based on the next wakeup of devices as well as QoS constraints. But since, not all domains may contain devices capable of specifying the next wakeup, let's enable this additional check only if specified by the domain's flags when initializing the domain. Signed-off-by: Lina Iyer Reviewed-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain_governor.c | 102 ++++++++++++++++++++++++--- include/linux/pm_domain.h | 6 ++ 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 490ed7deb99a..c6c218758f0b 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -117,6 +117,55 @@ static bool default_suspend_ok(struct device *dev) return td->cached_suspend_ok; } +static void update_domain_next_wakeup(struct generic_pm_domain *genpd, ktime_t now) +{ + ktime_t domain_wakeup = KTIME_MAX; + ktime_t next_wakeup; + struct pm_domain_data *pdd; + struct gpd_link *link; + + if (!(genpd->flags & GENPD_FLAG_MIN_RESIDENCY)) + return; + + /* + * Devices that have a predictable wakeup pattern, may specify + * their next wakeup. Let's find the next wakeup from all the + * devices attached to this domain and from all the sub-domains. + * It is possible that component's a next wakeup may have become + * stale when we read that here. We will ignore to ensure the domain + * is able to enter its optimal idle state. + */ + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + next_wakeup = to_gpd_data(pdd)->next_wakeup; + if (next_wakeup != KTIME_MAX && !ktime_before(next_wakeup, now)) + if (ktime_before(next_wakeup, domain_wakeup)) + domain_wakeup = next_wakeup; + } + + list_for_each_entry(link, &genpd->parent_links, parent_node) { + next_wakeup = link->child->next_wakeup; + if (next_wakeup != KTIME_MAX && !ktime_before(next_wakeup, now)) + if (ktime_before(next_wakeup, domain_wakeup)) + domain_wakeup = next_wakeup; + } + + genpd->next_wakeup = domain_wakeup; +} + +static bool next_wakeup_allows_state(struct generic_pm_domain *genpd, + unsigned int state, ktime_t now) +{ + ktime_t domain_wakeup = genpd->next_wakeup; + s64 idle_time_ns, min_sleep_ns; + + min_sleep_ns = genpd->states[state].power_off_latency_ns + + genpd->states[state].residency_ns; + + idle_time_ns = ktime_to_ns(ktime_sub(domain_wakeup, now)); + + return idle_time_ns >= min_sleep_ns; +} + static bool __default_power_down_ok(struct dev_pm_domain *pd, unsigned int state) { @@ -201,16 +250,41 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, } /** - * default_power_down_ok - Default generic PM domain power off governor routine. + * _default_power_down_ok - Default generic PM domain power off governor routine. * @pd: PM domain to check. * * This routine must be executed under the PM domain's lock. */ -static bool default_power_down_ok(struct dev_pm_domain *pd) +static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now) { struct generic_pm_domain *genpd = pd_to_genpd(pd); + int state_idx = genpd->state_count - 1; struct gpd_link *link; + /* + * Find the next wakeup from devices that can determine their own wakeup + * to find when the domain would wakeup and do it for every device down + * the hierarchy. It is not worth while to sleep if the state's residency + * cannot be met. + */ + update_domain_next_wakeup(genpd, now); + if ((genpd->flags & GENPD_FLAG_MIN_RESIDENCY) && (genpd->next_wakeup != KTIME_MAX)) { + /* Let's find out the deepest domain idle state, the devices prefer */ + while (state_idx >= 0) { + if (next_wakeup_allows_state(genpd, state_idx, now)) { + genpd->max_off_time_changed = true; + break; + } + state_idx--; + } + + if (state_idx < 0) { + state_idx = 0; + genpd->cached_power_down_ok = false; + goto done; + } + } + if (!genpd->max_off_time_changed) { genpd->state_idx = genpd->cached_power_down_state_idx; return genpd->cached_power_down_ok; @@ -228,21 +302,30 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) genpd->max_off_time_ns = -1; genpd->max_off_time_changed = false; genpd->cached_power_down_ok = true; - genpd->state_idx = genpd->state_count - 1; - /* Find a state to power down to, starting from the deepest. */ - while (!__default_power_down_ok(pd, genpd->state_idx)) { - if (genpd->state_idx == 0) { + /* + * Find a state to power down to, starting from the state + * determined by the next wakeup. + */ + while (!__default_power_down_ok(pd, state_idx)) { + if (state_idx == 0) { genpd->cached_power_down_ok = false; break; } - genpd->state_idx--; + state_idx--; } +done: + genpd->state_idx = state_idx; genpd->cached_power_down_state_idx = genpd->state_idx; return genpd->cached_power_down_ok; } +static bool default_power_down_ok(struct dev_pm_domain *pd) +{ + return _default_power_down_ok(pd, ktime_get()); +} + static bool always_on_power_down_ok(struct dev_pm_domain *domain) { return false; @@ -254,11 +337,12 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd) struct generic_pm_domain *genpd = pd_to_genpd(pd); struct cpuidle_device *dev; ktime_t domain_wakeup, next_hrtimer; + ktime_t now = ktime_get(); s64 idle_duration_ns; int cpu, i; /* Validate dev PM QoS constraints. */ - if (!default_power_down_ok(pd)) + if (!_default_power_down_ok(pd, now)) return false; if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN)) @@ -280,7 +364,7 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd) } /* The minimum idle duration is from now - until the next wakeup. */ - idle_duration_ns = ktime_to_ns(ktime_sub(domain_wakeup, ktime_get())); + idle_duration_ns = ktime_to_ns(ktime_sub(domain_wakeup, now)); if (idle_duration_ns <= 0) return false; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 735583c0bc6d..dfcfbcecc34b 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -56,6 +56,10 @@ * * GENPD_FLAG_RPM_ALWAYS_ON: Instructs genpd to always keep the PM domain * powered on except for system suspend. + * + * GENPD_FLAG_MIN_RESIDENCY: Enable the genpd governor to consider its + * components' next wakeup when determining the + * optimal idle state. */ #define GENPD_FLAG_PM_CLK (1U << 0) #define GENPD_FLAG_IRQ_SAFE (1U << 1) @@ -63,6 +67,7 @@ #define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3) #define GENPD_FLAG_CPU_DOMAIN (1U << 4) #define GENPD_FLAG_RPM_ALWAYS_ON (1U << 5) +#define GENPD_FLAG_MIN_RESIDENCY (1U << 6) enum gpd_status { GENPD_STATE_ON = 0, /* PM domain is on */ @@ -130,6 +135,7 @@ struct generic_pm_domain { unsigned int state); struct gpd_dev_ops dev_ops; s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ + ktime_t next_wakeup; /* Maintained by the domain governor */ bool max_off_time_changed; bool cached_power_down_ok; bool cached_power_down_state_idx; From 079c42a0ed73500f1d11b5564e31d56c52bee21e Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 21 Jan 2021 00:12:30 +0300 Subject: [PATCH 27/89] PM: domains: Make set_performance_state() callback optional Make set_performance_state() callback optional in order to remove the need from power domain drivers to implement a dummy callback. If callback isn't implemented by a GENPD driver, then the performance state is passed to the parent domain. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar [tested on NVIDIA Tegra20/30/124 SoCs] Suggested-by: Ulf Hansson Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Reviewed-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 014033c7c287..4878c824e66c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -297,6 +297,18 @@ static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd, return state; } +static int genpd_xlate_performance_state(struct generic_pm_domain *genpd, + struct generic_pm_domain *parent, + unsigned int pstate) +{ + if (!parent->set_performance_state) + return pstate; + + return dev_pm_opp_xlate_performance_state(genpd->opp_table, + parent->opp_table, + pstate); +} + static int _genpd_set_performance_state(struct generic_pm_domain *genpd, unsigned int state, int depth) { @@ -311,13 +323,8 @@ static int _genpd_set_performance_state(struct generic_pm_domain *genpd, list_for_each_entry(link, &genpd->child_links, child_node) { parent = link->parent; - if (!parent->set_performance_state) - continue; - /* Find parent's performance state */ - ret = dev_pm_opp_xlate_performance_state(genpd->opp_table, - parent->opp_table, - state); + ret = genpd_xlate_performance_state(genpd, parent, state); if (unlikely(ret < 0)) goto err; @@ -339,9 +346,11 @@ static int _genpd_set_performance_state(struct generic_pm_domain *genpd, goto err; } - ret = genpd->set_performance_state(genpd, state); - if (ret) - goto err; + if (genpd->set_performance_state) { + ret = genpd->set_performance_state(genpd, state); + if (ret) + goto err; + } genpd->performance_state = state; return 0; @@ -352,9 +361,6 @@ err: child_node) { parent = link->parent; - if (!parent->set_performance_state) - continue; - genpd_lock_nested(parent, depth + 1); parent_state = link->prev_performance_state; @@ -399,9 +405,6 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) if (!genpd) return -ENODEV; - if (unlikely(!genpd->set_performance_state)) - return -EINVAL; - if (WARN_ON(!dev->power.subsys_data || !dev->power.subsys_data->domain_data)) return -EINVAL; From 18027d6f392ee8d89d9df4dff0a7db4fb2d6f8a5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 21 Jan 2021 00:12:31 +0300 Subject: [PATCH 28/89] PM: domains: Make of_genpd_add_subdomain() return -EPROBE_DEFER Driver of a power domain provider may not be ready at the time of of_genpd_add_subdomain() invocation. Make this function to return -EPROBE_DEFER instead of -ENOENT in order to remove a need from power domain drivers to handle the error code specially. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar [tested on NVIDIA Tegra20/30/124 SoCs] Suggested-by: Ulf Hansson Reviewed-by: Ulf Hansson Reviewed-by: Viresh Kumar Signed-off-by: Dmitry Osipenko Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 4878c824e66c..c615abf56c52 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2496,7 +2496,7 @@ int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, out: mutex_unlock(&gpd_list_lock); - return ret; + return ret == -ENOENT ? -EPROBE_DEFER : ret; } EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); From 45fbc464b047b3fbd760c9cb460a50a1ef2cf933 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 21 Jan 2021 00:12:32 +0300 Subject: [PATCH 29/89] PM: domains: Add "performance" column to debug summary Add "performance" column to debug summary which shows performance state of all power domains and theirs devices. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar [tested on NVIDIA Tegra20/30/124 SoCs] Reviewed-by: Ulf Hansson Reviewed-by: Viresh Kumar Signed-off-by: Dmitry Osipenko Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index c615abf56c52..50211a402fa5 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2985,7 +2985,15 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev) else WARN_ON(1); - seq_puts(s, p); + seq_printf(s, "%-25s ", p); +} + +static void perf_status_str(struct seq_file *s, struct device *dev) +{ + struct generic_pm_domain_data *gpd_data; + + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + seq_put_decimal_ull(s, "", gpd_data->performance_state); } static int genpd_summary_one(struct seq_file *s, @@ -3013,7 +3021,7 @@ static int genpd_summary_one(struct seq_file *s, else snprintf(state, sizeof(state), "%s", status_lookup[genpd->status]); - seq_printf(s, "%-30s %-15s ", genpd->name, state); + seq_printf(s, "%-30s %-50s %u", genpd->name, state, genpd->performance_state); /* * Modifications on the list require holding locks on both @@ -3021,6 +3029,8 @@ static int genpd_summary_one(struct seq_file *s, * Also genpd->name is immutable. */ list_for_each_entry(link, &genpd->parent_links, parent_node) { + if (list_is_first(&link->parent_node, &genpd->parent_links)) + seq_printf(s, "\n%48s", " "); seq_printf(s, "%s", link->child->name); if (!list_is_last(&link->parent_node, &genpd->parent_links)) seq_puts(s, ", "); @@ -3035,6 +3045,7 @@ static int genpd_summary_one(struct seq_file *s, seq_printf(s, "\n %-50s ", kobj_path); rtpm_status_str(s, pm_data->dev); + perf_status_str(s, pm_data->dev); kfree(kobj_path); } @@ -3050,9 +3061,9 @@ static int summary_show(struct seq_file *s, void *data) struct generic_pm_domain *genpd; int ret = 0; - seq_puts(s, "domain status children\n"); + seq_puts(s, "domain status children performance\n"); seq_puts(s, " /device runtime status\n"); - seq_puts(s, "----------------------------------------------------------------------\n"); + seq_puts(s, "----------------------------------------------------------------------------------------------\n"); ret = mutex_lock_interruptible(&gpd_list_lock); if (ret) From 629d512d682de2259179046e2364f1f1ff4232e3 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 25 Jan 2021 11:34:36 -0600 Subject: [PATCH 30/89] cpupower: Update msr_pstate union struct naming The msr_pstate union struct named fam17h_bits is misleading since this is the struct to use for all families >= 0x17, not just for family 0x17. Rename the bits structs to be 'pstate' (for pre family 17h CPUs) and 'pstatedef' (for CPUs since fam 17h) to align closer with PPR/BDKG (1) naming. There are no functional changes as part of this update. 1: AMD Processor Programming Reference (PPR) and BIOS and Kernel Developer's Guide (BKDG) available at: http://developer.amd.com/resources/developer-guides-manuals Signed-off-by: Nathan Fontenot Reviewed-by: Robert Richter Reviewed-by: skhan@linuxfoundation.org Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 26 +++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 7c4f83a8c973..34368436bbd6 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -13,7 +13,8 @@ #define MSR_AMD_PSTATE 0xc0010064 #define MSR_AMD_PSTATE_LIMIT 0xc0010061 -union msr_pstate { +union core_pstate { + /* pre fam 17h: */ struct { unsigned fid:6; unsigned did:3; @@ -26,7 +27,8 @@ union msr_pstate { unsigned idddiv:2; unsigned res3:21; unsigned en:1; - } bits; + } pstate; + /* since fam 17h: */ struct { unsigned fid:8; unsigned did:6; @@ -35,36 +37,36 @@ union msr_pstate { unsigned idddiv:2; unsigned res1:31; unsigned en:1; - } fam17h_bits; + } pstatedef; unsigned long long val; }; -static int get_did(int family, union msr_pstate pstate) +static int get_did(int family, union core_pstate pstate) { int t; if (family == 0x12) t = pstate.val & 0xf; else if (family == 0x17 || family == 0x18) - t = pstate.fam17h_bits.did; + t = pstate.pstatedef.did; else - t = pstate.bits.did; + t = pstate.pstate.did; return t; } -static int get_cof(int family, union msr_pstate pstate) +static int get_cof(int family, union core_pstate pstate) { int t; int fid, did, cof; did = get_did(family, pstate); if (family == 0x17 || family == 0x18) { - fid = pstate.fam17h_bits.fid; + fid = pstate.pstatedef.fid; cof = 200 * fid / did; } else { t = 0x10; - fid = pstate.bits.fid; + fid = pstate.pstate.fid; if (family == 0x11) t = 0x8; cof = (100 * (fid + t)) >> did; @@ -89,7 +91,7 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, int boost_states, unsigned long *pstates, int *no) { int i, psmax, pscur; - union msr_pstate pstate; + union core_pstate pstate; unsigned long long val; /* Only read out frequencies from HW when CPU might be boostable @@ -119,9 +121,9 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, } if (read_msr(cpu, MSR_AMD_PSTATE + i, &pstate.val)) return -1; - if ((cpu_family == 0x17) && (!pstate.fam17h_bits.en)) + if ((cpu_family == 0x17) && (!pstate.pstatedef.en)) continue; - else if (!pstate.bits.en) + else if (!pstate.pstate.en) continue; pstates[i] = get_cof(cpu_family, pstate); From 7a136a8fcd7ef14c63d07667e81c4dcac77e0a13 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Mon, 25 Jan 2021 11:34:42 -0600 Subject: [PATCH 31/89] cpupower: Correct macro name for CPB caps flag The name is Core Performance Boost (CPB) for the cpuid flag. Correct cpuid caps flag to use this name (instead of CBP). Signed-off-by: Robert Richter Signed-off-by: Nathan Fontenot Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/cpuid.c | 2 +- tools/power/cpupower/utils/helpers/helpers.h | 2 +- tools/power/cpupower/utils/helpers/misc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index 73bfafc60e9b..f9a66a430b72 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -130,7 +130,7 @@ out: cpu_info->vendor == X86_VENDOR_HYGON) { if (ext_cpuid_level >= 0x80000007 && (cpuid_edx(0x80000007) & (1 << 9))) - cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; + cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; if (ext_cpuid_level >= 0x80000008 && cpuid_ebx(0x80000008) & (1 << 4)) diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 0642e60a6ce1..a84f85a9dbd2 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -64,7 +64,7 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, #define CPUPOWER_CAP_INV_TSC 0x00000001 #define CPUPOWER_CAP_APERF 0x00000002 -#define CPUPOWER_CAP_AMD_CBP 0x00000004 +#define CPUPOWER_CAP_AMD_CPB 0x00000004 #define CPUPOWER_CAP_PERF_BIAS 0x00000008 #define CPUPOWER_CAP_HAS_TURBO_RATIO 0x00000010 #define CPUPOWER_CAP_IS_SNB 0x00000020 diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index 650b9a9a6584..f9bcce9c72d5 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -26,7 +26,7 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, if (ret) return ret; - if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CBP) { + if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CPB) { *support = 1; /* AMD Family 0x17 does not utilize PCI D18F4 like prior From a0255a76bf3a78d322adfe4eb4e73eb83998f61a Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 25 Jan 2021 11:34:49 -0600 Subject: [PATCH 32/89] cpupower: Add CPUPOWER_CAP_AMD_HW_PSTATE cpuid caps flag Add a check in get_cpu_info() for the ability to read frequencies from hardware and set the CPUPOWER_CAP_AMD_HW_PSTATE cpuid flag. The cpuid flag is set when CPUID_80000007_EDX[7] is set, which is all families >= 10h. The check excludes family 14h because HW pstate reporting was not implemented on family 14h. This is intended to reduce family checks in the main code paths. Signed-off-by: Nathan Fontenot Reviewed-by: Robert Richter Reviewed-by: skhan@linuxfoundation.org Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 9 ++++----- tools/power/cpupower/utils/helpers/cpuid.c | 12 +++++++++--- tools/power/cpupower/utils/helpers/helpers.h | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 34368436bbd6..8b69c7ff639a 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -94,11 +94,10 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, union core_pstate pstate; unsigned long long val; - /* Only read out frequencies from HW when CPU might be boostable - to keep the code as short and clean as possible. - Otherwise frequencies are exported via ACPI tables. - */ - if (cpu_family < 0x10 || cpu_family == 0x14) + /* Only read out frequencies from HW if HW Pstate is supported, + * otherwise frequencies are exported via ACPI tables. + */ + if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_HW_PSTATE)) return -1; if (read_msr(cpu, MSR_AMD_PSTATE_LIMIT, &val)) diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index f9a66a430b72..d577220a193b 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -128,9 +128,15 @@ out: /* AMD or Hygon Boost state enable/disable register */ if (cpu_info->vendor == X86_VENDOR_AMD || cpu_info->vendor == X86_VENDOR_HYGON) { - if (ext_cpuid_level >= 0x80000007 && - (cpuid_edx(0x80000007) & (1 << 9))) - cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; + if (ext_cpuid_level >= 0x80000007) { + if (cpuid_edx(0x80000007) & (1 << 9)) + cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; + + if ((cpuid_edx(0x80000007) & (1 << 7)) && + cpu_info->family != 0x14) + /* HW pstate was not implemented in family 0x14 */ + cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE; + } if (ext_cpuid_level >= 0x80000008 && cpuid_ebx(0x80000008) & (1 << 4)) diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index a84f85a9dbd2..5f61eefff5b2 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -70,6 +70,7 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, #define CPUPOWER_CAP_IS_SNB 0x00000020 #define CPUPOWER_CAP_INTEL_IDA 0x00000040 #define CPUPOWER_CAP_AMD_RDPRU 0x00000080 +#define CPUPOWER_CAP_AMD_HW_PSTATE 0x00000100 #define CPUPOWER_AMD_CPBDIS 0x02000000 From 1421de7919cd082bad692626937f055f367586ba Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 25 Jan 2021 11:35:10 -0600 Subject: [PATCH 33/89] cpupower: Remove unused pscur variable. The pscur variable is set but not uused, just remove it. This may have previsously been set to validate the MSR_AMD_PSTATE_STATUS MSR. With the addition of the CPUPOWER_CAP_AMD_HW_PSTATE cap flag this is no longer needed since the cpuid bit to enable this cap flag also validates that the MSR_AMD_PSTATE_STATUS MSR is present. Signed-off-by: Nathan Fontenot Reviewed-by: Robert Richter Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 8b69c7ff639a..fc2ac1e6bfb2 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -90,7 +90,7 @@ static int get_cof(int family, union core_pstate pstate) int decode_pstates(unsigned int cpu, unsigned int cpu_family, int boost_states, unsigned long *pstates, int *no) { - int i, psmax, pscur; + int i, psmax; union core_pstate pstate; unsigned long long val; @@ -104,13 +104,6 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, return -1; psmax = (val >> 4) & 0x7; - - if (read_msr(cpu, MSR_AMD_PSTATE_STATUS, &val)) - return -1; - - pscur = val & 0x7; - - pscur += boost_states; psmax += boost_states; for (i = 0; i <= psmax; i++) { if (i >= MAX_HW_PSTATES) { From 23765b82a808da416b70b41d711468e723531e6a Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 25 Jan 2021 11:35:17 -0600 Subject: [PATCH 34/89] cpupower: Update family checks when decoding HW pstates The family checks in get_cof() and get_did() need to use the correct MSR format depending on the family. Add a cpupower capability for using the pstatedef (family 17h and newer) to control this instead of direct family checks. Signed-off-by: Nathan Fontenot Reviewed-by: Robert Richter Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 8 ++++---- tools/power/cpupower/utils/helpers/cpuid.c | 6 +++++- tools/power/cpupower/utils/helpers/helpers.h | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index fc2ac1e6bfb2..b4731daa6820 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -45,10 +45,10 @@ static int get_did(int family, union core_pstate pstate) { int t; - if (family == 0x12) - t = pstate.val & 0xf; - else if (family == 0x17 || family == 0x18) + if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) t = pstate.pstatedef.did; + else if (family == 0x12) + t = pstate.val & 0xf; else t = pstate.pstate.did; @@ -61,7 +61,7 @@ static int get_cof(int family, union core_pstate pstate) int fid, did, cof; did = get_did(family, pstate); - if (family == 0x17 || family == 0x18) { + if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) { fid = pstate.pstatedef.fid; cof = 200 * fid / did; } else { diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index d577220a193b..db2e88ceb67b 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -133,9 +133,13 @@ out: cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; if ((cpuid_edx(0x80000007) & (1 << 7)) && - cpu_info->family != 0x14) + cpu_info->family != 0x14) { /* HW pstate was not implemented in family 0x14 */ cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE; + + if (cpu_info->family >= 0x17) + cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF; + } } if (ext_cpuid_level >= 0x80000008 && diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 5f61eefff5b2..e4dc44ced770 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -71,6 +71,7 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, #define CPUPOWER_CAP_INTEL_IDA 0x00000040 #define CPUPOWER_CAP_AMD_RDPRU 0x00000080 #define CPUPOWER_CAP_AMD_HW_PSTATE 0x00000100 +#define CPUPOWER_CAP_AMD_PSTATEDEF 0x00000200 #define CPUPOWER_AMD_CPBDIS 0x02000000 From 56a85eebebdba62ebf6c46bd957949cc6e926aa0 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 25 Jan 2021 11:35:23 -0600 Subject: [PATCH 35/89] cpupower: Condense pstate enabled bit checks in decode_pstates() The enabled bit (bit 63) is common for all families so we can remove the multiple enabled checks based on family and have a common check for HW pstate enabled. Signed-off-by: Nathan Fontenot Reviewed-by: Robert Richter Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index b4731daa6820..216240e2b771 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -113,9 +113,9 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, } if (read_msr(cpu, MSR_AMD_PSTATE + i, &pstate.val)) return -1; - if ((cpu_family == 0x17) && (!pstate.pstatedef.en)) - continue; - else if (!pstate.pstate.en) + + /* The enabled bit (bit 63) is common for all families */ + if (!pstate.pstatedef.en) continue; pstates[i] = get_cof(cpu_family, pstate); From d1abc4e996d7784ce4d56749e4b5ca8ff23b1e0f Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 25 Jan 2021 11:36:01 -0600 Subject: [PATCH 36/89] cpupower: Remove family arg to decode_pstates() The decode_pstates() routine no longer uses the CPU family and the caleed routines (get_cof() and get_did()) can grab the family from the global cpupower_cpu_info struct. These update removes passing the family arg to all these routines. Signed-off-by: Nathan Fontenot Reviewed-by: Robert Richter Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/cpufreq-info.c | 3 +-- tools/power/cpupower/utils/helpers/amd.c | 19 +++++++++---------- tools/power/cpupower/utils/helpers/helpers.h | 9 ++++----- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 6efc0f6b1b11..f9895e31ff5a 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -186,8 +186,7 @@ static int get_boost_mode_x86(unsigned int cpu) if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD && cpupower_cpu_info.family >= 0x10) || cpupower_cpu_info.vendor == X86_VENDOR_HYGON) { - ret = decode_pstates(cpu, cpupower_cpu_info.family, b_states, - pstates, &pstate_no); + ret = decode_pstates(cpu, b_states, pstates, &pstate_no); if (ret) return ret; diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 216240e2b771..97f2c857048e 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -41,13 +41,13 @@ union core_pstate { unsigned long long val; }; -static int get_did(int family, union core_pstate pstate) +static int get_did(union core_pstate pstate) { int t; if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) t = pstate.pstatedef.did; - else if (family == 0x12) + else if (cpupower_cpu_info.family == 0x12) t = pstate.val & 0xf; else t = pstate.pstate.did; @@ -55,19 +55,19 @@ static int get_did(int family, union core_pstate pstate) return t; } -static int get_cof(int family, union core_pstate pstate) +static int get_cof(union core_pstate pstate) { int t; int fid, did, cof; - did = get_did(family, pstate); + did = get_did(pstate); if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) { fid = pstate.pstatedef.fid; cof = 200 * fid / did; } else { t = 0x10; fid = pstate.pstate.fid; - if (family == 0x11) + if (cpupower_cpu_info.family == 0x11) t = 0x8; cof = (100 * (fid + t)) >> did; } @@ -76,8 +76,7 @@ static int get_cof(int family, union core_pstate pstate) /* Needs: * cpu -> the cpu that gets evaluated - * cpu_family -> The cpu's family (0x10, 0x12,...) - * boots_states -> how much boost states the machines support + * boost_states -> how much boost states the machines support * * Fills up: * pstates -> a pointer to an array of size MAX_HW_PSTATES @@ -87,8 +86,8 @@ static int get_cof(int family, union core_pstate pstate) * * returns zero on success, -1 on failure */ -int decode_pstates(unsigned int cpu, unsigned int cpu_family, - int boost_states, unsigned long *pstates, int *no) +int decode_pstates(unsigned int cpu, int boost_states, + unsigned long *pstates, int *no) { int i, psmax; union core_pstate pstate; @@ -118,7 +117,7 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, if (!pstate.pstatedef.en) continue; - pstates[i] = get_cof(cpu_family, pstate); + pstates[i] = get_cof(pstate); } *no = i; return 0; diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index e4dc44ced770..8a0c11c6ec63 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -127,8 +127,8 @@ extern struct pci_dev *pci_slot_func_init(struct pci_access **pacc, /* AMD HW pstate decoding **************************/ -extern int decode_pstates(unsigned int cpu, unsigned int cpu_family, - int boost_states, unsigned long *pstates, int *no); +extern int decode_pstates(unsigned int cpu, int boost_states, + unsigned long *pstates, int *no); /* AMD HW pstate decoding **************************/ @@ -145,9 +145,8 @@ unsigned int cpuid_edx(unsigned int op); /* cpuid and cpuinfo helpers **************************/ /* X86 ONLY ********************************************/ #else -static inline int decode_pstates(unsigned int cpu, unsigned int cpu_family, - int boost_states, unsigned long *pstates, - int *no) +static inline int decode_pstates(unsigned int cpu, int boost_states, + unsigned long *pstates, int *no) { return -1; }; static inline int read_msr(int cpu, unsigned int idx, unsigned long long *val) From 3a3ecfdb605cc8d98988012a4f88c34b4d220c21 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 25 Jan 2021 11:36:23 -0600 Subject: [PATCH 37/89] cpupower: Add cpuid cap flag for MSR_AMD_HWCR support Remove the family check for accessing the MSR_AMD_HWCR MSR and replace it with a cpupower cap flag. This update also allows for the removal of the local cpupower_cpu_info variable in cpufreq_has_boost_support() since we no longer need it to check the family. Signed-off-by: Nathan Fontenot Reviewed-by: Robert Richter Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/cpuid.c | 6 +++++- tools/power/cpupower/utils/helpers/helpers.h | 1 + tools/power/cpupower/utils/helpers/misc.c | 7 +------ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index db2e88ceb67b..72eb43593180 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -129,9 +129,13 @@ out: if (cpu_info->vendor == X86_VENDOR_AMD || cpu_info->vendor == X86_VENDOR_HYGON) { if (ext_cpuid_level >= 0x80000007) { - if (cpuid_edx(0x80000007) & (1 << 9)) + if (cpuid_edx(0x80000007) & (1 << 9)) { cpu_info->caps |= CPUPOWER_CAP_AMD_CPB; + if (cpu_info->family >= 0x17) + cpu_info->caps |= CPUPOWER_CAP_AMD_CPB_MSR; + } + if ((cpuid_edx(0x80000007) & (1 << 7)) && cpu_info->family != 0x14) { /* HW pstate was not implemented in family 0x14 */ diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 8a0c11c6ec63..33ffacee7fcb 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -72,6 +72,7 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, #define CPUPOWER_CAP_AMD_RDPRU 0x00000080 #define CPUPOWER_CAP_AMD_HW_PSTATE 0x00000100 #define CPUPOWER_CAP_AMD_PSTATEDEF 0x00000200 +#define CPUPOWER_CAP_AMD_CPB_MSR 0x00000400 #define CPUPOWER_AMD_CPBDIS 0x02000000 diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index f9bcce9c72d5..fc6e34511721 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -16,16 +16,11 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, int *states) { - struct cpupower_cpu_info cpu_info; int ret; unsigned long long val; *support = *active = *states = 0; - ret = get_cpu_info(&cpu_info); - if (ret) - return ret; - if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CPB) { *support = 1; @@ -34,7 +29,7 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, * has Hardware determined variable increments instead. */ - if (cpu_info.family == 0x17 || cpu_info.family == 0x18) { + if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CPB_MSR) { if (!read_msr(cpu, MSR_AMD_HWCR, &val)) { if (!(val & CPUPOWER_AMD_CPBDIS)) *active = 1; From ccf7ce46ab91515a7146c00300e168efa9dc777e Mon Sep 17 00:00:00 2001 From: Zqiang Date: Mon, 25 Jan 2021 12:18:28 +0800 Subject: [PATCH 38/89] PM: sleep: No need to check PF_WQ_WORKER in thaw_kernel_threads() Because PF_KTHREAD is set for all wq worker threads, it is not necessary to check PF_WQ_WORKER in addition to it in thaw_kernel_threads(), so stop doing that. Signed-off-by: Zqiang [ rjw: Subject and changelog rewrite ] Signed-off-by: Rafael J. Wysocki --- kernel/power/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/power/process.c b/kernel/power/process.c index 45b054b7b5ec..50cc63534486 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -235,7 +235,7 @@ void thaw_kernel_threads(void) read_lock(&tasklist_lock); for_each_process_thread(g, p) { - if (p->flags & (PF_KTHREAD | PF_WQ_WORKER)) + if (p->flags & PF_KTHREAD) __thaw_task(p); } read_unlock(&tasklist_lock); From eb23d91af55bc2369fe3f0aa6997e72eb20e16fe Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 26 Jan 2021 15:29:40 -0600 Subject: [PATCH 39/89] PM: sleep: Use dev_printk() when possible Use dev_printk() when possible to make messages more consistent with other device-related messages. Signed-off-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 46793276598d..f893c3c5af07 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -16,6 +16,7 @@ */ #define pr_fmt(fmt) "PM: " fmt +#define dev_fmt pr_fmt #include #include @@ -449,8 +450,8 @@ static void pm_dev_dbg(struct device *dev, pm_message_t state, const char *info) static void pm_dev_err(struct device *dev, pm_message_t state, const char *info, int error) { - pr_err("Device %s failed to %s%s: error %d\n", - dev_name(dev), pm_verb(state.event), info, error); + dev_err(dev, "failed to %s%s: error %d\n", pm_verb(state.event), info, + error); } static void dpm_show_time(ktime_t starttime, pm_message_t state, int error, @@ -1897,8 +1898,8 @@ int dpm_prepare(pm_message_t state) error = 0; continue; } - pr_info("Device %s not prepared for power transition: code %d\n", - dev_name(dev), error); + dev_info(dev, "not prepared for power transition: code %d\n", + error); put_device(dev); break; } From 309663093c8aba02cbea83b0bc8ee9a99833c482 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 26 Jan 2021 15:26:55 -0600 Subject: [PATCH 40/89] PM: runtime: Fix typos and grammar Fix minor typos and grammatical issues. Signed-off-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- Documentation/power/runtime_pm.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/power/runtime_pm.rst b/Documentation/power/runtime_pm.rst index 0553008b6279..d9c777b18f7a 100644 --- a/Documentation/power/runtime_pm.rst +++ b/Documentation/power/runtime_pm.rst @@ -579,7 +579,7 @@ should be used. Of course, for this purpose the device's runtime PM has to be enabled earlier by calling pm_runtime_enable(). Note, if the device may execute pm_runtime calls during the probe (such as -if it is registers with a subsystem that may call back in) then the +if it is registered with a subsystem that may call back in) then the pm_runtime_get_sync() call paired with a pm_runtime_put() call will be appropriate to ensure that the device is not put back to sleep during the probe. This can happen with systems such as the network device layer. @@ -587,11 +587,11 @@ probe. This can happen with systems such as the network device layer. It may be desirable to suspend the device once ->probe() has finished. Therefore the driver core uses the asynchronous pm_request_idle() to submit a request to execute the subsystem-level idle callback for the device at that -time. A driver that makes use of the runtime autosuspend feature, may want to +time. A driver that makes use of the runtime autosuspend feature may want to update the last busy mark before returning from ->probe(). Moreover, the driver core prevents runtime PM callbacks from racing with the bus -notifier callback in __device_release_driver(), which is necessary, because the +notifier callback in __device_release_driver(), which is necessary because the notifier is used by some subsystems to carry out operations affecting the runtime PM functionality. It does so by calling pm_runtime_get_sync() before driver_sysfs_remove() and the BUS_NOTIFY_UNBIND_DRIVER notifications. This @@ -603,7 +603,7 @@ calling pm_runtime_suspend() from their ->remove() routines, the driver core executes pm_runtime_put_sync() after running the BUS_NOTIFY_UNBIND_DRIVER notifications in __device_release_driver(). This requires bus types and drivers to make their ->remove() callbacks avoid races with runtime PM directly, -but also it allows of more flexibility in the handling of devices during the +but it also allows more flexibility in the handling of devices during the removal of their drivers. Drivers in ->remove() callback should undo the runtime PM changes done @@ -693,7 +693,7 @@ that the device appears to be runtime-suspended and its state is fine, so it may be left in runtime suspend provided that all of its descendants are also left in runtime suspend. If that happens, the PM core will not execute any system suspend and resume callbacks for all of those devices, except for the -complete callback, which is then entirely responsible for handling the device +.complete() callback, which is then entirely responsible for handling the device as appropriate. This only applies to system suspend transitions that are not related to hibernation (see Documentation/driver-api/pm/devices.rst for more information). @@ -706,7 +706,7 @@ out the following operations: right before executing the subsystem-level .prepare() callback for it and pm_runtime_barrier() is called for every device right before executing the subsystem-level .suspend() callback for it. In addition to that the PM core - calls __pm_runtime_disable() with 'false' as the second argument for every + calls __pm_runtime_disable() with 'false' as the second argument for every device right before executing the subsystem-level .suspend_late() callback for it. @@ -783,7 +783,7 @@ driver/base/power/generic_ops.c: `int pm_generic_restore_noirq(struct device *dev);` - invoke the ->restore_noirq() callback provided by the device's driver -These functions are the defaults used by the PM core, if a subsystem doesn't +These functions are the defaults used by the PM core if a subsystem doesn't provide its own callbacks for ->runtime_idle(), ->runtime_suspend(), ->runtime_resume(), ->suspend(), ->suspend_noirq(), ->resume(), ->resume_noirq(), ->freeze(), ->freeze_noirq(), ->thaw(), ->thaw_noirq(), From cca26b66efc1e92c10701087aca4895530660b85 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 27 Jan 2021 13:42:27 +0800 Subject: [PATCH 41/89] powercap/intel_rapl: add support for AlderLake Mobile Add intel_rapl support for the AlderLake Mobile platform. Signed-off-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index c9e57237d778..f0799837c2dd 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -1049,6 +1049,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &rapl_defaults_core), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server), X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core), From 0bfa0820c274b019583b3454c6c889c99c24558d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 25 Jan 2021 14:29:18 -0500 Subject: [PATCH 42/89] PM: clk: make PM clock layer compatible with clocks that must sleep The clock API splits its interface into sleepable ant atomic contexts: - clk_prepare/clk_unprepare for stuff that might sleep - clk_enable_clk_disable for anything that may be done in atomic context The code handling runtime PM for clocks only calls clk_disable() on suspend requests, and clk_enable on resume requests. This means that runtime PM with clock providers that only have the prepare/unprepare methods implemented is basically useless. Many clock implementations can't accommodate atomic contexts. This is often the case when communication with the clock happens through another subsystem like I2C or SCMI. Let's make the clock PM code useful with such clocks by safely invoking clk_prepare/clk_unprepare upon resume/suspend requests. Of course, when such clocks are registered with the PM layer then pm_runtime_irq_safe() can't be used, and neither pm_runtime_suspend() nor pm_runtime_resume() may be invoked in atomic context. For clocks that do implement the enable and disable methods then everything just works as before. A note on sparse: According to https://lwn.net/Articles/109066/ there are things that sparse can't cope with. In particular, pm_clk_op_lock() and pm_clk_op_unlock() may or may not lock/unlock psd->lock depending on some runtime condition. To work around that we tell it the lock is always untaken for the purpose of static analisys. Thanks to Naresh Kamboju for reporting issues with the initial patch. Signed-off-by: Nicolas Pitre Tested-by: Naresh Kamboju Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 223 +++++++++++++++++++++++++++------ drivers/clk/clk.c | 21 ++++ include/linux/clk.h | 24 +++- include/linux/pm.h | 2 + 4 files changed, 228 insertions(+), 42 deletions(-) diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index ced6863a16a5..84d5acb6301b 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -23,6 +23,7 @@ enum pce_status { PCE_STATUS_NONE = 0, PCE_STATUS_ACQUIRED, + PCE_STATUS_PREPARED, PCE_STATUS_ENABLED, PCE_STATUS_ERROR, }; @@ -32,8 +33,112 @@ struct pm_clock_entry { char *con_id; struct clk *clk; enum pce_status status; + bool enabled_when_prepared; }; +/** + * pm_clk_list_lock - ensure exclusive access for modifying the PM clock + * entry list. + * @psd: pm_subsys_data instance corresponding to the PM clock entry list + * and clk_op_might_sleep count to be modified. + * + * Get exclusive access before modifying the PM clock entry list and the + * clock_op_might_sleep count to guard against concurrent modifications. + * This also protects against a concurrent clock_op_might_sleep and PM clock + * entry list usage in pm_clk_suspend()/pm_clk_resume() that may or may not + * happen in atomic context, hence both the mutex and the spinlock must be + * taken here. + */ +static void pm_clk_list_lock(struct pm_subsys_data *psd) + __acquires(&psd->lock) +{ + mutex_lock(&psd->clock_mutex); + spin_lock_irq(&psd->lock); +} + +/** + * pm_clk_list_unlock - counterpart to pm_clk_list_lock(). + * @psd: the same pm_subsys_data instance previously passed to + * pm_clk_list_lock(). + */ +static void pm_clk_list_unlock(struct pm_subsys_data *psd) + __releases(&psd->lock) +{ + spin_unlock_irq(&psd->lock); + mutex_unlock(&psd->clock_mutex); +} + +/** + * pm_clk_op_lock - ensure exclusive access for performing clock operations. + * @psd: pm_subsys_data instance corresponding to the PM clock entry list + * and clk_op_might_sleep count being used. + * @flags: stored irq flags. + * @fn: string for the caller function's name. + * + * This is used by pm_clk_suspend() and pm_clk_resume() to guard + * against concurrent modifications to the clock entry list and the + * clock_op_might_sleep count. If clock_op_might_sleep is != 0 then + * only the mutex can be locked and those functions can only be used in + * non atomic context. If clock_op_might_sleep == 0 then these functions + * may be used in any context and only the spinlock can be locked. + * Returns -EINVAL if called in atomic context when clock ops might sleep. + */ +static int pm_clk_op_lock(struct pm_subsys_data *psd, unsigned long *flags, + const char *fn) + /* sparse annotations don't work here as exit state isn't static */ +{ + bool atomic_context = in_atomic() || irqs_disabled(); + +try_again: + spin_lock_irqsave(&psd->lock, *flags); + if (!psd->clock_op_might_sleep) { + /* the __release is there to work around sparse limitations */ + __release(&psd->lock); + return 0; + } + + /* bail out if in atomic context */ + if (atomic_context) { + pr_err("%s: atomic context with clock_ops_might_sleep = %d", + fn, psd->clock_op_might_sleep); + spin_unlock_irqrestore(&psd->lock, *flags); + might_sleep(); + return -EPERM; + } + + /* we must switch to the mutex */ + spin_unlock_irqrestore(&psd->lock, *flags); + mutex_lock(&psd->clock_mutex); + + /* + * There was a possibility for psd->clock_op_might_sleep + * to become 0 above. Keep the mutex only if not the case. + */ + if (likely(psd->clock_op_might_sleep)) + return 0; + + mutex_unlock(&psd->clock_mutex); + goto try_again; +} + +/** + * pm_clk_op_unlock - counterpart to pm_clk_op_lock(). + * @psd: the same pm_subsys_data instance previously passed to + * pm_clk_op_lock(). + * @flags: irq flags provided by pm_clk_op_lock(). + */ +static void pm_clk_op_unlock(struct pm_subsys_data *psd, unsigned long *flags) + /* sparse annotations don't work here as entry state isn't static */ +{ + if (psd->clock_op_might_sleep) { + mutex_unlock(&psd->clock_mutex); + } else { + /* the __acquire is there to work around sparse limitations */ + __acquire(&psd->lock); + spin_unlock_irqrestore(&psd->lock, *flags); + } +} + /** * pm_clk_enable - Enable a clock, reporting any errors * @dev: The device for the given clock @@ -43,14 +148,21 @@ static inline void __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce { int ret; - if (ce->status < PCE_STATUS_ERROR) { + switch (ce->status) { + case PCE_STATUS_ACQUIRED: + ret = clk_prepare_enable(ce->clk); + break; + case PCE_STATUS_PREPARED: ret = clk_enable(ce->clk); - if (!ret) - ce->status = PCE_STATUS_ENABLED; - else - dev_err(dev, "%s: failed to enable clk %p, error %d\n", - __func__, ce->clk, ret); + break; + default: + return; } + if (!ret) + ce->status = PCE_STATUS_ENABLED; + else + dev_err(dev, "%s: failed to enable clk %p, error %d\n", + __func__, ce->clk, ret); } /** @@ -64,17 +176,20 @@ static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) ce->clk = clk_get(dev, ce->con_id); if (IS_ERR(ce->clk)) { ce->status = PCE_STATUS_ERROR; + return; + } else if (clk_is_enabled_when_prepared(ce->clk)) { + /* we defer preparing the clock in that case */ + ce->status = PCE_STATUS_ACQUIRED; + ce->enabled_when_prepared = true; + } else if (clk_prepare(ce->clk)) { + ce->status = PCE_STATUS_ERROR; + dev_err(dev, "clk_prepare() failed\n"); + return; } else { - if (clk_prepare(ce->clk)) { - ce->status = PCE_STATUS_ERROR; - dev_err(dev, "clk_prepare() failed\n"); - } else { - ce->status = PCE_STATUS_ACQUIRED; - dev_dbg(dev, - "Clock %pC con_id %s managed by runtime PM.\n", - ce->clk, ce->con_id); - } + ce->status = PCE_STATUS_PREPARED; } + dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n", + ce->clk, ce->con_id); } static int __pm_clk_add(struct device *dev, const char *con_id, @@ -106,9 +221,11 @@ static int __pm_clk_add(struct device *dev, const char *con_id, pm_clk_acquire(dev, ce); - spin_lock_irq(&psd->lock); + pm_clk_list_lock(psd); list_add_tail(&ce->node, &psd->clock_list); - spin_unlock_irq(&psd->lock); + if (ce->enabled_when_prepared) + psd->clock_op_might_sleep++; + pm_clk_list_unlock(psd); return 0; } @@ -239,14 +356,20 @@ static void __pm_clk_remove(struct pm_clock_entry *ce) if (!ce) return; - if (ce->status < PCE_STATUS_ERROR) { - if (ce->status == PCE_STATUS_ENABLED) - clk_disable(ce->clk); - - if (ce->status >= PCE_STATUS_ACQUIRED) { - clk_unprepare(ce->clk); + switch (ce->status) { + case PCE_STATUS_ENABLED: + clk_disable(ce->clk); + fallthrough; + case PCE_STATUS_PREPARED: + clk_unprepare(ce->clk); + fallthrough; + case PCE_STATUS_ACQUIRED: + case PCE_STATUS_ERROR: + if (!IS_ERR(ce->clk)) clk_put(ce->clk); - } + break; + default: + break; } kfree(ce->con_id); @@ -269,7 +392,7 @@ void pm_clk_remove(struct device *dev, const char *con_id) if (!psd) return; - spin_lock_irq(&psd->lock); + pm_clk_list_lock(psd); list_for_each_entry(ce, &psd->clock_list, node) { if (!con_id && !ce->con_id) @@ -280,12 +403,14 @@ void pm_clk_remove(struct device *dev, const char *con_id) goto remove; } - spin_unlock_irq(&psd->lock); + pm_clk_list_unlock(psd); return; remove: list_del(&ce->node); - spin_unlock_irq(&psd->lock); + if (ce->enabled_when_prepared) + psd->clock_op_might_sleep--; + pm_clk_list_unlock(psd); __pm_clk_remove(ce); } @@ -307,19 +432,21 @@ void pm_clk_remove_clk(struct device *dev, struct clk *clk) if (!psd || !clk) return; - spin_lock_irq(&psd->lock); + pm_clk_list_lock(psd); list_for_each_entry(ce, &psd->clock_list, node) { if (clk == ce->clk) goto remove; } - spin_unlock_irq(&psd->lock); + pm_clk_list_unlock(psd); return; remove: list_del(&ce->node); - spin_unlock_irq(&psd->lock); + if (ce->enabled_when_prepared) + psd->clock_op_might_sleep--; + pm_clk_list_unlock(psd); __pm_clk_remove(ce); } @@ -330,13 +457,16 @@ EXPORT_SYMBOL_GPL(pm_clk_remove_clk); * @dev: Device to initialize the list of PM clocks for. * * Initialize the lock and clock_list members of the device's pm_subsys_data - * object. + * object, set the count of clocks that might sleep to 0. */ void pm_clk_init(struct device *dev) { struct pm_subsys_data *psd = dev_to_psd(dev); - if (psd) + if (psd) { INIT_LIST_HEAD(&psd->clock_list); + mutex_init(&psd->clock_mutex); + psd->clock_op_might_sleep = 0; + } } EXPORT_SYMBOL_GPL(pm_clk_init); @@ -372,12 +502,13 @@ void pm_clk_destroy(struct device *dev) INIT_LIST_HEAD(&list); - spin_lock_irq(&psd->lock); + pm_clk_list_lock(psd); list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node) list_move(&ce->node, &list); + psd->clock_op_might_sleep = 0; - spin_unlock_irq(&psd->lock); + pm_clk_list_unlock(psd); dev_pm_put_subsys_data(dev); @@ -397,23 +528,30 @@ int pm_clk_suspend(struct device *dev) struct pm_subsys_data *psd = dev_to_psd(dev); struct pm_clock_entry *ce; unsigned long flags; + int ret; dev_dbg(dev, "%s()\n", __func__); if (!psd) return 0; - spin_lock_irqsave(&psd->lock, flags); + ret = pm_clk_op_lock(psd, &flags, __func__); + if (ret) + return ret; list_for_each_entry_reverse(ce, &psd->clock_list, node) { - if (ce->status < PCE_STATUS_ERROR) { - if (ce->status == PCE_STATUS_ENABLED) + if (ce->status == PCE_STATUS_ENABLED) { + if (ce->enabled_when_prepared) { + clk_disable_unprepare(ce->clk); + ce->status = PCE_STATUS_ACQUIRED; + } else { clk_disable(ce->clk); - ce->status = PCE_STATUS_ACQUIRED; + ce->status = PCE_STATUS_PREPARED; + } } } - spin_unlock_irqrestore(&psd->lock, flags); + pm_clk_op_unlock(psd, &flags); return 0; } @@ -428,18 +566,21 @@ int pm_clk_resume(struct device *dev) struct pm_subsys_data *psd = dev_to_psd(dev); struct pm_clock_entry *ce; unsigned long flags; + int ret; dev_dbg(dev, "%s()\n", __func__); if (!psd) return 0; - spin_lock_irqsave(&psd->lock, flags); + ret = pm_clk_op_lock(psd, &flags, __func__); + if (ret) + return ret; list_for_each_entry(ce, &psd->clock_list, node) __pm_clk_enable(dev, ce); - spin_unlock_irqrestore(&psd->lock, flags); + pm_clk_op_unlock(psd, &flags); return 0; } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 8c1d04db990d..3d751ae5bc70 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1164,6 +1164,27 @@ int clk_enable(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_enable); +/** + * clk_is_enabled_when_prepared - indicate if preparing a clock also enables it. + * @clk: clock source + * + * Returns true if clk_prepare() implicitly enables the clock, effectively + * making clk_enable()/clk_disable() no-ops, false otherwise. + * + * This is of interest mainly to power management code where actually + * disabling the clock also requires unpreparing it to have any material + * effect. + * + * Regardless of the value returned here, the caller must always invoke + * clk_enable() or clk_prepare_enable() and counterparts for usage counts + * to be right. + */ +bool clk_is_enabled_when_prepared(struct clk *clk) +{ + return clk && !(clk->core->ops->enable && clk->core->ops->disable); +} +EXPORT_SYMBOL_GPL(clk_is_enabled_when_prepared); + static int clk_core_prepare_enable(struct clk_core *core) { int ret; diff --git a/include/linux/clk.h b/include/linux/clk.h index 31ff1bf1b79f..a4a86aa8b11a 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -238,6 +238,7 @@ static inline bool clk_is_match(const struct clk *p, const struct clk *q) #endif +#ifdef CONFIG_HAVE_CLK_PREPARE /** * clk_prepare - prepare a clock source * @clk: clock source @@ -246,10 +247,26 @@ static inline bool clk_is_match(const struct clk *p, const struct clk *q) * * Must not be called from within atomic context. */ -#ifdef CONFIG_HAVE_CLK_PREPARE int clk_prepare(struct clk *clk); int __must_check clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks); + +/** + * clk_is_enabled_when_prepared - indicate if preparing a clock also enables it. + * @clk: clock source + * + * Returns true if clk_prepare() implicitly enables the clock, effectively + * making clk_enable()/clk_disable() no-ops, false otherwise. + * + * This is of interest mainly to the power management code where actually + * disabling the clock also requires unpreparing it to have any material + * effect. + * + * Regardless of the value returned here, the caller must always invoke + * clk_enable() or clk_prepare_enable() and counterparts for usage counts + * to be right. + */ +bool clk_is_enabled_when_prepared(struct clk *clk); #else static inline int clk_prepare(struct clk *clk) { @@ -263,6 +280,11 @@ clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks) might_sleep(); return 0; } + +static inline bool clk_is_enabled_when_prepared(struct clk *clk) +{ + return false; +} #endif /** diff --git a/include/linux/pm.h b/include/linux/pm.h index 47aca6bac1d6..482313a8ccfc 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -537,6 +537,8 @@ struct pm_subsys_data { spinlock_t lock; unsigned int refcount; #ifdef CONFIG_PM_CLK + unsigned int clock_op_might_sleep; + struct mutex clock_mutex; struct list_head clock_list; #endif #ifdef CONFIG_PM_GENERIC_DOMAINS From 6dc466d34f51767ad34fb900de8d278a66a3f1ed Mon Sep 17 00:00:00 2001 From: Abaci Team Date: Wed, 27 Jan 2021 16:42:05 +0800 Subject: [PATCH 43/89] PM: domains: Simplify the calculation of variables Fix the following coccicheck warnings: ./drivers/base/power/domain.c:938:31-33: WARNING !A || A && B is equivalent to !A || B. Reported-by: Abaci Robot Suggested-by: Jiapeng Zhong Signed-off-by: Abaci Team Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 50211a402fa5..aaf6c83b5cf6 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -966,8 +966,7 @@ static int genpd_runtime_resume(struct device *dev) err_stop: genpd_stop_dev(genpd, dev); err_poweroff: - if (!pm_runtime_is_irq_safe(dev) || - (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) { + if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) { genpd_lock(genpd); genpd_power_off(genpd, true, 0); genpd_unlock(genpd); From 32715be4fe95fc98762959f8dff6f9f8a39df28f Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:13 +0300 Subject: [PATCH 44/89] opp: Fix adding OPP entries in a wrong order if rate is unavailable Fix adding OPP entries in a wrong (opposite) order if OPP rate is unavailable. The OPP comparison was erroneously skipped, thus OPPs were left unsorted. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 8c905aabacc0..5793c833b86a 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1527,12 +1527,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, mutex_lock(&opp_table->lock); head = &opp_table->opp_list; - if (likely(!rate_not_available)) { - ret = _opp_is_duplicate(dev, new_opp, opp_table, &head); - if (ret) { - mutex_unlock(&opp_table->lock); - return ret; - } + ret = _opp_is_duplicate(dev, new_opp, opp_table, &head); + if (ret) { + mutex_unlock(&opp_table->lock); + return ret; } list_add(&new_opp->node, head); From cf65948d62c6aefd22f51c1433743f80517ee3fe Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:14 +0300 Subject: [PATCH 45/89] opp: Filter out OPPs based on availability of a required-OPP A required OPP may not be available, and thus, all OPPs which are using this required OPP should be unavailable too. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 5793c833b86a..253bc87b5695 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1522,6 +1522,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available) { struct list_head *head; + unsigned int i; int ret; mutex_lock(&opp_table->lock); @@ -1547,6 +1548,16 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, __func__, new_opp->rate); } + for (i = 0; i < opp_table->required_opp_count; i++) { + if (new_opp->required_opps[i]->available) + continue; + + new_opp->available = false; + dev_warn(dev, "%s: OPP not supported by required OPP %pOF (%lu)\n", + __func__, new_opp->required_opps[i]->np, new_opp->rate); + break; + } + return 0; } From d7b9d9b31a3e55dcc9b5c289abfafe31efa5b5c4 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:15 +0300 Subject: [PATCH 46/89] opp: Correct debug message in _opp_add_static_v2() The debug message always prints rate=0 instead of a proper value, fix it. Fixes: 6c591eec67cb ("OPP: Add helpers for reading the binding properties") Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko [ Viresh: Added Fixes tag ] Signed-off-by: Viresh Kumar --- drivers/opp/of.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 03cb387236c4..d0c0336be39b 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -755,7 +755,6 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, struct device *dev, struct device_node *np) { struct dev_pm_opp *new_opp; - u64 rate = 0; u32 val; int ret; bool rate_not_available = false; @@ -772,7 +771,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, /* Check if the OPP supports hardware's hierarchy of versions or not */ if (!_opp_is_supported(dev, opp_table, np)) { - dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); + dev_dbg(dev, "OPP not supported by hardware: %lu\n", + new_opp->rate); goto free_opp; } From d758eaf5f8cbdf2554e34269c75694f60c38745d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Jan 2021 11:08:47 +0530 Subject: [PATCH 47/89] opp: Staticize _add_opp_table() _add_opp_table() isn't used outside of core.c, mark it static. Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 2 +- drivers/opp/opp.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 253bc87b5695..dc7a298f3611 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1193,7 +1193,7 @@ unlock: return opp_table; } -struct opp_table *_add_opp_table(struct device *dev) +static struct opp_table *_add_opp_table(struct device *dev) { return _add_opp_table_indexed(dev, 0); } diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 4ced7ffa8158..ee2593afae0c 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -223,7 +223,6 @@ int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2); int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available); int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); -struct opp_table *_add_opp_table(struct device *dev); struct opp_table *_add_opp_table_indexed(struct device *dev, int index); void _put_opp_list_kref(struct opp_table *opp_table); From 8dd5cada393f6f4e825833a6ff05b1f51f36a791 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:18 +0300 Subject: [PATCH 48/89] opp: Add dev_pm_opp_find_level_ceil() Add a ceil version of the dev_pm_opp_find_level(). It's handy to have if levels don't start from 0 in OPP table and zero usually means a minimal level. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 49 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 8 +++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index dc7a298f3611..b29f31146770 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -449,6 +449,55 @@ struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact); +/** + * dev_pm_opp_find_level_ceil() - search for an rounded up level + * @dev: device for which we do this operation + * @level: level to search for + * + * Return: Searches for rounded up match in the opp table and returns pointer + * to the matching opp if found, else returns ERR_PTR in case of error and + * should be handled using IS_ERR. Error return values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, + unsigned int *level) +{ + struct opp_table *opp_table; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + int r = PTR_ERR(opp_table); + + dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); + return ERR_PTR(r); + } + + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { + if (temp_opp->available && temp_opp->level >= *level) { + opp = temp_opp; + *level = opp->level; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); + break; + } + } + + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_ceil); + static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 1435c054016a..2b3030cb2ed2 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -111,6 +111,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, bool available); struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level); +struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, + unsigned int *level); struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq); @@ -226,6 +228,12 @@ static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, return ERR_PTR(-ENOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, + unsigned int *level) +{ + return ERR_PTR(-ENOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) { From 597ff5431fd41afa888809f7936508a15c977cde Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:19 +0300 Subject: [PATCH 49/89] opp: Add dev_pm_opp_get_required_pstate() Add dev_pm_opp_get_required_pstate() which allows OPP users to retrieve required performance state of a given OPP. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 22 ++++++++++++++++++++++ include/linux/pm_opp.h | 10 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index b29f31146770..64a95aaa90eb 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -145,6 +145,28 @@ unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_level); +/** + * dev_pm_opp_get_required_pstate() - Gets the required performance state + * corresponding to an available opp + * @opp: opp for which performance state has to be returned for + * @index: index of the required opp + * + * Return: performance state read from device tree corresponding to the + * required opp, else return 0. + */ +unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, + unsigned int index) +{ + if (IS_ERR_OR_NULL(opp) || !opp->available || + index >= opp->opp_table->required_opp_count) { + pr_err("%s: Invalid parameters\n", __func__); + return 0; + } + + return opp->required_opps[index]->pstate; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_required_pstate); + /** * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not * @opp: opp for which turbo mode is being verified diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 2b3030cb2ed2..8f926815bad9 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -98,6 +98,9 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp); +unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, + unsigned int index); + bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); int dev_pm_opp_get_opp_count(struct device *dev); @@ -186,6 +189,13 @@ static inline unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) return 0; } +static inline +unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, + unsigned int index) +{ + return 0; +} + static inline bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) { return false; From ce8073d83f63a2cdcfc1b86d769456726faad51d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 21 Jan 2021 01:26:47 +0300 Subject: [PATCH 50/89] opp: Add dev_pm_opp_sync_regulators() Extend OPP API with dev_pm_opp_sync_regulators() function, which syncs voltage state of regulators. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko [ Viresh: Added unlikely() ] Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 47 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 64a95aaa90eb..bf7cdab0ba64 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2584,3 +2584,44 @@ void dev_pm_opp_remove_table(struct device *dev) dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_remove_table); + +/** + * dev_pm_opp_sync_regulators() - Sync state of voltage regulators + * @dev: device for which we do this operation + * + * Sync voltage state of the OPP table regulators. + * + * Return: 0 on success or a negative error value. + */ +int dev_pm_opp_sync_regulators(struct device *dev) +{ + struct opp_table *opp_table; + struct regulator *reg; + int i, ret = 0; + + /* Device may not have OPP table */ + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return 0; + + /* Regulator may not be required for the device */ + if (unlikely(!opp_table->regulators)) + goto put_table; + + /* Nothing to sync if voltage wasn't changed */ + if (!opp_table->enabled) + goto put_table; + + for (i = 0; i < opp_table->regulator_count; i++) { + reg = opp_table->regulators[i]; + ret = regulator_sync_voltage(reg); + if (ret) + break; + } +put_table: + /* Drop reference taken by _find_opp_table() */ + dev_pm_opp_put_opp_table(opp_table); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_sync_regulators); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 8f926815bad9..979b208bc4a8 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -161,6 +161,7 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cp int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); void dev_pm_opp_remove_table(struct device *dev); void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask); +int dev_pm_opp_sync_regulators(struct device *dev); #else static inline struct opp_table *dev_pm_opp_get_opp_table(struct device *dev) { @@ -384,6 +385,11 @@ static inline void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask { } +static inline int dev_pm_opp_sync_regulators(struct device *dev) +{ + return -ENOTSUPP; +} + #endif /* CONFIG_PM_OPP */ #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) From 406e47652161d4f0d9bc4cd6237b36c51497ec75 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 27 Jan 2021 12:45:56 +0530 Subject: [PATCH 51/89] opp: Create _of_add_table_indexed() to reduce code duplication The implementation of dev_pm_opp_of_add_table() and dev_pm_opp_of_add_table_indexed() are almost identical. Create _of_add_table_indexed() to reduce code redundancy. Also remove the duplication of the doc style comments by referring to dev_pm_opp_of_add_table() from dev_pm_opp_of_add_table_indexed(). Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/of.c | 111 ++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 63 deletions(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index d0c0336be39b..c6856dcf4c34 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -956,68 +956,7 @@ remove_static_opp: return ret; } -/** - * dev_pm_opp_of_add_table() - Initialize opp table from device tree - * @dev: device pointer used to lookup OPP table. - * - * Register the initial OPP table with the OPP library for given device. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -ENODEV when 'operating-points' property is not found or is invalid data - * in device node. - * -ENODATA when empty 'operating-points' property is found - * -EINVAL when invalid entries are found in opp-v2 table - */ -int dev_pm_opp_of_add_table(struct device *dev) -{ - struct opp_table *opp_table; - int ret; - - opp_table = _add_opp_table_indexed(dev, 0); - if (IS_ERR(opp_table)) - return PTR_ERR(opp_table); - - /* - * OPPs have two version of bindings now. Also try the old (v1) - * bindings for backward compatibility with older dtbs. - */ - if (opp_table->np) - ret = _of_add_opp_table_v2(dev, opp_table); - else - ret = _of_add_opp_table_v1(dev, opp_table); - - if (ret) - dev_pm_opp_put_opp_table(opp_table); - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); - -/** - * dev_pm_opp_of_add_table_indexed() - Initialize indexed opp table from device tree - * @dev: device pointer used to lookup OPP table. - * @index: Index number. - * - * Register the initial OPP table with the OPP library for given device only - * using the "operating-points-v2" property. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -ENODEV when 'operating-points' property is not found or is invalid data - * in device node. - * -ENODATA when empty 'operating-points' property is found - * -EINVAL when invalid entries are found in opp-v2 table - */ -int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) +static int _of_add_table_indexed(struct device *dev, int index) { struct opp_table *opp_table; int ret, count; @@ -1037,12 +976,58 @@ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) if (IS_ERR(opp_table)) return PTR_ERR(opp_table); - ret = _of_add_opp_table_v2(dev, opp_table); + /* + * OPPs have two version of bindings now. Also try the old (v1) + * bindings for backward compatibility with older dtbs. + */ + if (opp_table->np) + ret = _of_add_opp_table_v2(dev, opp_table); + else + ret = _of_add_opp_table_v1(dev, opp_table); + if (ret) dev_pm_opp_put_opp_table(opp_table); return ret; } + +/** + * dev_pm_opp_of_add_table() - Initialize opp table from device tree + * @dev: device pointer used to lookup OPP table. + * + * Register the initial OPP table with the OPP library for given device. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -ENODEV when 'operating-points' property is not found or is invalid data + * in device node. + * -ENODATA when empty 'operating-points' property is found + * -EINVAL when invalid entries are found in opp-v2 table + */ +int dev_pm_opp_of_add_table(struct device *dev) +{ + return _of_add_table_indexed(dev, 0); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); + +/** + * dev_pm_opp_of_add_table_indexed() - Initialize indexed opp table from device tree + * @dev: device pointer used to lookup OPP table. + * @index: Index number. + * + * Register the initial OPP table with the OPP library for given device only + * using the "operating-points-v2" property. + * + * Return: Refer to dev_pm_opp_of_add_table() for return values. + */ +int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) +{ + return _of_add_table_indexed(dev, index); +} EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed); /* CPU device specific helpers */ From 32439ac7535a8eddfa016c62ca66ce33b7df1573 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Jan 2021 12:05:22 +0530 Subject: [PATCH 52/89] opp: Defer acquiring the clk until OPPs are added We acquire the clk at the time the OPP table is allocated, though it works fine, it is not the best place to do so. One of the main reason being we may need to acquire it again from dev_pm_opp_set_clkname() if the platform wants another clock to be acquired instead. There is also requirement from some of the platforms where they do not want the OPP core to manage the clock at all. This patch hence defers acquiring the clk until the time we are certain about which clk we need to acquire and if we really need to acquire one. With this commit, the clk will get acquired either from dev_pm_opp_set_clkname() or while we initialize the OPPs within the table. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 73 ++++++++++++++++++++++++++++------------------ drivers/opp/of.c | 8 ++--- drivers/opp/opp.h | 2 +- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index bf7cdab0ba64..52f4a64926e6 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1158,21 +1158,11 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) _of_init_opp_table(opp_table, dev, index); - /* Find clk for the device */ - opp_table->clk = clk_get(dev, NULL); - if (IS_ERR(opp_table->clk)) { - ret = PTR_ERR(opp_table->clk); - if (ret == -EPROBE_DEFER) - goto remove_opp_dev; - - dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret); - } - /* Find interconnect path(s) for the device */ ret = dev_pm_opp_of_find_icc_paths(dev, opp_table); if (ret) { if (ret == -EPROBE_DEFER) - goto put_clk; + goto remove_opp_dev; dev_warn(dev, "%s: Error finding interconnect paths: %d\n", __func__, ret); @@ -1184,9 +1174,6 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) return opp_table; -put_clk: - if (!IS_ERR(opp_table->clk)) - clk_put(opp_table->clk); remove_opp_dev: _remove_opp_dev(opp_dev, opp_table); err: @@ -1199,6 +1186,33 @@ void _get_opp_table_kref(struct opp_table *opp_table) kref_get(&opp_table->kref); } +static struct opp_table *_update_opp_table_clk(struct device *dev, + struct opp_table *opp_table, + bool getclk) +{ + /* + * Return early if we don't need to get clk or we have already tried it + * earlier. + */ + if (!getclk || IS_ERR(opp_table) || opp_table->clk) + return opp_table; + + /* Find clk for the device */ + opp_table->clk = clk_get(dev, NULL); + if (IS_ERR(opp_table->clk)) { + int ret = PTR_ERR(opp_table->clk); + + if (ret == -EPROBE_DEFER) { + dev_pm_opp_put_opp_table(opp_table); + return ERR_PTR(ret); + } + + dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret); + } + + return opp_table; +} + /* * We need to make sure that the OPP table for a device doesn't get added twice, * if this routine gets called in parallel with the same device pointer. @@ -1214,7 +1228,8 @@ void _get_opp_table_kref(struct opp_table *opp_table) * uses the opp_tables_busy flag to indicate if another creator is in the middle * of adding an OPP table and others should wait for it to finish. */ -struct opp_table *_add_opp_table_indexed(struct device *dev, int index) +struct opp_table *_add_opp_table_indexed(struct device *dev, int index, + bool getclk) { struct opp_table *opp_table; @@ -1261,12 +1276,12 @@ again: unlock: mutex_unlock(&opp_table_lock); - return opp_table; + return _update_opp_table_clk(dev, opp_table, getclk); } -static struct opp_table *_add_opp_table(struct device *dev) +static struct opp_table *_add_opp_table(struct device *dev, bool getclk) { - return _add_opp_table_indexed(dev, 0); + return _add_opp_table_indexed(dev, 0, getclk); } struct opp_table *dev_pm_opp_get_opp_table(struct device *dev) @@ -1711,7 +1726,7 @@ struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, { struct opp_table *opp_table; - opp_table = _add_opp_table(dev); + opp_table = _add_opp_table(dev, false); if (IS_ERR(opp_table)) return opp_table; @@ -1773,7 +1788,7 @@ struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) { struct opp_table *opp_table; - opp_table = _add_opp_table(dev); + opp_table = _add_opp_table(dev, false); if (IS_ERR(opp_table)) return opp_table; @@ -1869,7 +1884,7 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, struct regulator *reg; int ret, i; - opp_table = _add_opp_table(dev); + opp_table = _add_opp_table(dev, false); if (IS_ERR(opp_table)) return opp_table; @@ -1980,7 +1995,7 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) struct opp_table *opp_table; int ret; - opp_table = _add_opp_table(dev); + opp_table = _add_opp_table(dev, false); if (IS_ERR(opp_table)) return opp_table; @@ -1990,9 +2005,11 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) goto err; } - /* Already have default clk set, free it */ - if (!IS_ERR(opp_table->clk)) - clk_put(opp_table->clk); + /* clk shouldn't be initialized at this point */ + if (WARN_ON(opp_table->clk)) { + ret = -EBUSY; + goto err; + } /* Find clk for the device */ opp_table->clk = clk_get(dev, name); @@ -2051,7 +2068,7 @@ struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, if (!set_opp) return ERR_PTR(-EINVAL); - opp_table = _add_opp_table(dev); + opp_table = _add_opp_table(dev, false); if (IS_ERR(opp_table)) return opp_table; @@ -2138,7 +2155,7 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, int index = 0, ret = -EINVAL; const char **name = names; - opp_table = _add_opp_table(dev); + opp_table = _add_opp_table(dev, false); if (IS_ERR(opp_table)) return opp_table; @@ -2306,7 +2323,7 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) struct opp_table *opp_table; int ret; - opp_table = _add_opp_table(dev); + opp_table = _add_opp_table(dev, true); if (IS_ERR(opp_table)) return PTR_ERR(opp_table); diff --git a/drivers/opp/of.c b/drivers/opp/of.c index c6856dcf4c34..d4b51b2e384f 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -956,7 +956,7 @@ remove_static_opp: return ret; } -static int _of_add_table_indexed(struct device *dev, int index) +static int _of_add_table_indexed(struct device *dev, int index, bool getclk) { struct opp_table *opp_table; int ret, count; @@ -972,7 +972,7 @@ static int _of_add_table_indexed(struct device *dev, int index) index = 0; } - opp_table = _add_opp_table_indexed(dev, index); + opp_table = _add_opp_table_indexed(dev, index, getclk); if (IS_ERR(opp_table)) return PTR_ERR(opp_table); @@ -1010,7 +1010,7 @@ static int _of_add_table_indexed(struct device *dev, int index) */ int dev_pm_opp_of_add_table(struct device *dev) { - return _of_add_table_indexed(dev, 0); + return _of_add_table_indexed(dev, 0, true); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); @@ -1026,7 +1026,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); */ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) { - return _of_add_table_indexed(dev, index); + return _of_add_table_indexed(dev, index, true); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed); diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index ee2593afae0c..6e83855ade1f 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -223,7 +223,7 @@ int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2); int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available); int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); -struct opp_table *_add_opp_table_indexed(struct device *dev, int index); +struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk); void _put_opp_list_kref(struct opp_table *opp_table); #ifdef CONFIG_OF From 559fef0dfd91145b59b7c61061504f344ecf9ad8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 27 Jan 2021 14:23:45 +0530 Subject: [PATCH 53/89] opp: Add dev_pm_opp_of_add_table_noclk() A few drivers have device's clk but they don't want the OPP core to handle that. Add a new helper for them, dev_pm_opp_of_add_table_noclk(). Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/of.c | 18 ++++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index d4b51b2e384f..a905497c75f8 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -1030,6 +1030,24 @@ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) } EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed); +/** + * dev_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device + * tree without getting clk for device. + * @dev: device pointer used to lookup OPP table. + * @index: Index number. + * + * Register the initial OPP table with the OPP library for given device only + * using the "operating-points-v2" property. Do not try to get the clk for the + * device. + * + * Return: Refer to dev_pm_opp_of_add_table() for return values. + */ +int dev_pm_opp_of_add_table_noclk(struct device *dev, int index) +{ + return _of_add_table_indexed(dev, index, false); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_noclk); + /* CPU device specific helpers */ /** diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 979b208bc4a8..158158620dde 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -395,6 +395,7 @@ static inline int dev_pm_opp_sync_regulators(struct device *dev) #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) int dev_pm_opp_of_add_table(struct device *dev); int dev_pm_opp_of_add_table_indexed(struct device *dev, int index); +int dev_pm_opp_of_add_table_noclk(struct device *dev, int index); void dev_pm_opp_of_remove_table(struct device *dev); int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask); void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask); @@ -419,6 +420,11 @@ static inline int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) return -ENOTSUPP; } +static inline int dev_pm_opp_of_add_table_noclk(struct device *dev, int index) +{ + return -ENOTSUPP; +} + static inline void dev_pm_opp_of_remove_table(struct device *dev) { } From a3c47af6942dc8e07a4328913d0263a965786895 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:20 +0300 Subject: [PATCH 54/89] opp: Add devm_pm_opp_register_set_opp_helper Add resource-managed version of dev_pm_opp_register_set_opp_helper(). Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko [ Viresh: Manually apply the patch and relocate the routines ] Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 8 ++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 52f4a64926e6..09069a564896 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2106,6 +2106,40 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); +static void devm_pm_opp_unregister_set_opp_helper(void *data) +{ + dev_pm_opp_unregister_set_opp_helper(data); +} + +/** + * devm_pm_opp_register_set_opp_helper() - Register custom set OPP helper + * @dev: Device for which the helper is getting registered. + * @set_opp: Custom set OPP helper. + * + * This is a resource-managed version of dev_pm_opp_register_set_opp_helper(). + * + * Return: pointer to 'struct opp_table' on success and errorno otherwise. + */ +struct opp_table * +devm_pm_opp_register_set_opp_helper(struct device *dev, + int (*set_opp)(struct dev_pm_set_opp_data *data)) +{ + struct opp_table *opp_table; + int err; + + opp_table = dev_pm_opp_register_set_opp_helper(dev, set_opp); + if (IS_ERR(opp_table)) + return opp_table; + + err = devm_add_action_or_reset(dev, devm_pm_opp_unregister_set_opp_helper, + opp_table); + if (err) + return ERR_PTR(err); + + return opp_table; +} +EXPORT_SYMBOL_GPL(devm_pm_opp_register_set_opp_helper); + static void _opp_detach_genpd(struct opp_table *opp_table) { int index; diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 158158620dde..473daf34160d 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -152,6 +152,7 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); void dev_pm_opp_put_clkname(struct opp_table *opp_table); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); +struct opp_table *devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); void dev_pm_opp_detach_genpd(struct opp_table *opp_table); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); @@ -324,6 +325,13 @@ static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {} +static inline struct opp_table * +devm_pm_opp_register_set_opp_helper(struct device *dev, + int (*set_opp)(struct dev_pm_set_opp_data *data)) +{ + return ERR_PTR(-ENOTSUPP); +} + static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) { return ERR_PTR(-ENOTSUPP); From b4b9e223eccaeec6e05d927c292d4425fd18f243 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:21 +0300 Subject: [PATCH 55/89] opp: Add devm_pm_opp_attach_genpd Add resource-managed version of dev_pm_opp_attach_genpd(). Signed-off-by: Dmitry Osipenko [ Viresh: Manually apply the patch and relocate the routines ] Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 7 +++++++ 2 files changed, 43 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 09069a564896..ce0ec5bde22a 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2275,6 +2275,42 @@ void dev_pm_opp_detach_genpd(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd); +static void devm_pm_opp_detach_genpd(void *data) +{ + dev_pm_opp_detach_genpd(data); +} + +/** + * devm_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual + * device pointer + * @dev: Consumer device for which the genpd is getting attached. + * @names: Null terminated array of pointers containing names of genpd to attach. + * @virt_devs: Pointer to return the array of virtual devices. + * + * This is a resource-managed version of dev_pm_opp_attach_genpd(). + * + * Return: pointer to 'struct opp_table' on success and errorno otherwise. + */ +struct opp_table * +devm_pm_opp_attach_genpd(struct device *dev, const char **names, + struct device ***virt_devs) +{ + struct opp_table *opp_table; + int err; + + opp_table = dev_pm_opp_attach_genpd(dev, names, virt_devs); + if (IS_ERR(opp_table)) + return opp_table; + + err = devm_add_action_or_reset(dev, devm_pm_opp_detach_genpd, + opp_table); + if (err) + return ERR_PTR(err); + + return opp_table; +} +EXPORT_SYMBOL_GPL(devm_pm_opp_attach_genpd); + /** * dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table. * @src_table: OPP table which has dst_table as one of its required OPP table. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 473daf34160d..a2c871799603 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -155,6 +155,7 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); struct opp_table *devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); void dev_pm_opp_detach_genpd(struct opp_table *opp_table); +struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp); @@ -360,6 +361,12 @@ static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, cons static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {} +static inline struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, + const char **names, struct device ***virt_devs) +{ + return ERR_PTR(-ENOTSUPP); +} + static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate) { return -ENOTSUPP; From f2f4d2b86f432fecfd76afa5f4f60f47833121b5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:23 +0300 Subject: [PATCH 56/89] opp: Handle missing OPP table in dev_pm_opp_xlate_performance_state() NVIDIA Tegra SoCs have a power domains topology such that child domains only clamp a power rail, while parent domain controls shared performance state of the multiple child domains. In this case child's domain doesn't need to have OPP table. Hence we want to allow children power domains to pass performance state to the parent domain if child's domain doesn't have OPP table. The dev_pm_opp_xlate_performance_state() gets src_table=NULL if a child power domain doesn't have OPP table and in this case we should pass the performance state to the parent domain. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index ce0ec5bde22a..0417cd34b805 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2339,7 +2339,7 @@ int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, * and so none of them have the "required-opps" property set. Return the * pstate of the src_table as it is in such cases. */ - if (!src_table->required_opp_count) + if (!src_table || !src_table->required_opp_count) return pstate; for (i = 0; i < src_table->required_opp_count; i++) { From b6ecd5d4f6941628d0140735d3f05eb61907141e Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 18 Jan 2021 03:55:24 +0300 Subject: [PATCH 57/89] opp: Print OPP level in debug message of _opp_add_static_v2() Print OPP level in debug message of _opp_add_static_v2(). This helps to chase GENPD bugs. Tested-by: Peter Geis Tested-by: Nicolas Chauvet Tested-by: Matt Merhar Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/of.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index a905497c75f8..20ccdaab9384 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -822,10 +822,11 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max) opp_table->clock_latency_ns_max = new_opp->clock_latency_ns; - pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", + pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu level:%u\n", __func__, new_opp->turbo, new_opp->rate, new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min, - new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns); + new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns, + new_opp->level); /* * Notify the changes in the availability of the operable From 38bb34393804b79eff647bdf96762db5efce392c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 19 Jan 2021 11:58:58 +0530 Subject: [PATCH 58/89] opp: Prepare for ->set_opp() helper to work without regulators Until now the ->set_opp() helper (i.e. special implementation for setting the OPPs for platforms) was implemented only to take care of multiple regulators case, but going forward we would need that for other use cases as well. This patch prepares for that by allocating the regulator specific part from dev_pm_opp_set_regulators() and the opp helper part from dev_pm_opp_register_set_opp_helper(). Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 84 +++++++++++++++++++++++++--------------------- drivers/opp/opp.h | 2 ++ 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 0417cd34b805..f482937d72eb 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1832,38 +1832,6 @@ void dev_pm_opp_put_prop_name(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); -static int _allocate_set_opp_data(struct opp_table *opp_table) -{ - struct dev_pm_set_opp_data *data; - int len, count = opp_table->regulator_count; - - if (WARN_ON(!opp_table->regulators)) - return -EINVAL; - - /* space for set_opp_data */ - len = sizeof(*data); - - /* space for old_opp.supplies and new_opp.supplies */ - len += 2 * sizeof(struct dev_pm_opp_supply) * count; - - data = kzalloc(len, GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->old_opp.supplies = (void *)(data + 1); - data->new_opp.supplies = data->old_opp.supplies + count; - - opp_table->set_opp_data = data; - - return 0; -} - -static void _free_set_opp_data(struct opp_table *opp_table) -{ - kfree(opp_table->set_opp_data); - opp_table->set_opp_data = NULL; -} - /** * dev_pm_opp_set_regulators() - Set regulator names for the device * @dev: Device for which regulator name is being set. @@ -1880,6 +1848,7 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count) { + struct dev_pm_opp_supply *supplies; struct opp_table *opp_table; struct regulator *reg; int ret, i; @@ -1921,10 +1890,19 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, opp_table->regulator_count = count; - /* Allocate block only once to pass to set_opp() routines */ - ret = _allocate_set_opp_data(opp_table); - if (ret) + supplies = kmalloc_array(count * 2, sizeof(*supplies), GFP_KERNEL); + if (!supplies) { + ret = -ENOMEM; goto free_regulators; + } + + mutex_lock(&opp_table->lock); + opp_table->sod_supplies = supplies; + if (opp_table->set_opp_data) { + opp_table->set_opp_data->old_opp.supplies = supplies; + opp_table->set_opp_data->new_opp.supplies = supplies + count; + } + mutex_unlock(&opp_table->lock); return opp_table; @@ -1967,7 +1945,15 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) for (i = opp_table->regulator_count - 1; i >= 0; i--) regulator_put(opp_table->regulators[i]); - _free_set_opp_data(opp_table); + mutex_lock(&opp_table->lock); + if (opp_table->set_opp_data) { + opp_table->set_opp_data->old_opp.supplies = NULL; + opp_table->set_opp_data->new_opp.supplies = NULL; + } + + kfree(opp_table->sod_supplies); + opp_table->sod_supplies = NULL; + mutex_unlock(&opp_table->lock); kfree(opp_table->regulators); opp_table->regulators = NULL; @@ -2063,6 +2049,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_clkname); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)) { + struct dev_pm_set_opp_data *data; struct opp_table *opp_table; if (!set_opp) @@ -2079,8 +2066,23 @@ struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, } /* Another CPU that shares the OPP table has set the helper ? */ - if (!opp_table->set_opp) - opp_table->set_opp = set_opp; + if (opp_table->set_opp) + return opp_table; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + mutex_lock(&opp_table->lock); + opp_table->set_opp_data = data; + if (opp_table->sod_supplies) { + data->old_opp.supplies = opp_table->sod_supplies; + data->new_opp.supplies = opp_table->sod_supplies + + opp_table->regulator_count; + } + mutex_unlock(&opp_table->lock); + + opp_table->set_opp = set_opp; return opp_table; } @@ -2102,6 +2104,12 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) WARN_ON(!list_empty(&opp_table->opp_list)); opp_table->set_opp = NULL; + + mutex_lock(&opp_table->lock); + kfree(opp_table->set_opp_data); + opp_table->set_opp_data = NULL; + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 6e83855ade1f..64b9cb782a93 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -155,6 +155,7 @@ enum opp_table_access { * @genpd_performance_state: Device's power domain support performance state. * @is_genpd: Marks if the OPP table belongs to a genpd. * @set_opp: Platform specific set_opp callback + * @sod_supplies: Set opp data supplies * @set_opp_data: Data to be passed to set_opp callback * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. @@ -202,6 +203,7 @@ struct opp_table { bool is_genpd; int (*set_opp)(struct dev_pm_set_opp_data *data); + struct dev_pm_opp_supply *sod_supplies; struct dev_pm_set_opp_data *set_opp_data; #ifdef CONFIG_DEBUG_FS From 04b447df1d098dcd7d133203a310a6d415875547 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 21 Jan 2021 01:26:49 +0300 Subject: [PATCH 59/89] opp: Make _set_opp_custom() work without regulators Check whether OPP table has regulators in _set_opp_custom() and set up dev_pm_set_opp_data accordingly. Now _set_opp_custom() works properly, i.e. it doesn't crash if OPP table doesn't have assigned regulators. Signed-off-by: Dmitry Osipenko [ Viresh: Rearrange the routine a bit ] Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index f482937d72eb..b4528e40ad01 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -828,24 +828,31 @@ static int _set_opp_custom(const struct opp_table *opp_table, struct dev_pm_opp_supply *old_supply, struct dev_pm_opp_supply *new_supply) { - struct dev_pm_set_opp_data *data; + struct dev_pm_set_opp_data *data = opp_table->set_opp_data; int size; - data = opp_table->set_opp_data; + /* + * We support this only if dev_pm_opp_set_regulators() was called + * earlier. + */ + if (opp_table->sod_supplies) { + size = sizeof(*old_supply) * opp_table->regulator_count; + if (!old_supply) + memset(data->old_opp.supplies, 0, size); + else + memcpy(data->old_opp.supplies, old_supply, size); + + memcpy(data->new_opp.supplies, new_supply, size); + data->regulator_count = opp_table->regulator_count; + } else { + data->regulator_count = 0; + } + data->regulators = opp_table->regulators; - data->regulator_count = opp_table->regulator_count; data->clk = opp_table->clk; data->dev = dev; - data->old_opp.rate = old_freq; - size = sizeof(*old_supply) * opp_table->regulator_count; - if (!old_supply) - memset(data->old_opp.supplies, 0, size); - else - memcpy(data->old_opp.supplies, old_supply, size); - data->new_opp.rate = freq; - memcpy(data->new_opp.supplies, new_supply, size); return opp_table->set_opp(data); } From 5ad58bbacf802f7d11cadd76881311d6e4b2bce0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 12:08:45 +0530 Subject: [PATCH 60/89] opp: Rename _opp_set_rate_zero() This routine has nothing to do with frequency, it just disables all the resources previously enabled. Rename it to match its purpose. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index b4528e40ad01..9637f2994d2e 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -950,7 +950,7 @@ int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw); -static int _opp_set_rate_zero(struct device *dev, struct opp_table *opp_table) +static int _disable_opp_table(struct device *dev, struct opp_table *opp_table) { int ret; @@ -1004,7 +1004,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) } if (unlikely(!target_freq)) { - ret = _opp_set_rate_zero(dev, opp_table); + ret = _disable_opp_table(dev, opp_table); goto put_opp_table; } From 1d3c42cabbd351e9c171e906603b5cc2ea513640 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 Jan 2021 15:57:21 +0530 Subject: [PATCH 61/89] opp: No need to check clk for errors Clock is not optional for users who call into dev_pm_opp_set_rate(). Remove the unnecessary checks. While at it also drop the local variable for clk and use opp_table->clk instead. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 9637f2994d2e..8ef85cd918ce 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -994,7 +994,6 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) struct opp_table *opp_table; unsigned long freq, old_freq, temp_freq; struct dev_pm_opp *old_opp, *opp; - struct clk *clk; int ret; opp_table = _find_opp_table(dev); @@ -1008,19 +1007,11 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) goto put_opp_table; } - clk = opp_table->clk; - if (IS_ERR(clk)) { - dev_err(dev, "%s: No clock available for the device\n", - __func__); - ret = PTR_ERR(clk); - goto put_opp_table; - } - - freq = clk_round_rate(clk, target_freq); + freq = clk_round_rate(opp_table->clk, target_freq); if ((long)freq <= 0) freq = target_freq; - old_freq = clk_get_rate(clk); + old_freq = clk_get_rate(opp_table->clk); /* Return early if nothing to do */ if (opp_table->enabled && old_freq == freq) { @@ -1038,7 +1029,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) * equivalent to a clk_set_rate() */ if (!_get_opp_count(opp_table)) { - ret = _generic_set_opp_clk_only(dev, clk, freq); + ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); goto put_opp_table; } @@ -1078,7 +1069,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) opp->supplies); } else { /* Only frequency scaling */ - ret = _generic_set_opp_clk_only(dev, clk, freq); + ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); } /* Scaling down? Configure required OPPs after frequency */ From 81c4d8a3c41488e5491142c31cd7a821ff5d71ec Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 Jan 2021 16:16:48 +0530 Subject: [PATCH 62/89] opp: Keep track of currently programmed OPP The dev_pm_opp_set_rate() helper needs to know the currently programmed OPP to make few decisions and currently we try to find it on every invocation of this routine. Lets start keeping track of the current_opp programmed for the devices of the opp table, that will be quite useful going forward. If we fail to find the current OPP, we pick the first one available in the list, as the list is in ascending order of frequencies, level, or bandwidth and that's the best guess we can make anyway. Note that we used to do the frequency comparison a bit early in dev_pm_opp_set_rate() previously, and now instead we check the target opp, which shall be more accurate anyway. We need to make sure that current_opp's memory doesn't get freed while it is being used and so we keep a reference of it until the time it is used. Now that current_opp will always be set, we can drop some unnecessary checks as well. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 84 +++++++++++++++++++++++++++++----------------- drivers/opp/opp.h | 2 ++ 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 8ef85cd918ce..c77d8ae89836 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -788,8 +788,7 @@ restore_freq: __func__, old_freq); restore_voltage: /* This shouldn't harm even if the voltages weren't updated earlier */ - if (old_supply) - _set_opp_voltage(dev, reg, old_supply); + _set_opp_voltage(dev, reg, old_supply); return ret; } @@ -837,11 +836,7 @@ static int _set_opp_custom(const struct opp_table *opp_table, */ if (opp_table->sod_supplies) { size = sizeof(*old_supply) * opp_table->regulator_count; - if (!old_supply) - memset(data->old_opp.supplies, 0, size); - else - memcpy(data->old_opp.supplies, old_supply, size); - + memcpy(data->old_opp.supplies, old_supply, size); memcpy(data->new_opp.supplies, new_supply, size); data->regulator_count = opp_table->regulator_count; } else { @@ -950,6 +945,31 @@ int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw); +static void _find_current_opp(struct device *dev, struct opp_table *opp_table) +{ + struct dev_pm_opp *opp = ERR_PTR(-ENODEV); + unsigned long freq; + + if (!IS_ERR(opp_table->clk)) { + freq = clk_get_rate(opp_table->clk); + opp = _find_freq_ceil(opp_table, &freq); + } + + /* + * Unable to find the current OPP ? Pick the first from the list since + * it is in ascending order, otherwise rest of the code will need to + * make special checks to validate current_opp. + */ + if (IS_ERR(opp)) { + mutex_lock(&opp_table->lock); + opp = list_first_entry(&opp_table->opp_list, struct dev_pm_opp, node); + dev_pm_opp_get(opp); + mutex_unlock(&opp_table->lock); + } + + opp_table->current_opp = opp; +} + static int _disable_opp_table(struct device *dev, struct opp_table *opp_table) { int ret; @@ -1011,16 +1031,6 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) if ((long)freq <= 0) freq = target_freq; - old_freq = clk_get_rate(opp_table->clk); - - /* Return early if nothing to do */ - if (opp_table->enabled && old_freq == freq) { - dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n", - __func__, freq); - ret = 0; - goto put_opp_table; - } - /* * For IO devices which require an OPP on some platforms/SoCs * while just needing to scale the clock on some others @@ -1033,12 +1043,9 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) goto put_opp_table; } - temp_freq = old_freq; - old_opp = _find_freq_ceil(opp_table, &temp_freq); - if (IS_ERR(old_opp)) { - dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n", - __func__, old_freq, PTR_ERR(old_opp)); - } + /* Find the currently set OPP if we don't know already */ + if (unlikely(!opp_table->current_opp)) + _find_current_opp(dev, opp_table); temp_freq = freq; opp = _find_freq_ceil(opp_table, &temp_freq); @@ -1046,7 +1053,17 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) ret = PTR_ERR(opp); dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n", __func__, freq, ret); - goto put_old_opp; + goto put_opp_table; + } + + old_opp = opp_table->current_opp; + old_freq = old_opp->rate; + + /* Return early if nothing to do */ + if (opp_table->enabled && old_opp == opp) { + dev_dbg(dev, "%s: OPPs are same, nothing to do\n", __func__); + ret = 0; + goto put_opp; } dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, @@ -1061,11 +1078,10 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) if (opp_table->set_opp) { ret = _set_opp_custom(opp_table, dev, old_freq, freq, - IS_ERR(old_opp) ? NULL : old_opp->supplies, - opp->supplies); + old_opp->supplies, opp->supplies); } else if (opp_table->regulators) { ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, - IS_ERR(old_opp) ? NULL : old_opp->supplies, + old_opp->supplies, opp->supplies); } else { /* Only frequency scaling */ @@ -1081,15 +1097,18 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) if (!ret) { ret = _set_opp_bw(opp_table, opp, dev, false); - if (!ret) + if (!ret) { opp_table->enabled = true; + dev_pm_opp_put(old_opp); + + /* Make sure current_opp doesn't get freed */ + dev_pm_opp_get(opp); + opp_table->current_opp = opp; + } } put_opp: dev_pm_opp_put(opp); -put_old_opp: - if (!IS_ERR(old_opp)) - dev_pm_opp_put(old_opp); put_opp_table: dev_pm_opp_put_opp_table(opp_table); return ret; @@ -1298,6 +1317,9 @@ static void _opp_table_kref_release(struct kref *kref) list_del(&opp_table->node); mutex_unlock(&opp_table_lock); + if (opp_table->current_opp) + dev_pm_opp_put(opp_table->current_opp); + _of_clear_opp_table(opp_table); /* Release clk */ diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 64b9cb782a93..372df68e185b 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -135,6 +135,7 @@ enum opp_table_access { * @clock_latency_ns_max: Max clock latency in nanoseconds. * @parsed_static_opps: Count of devices for which OPPs are initialized from DT. * @shared_opp: OPP is shared between multiple devices. + * @current_opp: Currently configured OPP for the table. * @suspend_opp: Pointer to OPP to be used during device suspend. * @genpd_virt_dev_lock: Mutex protecting the genpd virtual device pointers. * @genpd_virt_devs: List of virtual devices for multiple genpd support. @@ -183,6 +184,7 @@ struct opp_table { unsigned int parsed_static_opps; enum opp_table_access shared_opp; + struct dev_pm_opp *current_opp; struct dev_pm_opp *suspend_opp; struct mutex genpd_virt_dev_lock; From 386ba854d9f3163aed0119b167a874169410d8bc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 12:12:09 +0530 Subject: [PATCH 63/89] opp: Split _set_opp() out of dev_pm_opp_set_rate() The _set_opp() helper will be used for devices which don't change their frequency (like power domains, etc.) later on, prepare for that by breaking the generic part out of dev_pm_opp_set_rate(). Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 126 +++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index c77d8ae89836..2c8939d18783 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -998,72 +998,27 @@ static int _disable_opp_table(struct device *dev, struct opp_table *opp_table) return ret; } -/** - * dev_pm_opp_set_rate() - Configure new OPP based on frequency - * @dev: device for which we do this operation - * @target_freq: frequency to achieve - * - * This configures the power-supplies to the levels specified by the OPP - * corresponding to the target_freq, and programs the clock to a value <= - * target_freq, as rounded by clk_round_rate(). Device wanting to run at fmax - * provided by the opp, should have already rounded to the target OPP's - * frequency. - */ -int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) +static int _set_opp(struct device *dev, struct opp_table *opp_table, + struct dev_pm_opp *opp, unsigned long freq) { - struct opp_table *opp_table; - unsigned long freq, old_freq, temp_freq; - struct dev_pm_opp *old_opp, *opp; + struct dev_pm_opp *old_opp; + unsigned long old_freq; int ret; - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "%s: device opp doesn't exist\n", __func__); - return PTR_ERR(opp_table); - } - - if (unlikely(!target_freq)) { - ret = _disable_opp_table(dev, opp_table); - goto put_opp_table; - } - - freq = clk_round_rate(opp_table->clk, target_freq); - if ((long)freq <= 0) - freq = target_freq; - - /* - * For IO devices which require an OPP on some platforms/SoCs - * while just needing to scale the clock on some others - * we look for empty OPP tables with just a clock handle and - * scale only the clk. This makes dev_pm_opp_set_rate() - * equivalent to a clk_set_rate() - */ - if (!_get_opp_count(opp_table)) { - ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); - goto put_opp_table; - } + if (unlikely(!opp)) + return _disable_opp_table(dev, opp_table); /* Find the currently set OPP if we don't know already */ if (unlikely(!opp_table->current_opp)) _find_current_opp(dev, opp_table); - temp_freq = freq; - opp = _find_freq_ceil(opp_table, &temp_freq); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n", - __func__, freq, ret); - goto put_opp_table; - } - old_opp = opp_table->current_opp; old_freq = old_opp->rate; /* Return early if nothing to do */ if (opp_table->enabled && old_opp == opp) { dev_dbg(dev, "%s: OPPs are same, nothing to do\n", __func__); - ret = 0; - goto put_opp; + return 0; } dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, @@ -1073,7 +1028,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) if (freq >= old_freq) { ret = _set_required_opps(dev, opp_table, opp, true); if (ret) - goto put_opp; + return ret; } if (opp_table->set_opp) { @@ -1107,8 +1062,69 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) } } -put_opp: - dev_pm_opp_put(opp); + return ret; +} + +/** + * dev_pm_opp_set_rate() - Configure new OPP based on frequency + * @dev: device for which we do this operation + * @target_freq: frequency to achieve + * + * This configures the power-supplies to the levels specified by the OPP + * corresponding to the target_freq, and programs the clock to a value <= + * target_freq, as rounded by clk_round_rate(). Device wanting to run at fmax + * provided by the opp, should have already rounded to the target OPP's + * frequency. + */ +int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) +{ + struct opp_table *opp_table; + unsigned long freq = 0, temp_freq; + struct dev_pm_opp *opp = NULL; + int ret; + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: device's opp table doesn't exist\n", __func__); + return PTR_ERR(opp_table); + } + + if (target_freq) { + /* + * For IO devices which require an OPP on some platforms/SoCs + * while just needing to scale the clock on some others + * we look for empty OPP tables with just a clock handle and + * scale only the clk. This makes dev_pm_opp_set_rate() + * equivalent to a clk_set_rate() + */ + if (!_get_opp_count(opp_table)) { + ret = _generic_set_opp_clk_only(dev, opp_table->clk, target_freq); + goto put_opp_table; + } + + freq = clk_round_rate(opp_table->clk, target_freq); + if ((long)freq <= 0) + freq = target_freq; + + /* + * The clock driver may support finer resolution of the + * frequencies than the OPP table, don't update the frequency we + * pass to clk_set_rate() here. + */ + temp_freq = freq; + opp = _find_freq_ceil(opp_table, &temp_freq); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n", + __func__, freq, ret); + goto put_opp_table; + } + } + + ret = _set_opp(dev, opp_table, opp, freq); + + if (target_freq) + dev_pm_opp_put(opp); put_opp_table: dev_pm_opp_put_opp_table(opp_table); return ret; From f0b88fa45595254fa51427bd8ca321732e2eb73d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 16:00:12 +0530 Subject: [PATCH 64/89] opp: Allow _set_opp() to work for non-freq devices The _set_opp() helper will be used for devices which don't change frequency (like power domains, etc.) later on, prepare for that by not relying on frequency for making decisions here. While at it, also update the debug print to contain all relevant information. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 2c8939d18783..cce1b59d7bca 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1003,7 +1003,7 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, { struct dev_pm_opp *old_opp; unsigned long old_freq; - int ret; + int scaling_down, ret; if (unlikely(!opp)) return _disable_opp_table(dev, opp_table); @@ -1021,11 +1021,17 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, return 0; } - dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, - old_freq, freq); + dev_dbg(dev, "%s: switching OPP: Freq %lu -> %lu Hz, Level %u -> %u, Bw %u -> %u\n", + __func__, old_freq, freq, old_opp->level, opp->level, + old_opp->bandwidth ? old_opp->bandwidth[0].peak : 0, + opp->bandwidth ? opp->bandwidth[0].peak : 0); + + scaling_down = _opp_compare_key(old_opp, opp); + if (scaling_down == -1) + scaling_down = 0; /* Scaling up? Configure required OPPs before frequency */ - if (freq >= old_freq) { + if (!scaling_down) { ret = _set_required_opps(dev, opp_table, opp, true); if (ret) return ret; @@ -1044,7 +1050,7 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, } /* Scaling down? Configure required OPPs after frequency */ - if (!ret && freq < old_freq) { + if (!ret && scaling_down) { ret = _set_required_opps(dev, opp_table, opp, false); if (ret) dev_err(dev, "Failed to set required opps: %d\n", ret); From 3f62670fcca4af3fe6492100a548603831ecc61d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 12:38:13 +0530 Subject: [PATCH 65/89] opp: Allow _generic_set_opp_regulator() to work for non-freq devices The _generic_set_opp_regulator() helper will be used for devices which don't change frequency (like power domains, etc.) later on, prepare for that by not relying on frequency for making decisions here. While at it, update its parameters to pass only what is necessary. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index cce1b59d7bca..c078c7dab6b2 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -737,12 +737,12 @@ static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk, static int _generic_set_opp_regulator(struct opp_table *opp_table, struct device *dev, - unsigned long old_freq, + struct dev_pm_opp *opp, unsigned long freq, - struct dev_pm_opp_supply *old_supply, - struct dev_pm_opp_supply *new_supply) + int scaling_down) { struct regulator *reg = opp_table->regulators[0]; + struct dev_pm_opp *old_opp = opp_table->current_opp; int ret; /* This function only supports single regulator per device */ @@ -752,8 +752,8 @@ static int _generic_set_opp_regulator(struct opp_table *opp_table, } /* Scaling up? Scale voltage before frequency */ - if (freq >= old_freq) { - ret = _set_opp_voltage(dev, reg, new_supply); + if (!scaling_down) { + ret = _set_opp_voltage(dev, reg, opp->supplies); if (ret) goto restore_voltage; } @@ -764,8 +764,8 @@ static int _generic_set_opp_regulator(struct opp_table *opp_table, goto restore_voltage; /* Scaling down? Scale voltage after frequency */ - if (freq < old_freq) { - ret = _set_opp_voltage(dev, reg, new_supply); + if (scaling_down) { + ret = _set_opp_voltage(dev, reg, opp->supplies); if (ret) goto restore_freq; } @@ -783,12 +783,12 @@ static int _generic_set_opp_regulator(struct opp_table *opp_table, return 0; restore_freq: - if (_generic_set_opp_clk_only(dev, opp_table->clk, old_freq)) + if (_generic_set_opp_clk_only(dev, opp_table->clk, old_opp->rate)) dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", - __func__, old_freq); + __func__, old_opp->rate); restore_voltage: /* This shouldn't harm even if the voltages weren't updated earlier */ - _set_opp_voltage(dev, reg, old_supply); + _set_opp_voltage(dev, reg, old_opp->supplies); return ret; } @@ -1041,9 +1041,8 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, ret = _set_opp_custom(opp_table, dev, old_freq, freq, old_opp->supplies, opp->supplies); } else if (opp_table->regulators) { - ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, - old_opp->supplies, - opp->supplies); + ret = _generic_set_opp_regulator(opp_table, dev, opp, freq, + scaling_down); } else { /* Only frequency scaling */ ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); From 35e74b2ee8ec64da6f8067c5b0744f16ff19915b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 12:38:13 +0530 Subject: [PATCH 66/89] opp: Allow _generic_set_opp_clk_only() to work for non-freq devices In order to avoid conditional statements at the caller site, this patch updates _generic_set_opp_clk_only() to work for devices that don't change frequency (like power domains, etc.). Return 0 if the clk pointer passed to this routine is not valid. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index c078c7dab6b2..f21ce52a5002 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -726,6 +726,10 @@ static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk, { int ret; + /* We may reach here for devices which don't change frequency */ + if (IS_ERR(clk)) + return 0; + ret = clk_set_rate(clk, freq); if (ret) { dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, From 509e4777ca41d30808deda5ae3c1e09e3f58a33f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 13:06:01 +0530 Subject: [PATCH 67/89] opp: Update parameters of _set_opp_custom() Drop the unnecessary parameters and follow the pattern from _generic_set_opp_regulator(). While at it, also remove the local variable old_freq. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index f21ce52a5002..2b5584ef0350 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -826,12 +826,11 @@ static int _set_opp_bw(const struct opp_table *opp_table, } static int _set_opp_custom(const struct opp_table *opp_table, - struct device *dev, unsigned long old_freq, - unsigned long freq, - struct dev_pm_opp_supply *old_supply, - struct dev_pm_opp_supply *new_supply) + struct device *dev, struct dev_pm_opp *opp, + unsigned long freq) { struct dev_pm_set_opp_data *data = opp_table->set_opp_data; + struct dev_pm_opp *old_opp = opp_table->current_opp; int size; /* @@ -839,9 +838,9 @@ static int _set_opp_custom(const struct opp_table *opp_table, * earlier. */ if (opp_table->sod_supplies) { - size = sizeof(*old_supply) * opp_table->regulator_count; - memcpy(data->old_opp.supplies, old_supply, size); - memcpy(data->new_opp.supplies, new_supply, size); + size = sizeof(*old_opp->supplies) * opp_table->regulator_count; + memcpy(data->old_opp.supplies, old_opp->supplies, size); + memcpy(data->new_opp.supplies, opp->supplies, size); data->regulator_count = opp_table->regulator_count; } else { data->regulator_count = 0; @@ -850,7 +849,7 @@ static int _set_opp_custom(const struct opp_table *opp_table, data->regulators = opp_table->regulators; data->clk = opp_table->clk; data->dev = dev; - data->old_opp.rate = old_freq; + data->old_opp.rate = old_opp->rate; data->new_opp.rate = freq; return opp_table->set_opp(data); @@ -1006,7 +1005,6 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, struct dev_pm_opp *opp, unsigned long freq) { struct dev_pm_opp *old_opp; - unsigned long old_freq; int scaling_down, ret; if (unlikely(!opp)) @@ -1017,7 +1015,6 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, _find_current_opp(dev, opp_table); old_opp = opp_table->current_opp; - old_freq = old_opp->rate; /* Return early if nothing to do */ if (opp_table->enabled && old_opp == opp) { @@ -1026,7 +1023,7 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, } dev_dbg(dev, "%s: switching OPP: Freq %lu -> %lu Hz, Level %u -> %u, Bw %u -> %u\n", - __func__, old_freq, freq, old_opp->level, opp->level, + __func__, old_opp->rate, freq, old_opp->level, opp->level, old_opp->bandwidth ? old_opp->bandwidth[0].peak : 0, opp->bandwidth ? opp->bandwidth[0].peak : 0); @@ -1042,8 +1039,7 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, } if (opp_table->set_opp) { - ret = _set_opp_custom(opp_table, dev, old_freq, freq, - old_opp->supplies, opp->supplies); + ret = _set_opp_custom(opp_table, dev, opp, freq); } else if (opp_table->regulators) { ret = _generic_set_opp_regulator(opp_table, dev, opp, freq, scaling_down); From abbe348340c7df9e08fd7c24491c1be31ab65370 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 12:15:36 +0530 Subject: [PATCH 68/89] opp: Implement dev_pm_opp_set_opp() The new helper dev_pm_opp_set_opp() can be used for configuring the devices for a particular OPP and can be used by different type of devices, even the ones which don't change frequency (like power domains). Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 28 ++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 2b5584ef0350..fac84d5a1d45 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1136,6 +1136,34 @@ put_opp_table: } EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); +/** + * dev_pm_opp_set_opp() - Configure device for OPP + * @dev: device for which we do this operation + * @opp: OPP to set to + * + * This configures the device based on the properties of the OPP passed to this + * routine. + * + * Return: 0 on success, a negative error number otherwise. + */ +int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp) +{ + struct opp_table *opp_table; + int ret; + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: device opp doesn't exist\n", __func__); + return PTR_ERR(opp_table); + } + + ret = _set_opp(dev, opp_table, opp, opp ? opp->rate : 0); + dev_pm_opp_put_opp_table(opp_table); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_opp); + /* OPP-dev Helpers */ static void _remove_opp_dev(struct opp_device *opp_dev, struct opp_table *opp_table) diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index a2c871799603..7b1d47ab3fb3 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -158,6 +158,7 @@ void dev_pm_opp_detach_genpd(struct opp_table *opp_table); struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); +int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp); int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); @@ -377,6 +378,11 @@ static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_f return -ENOTSUPP; } +static inline int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp) +{ + return -ENOTSUPP; +} + static inline int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) { return -EOPNOTSUPP; From 8d25157f738c413b40b82776b0d260cd23505266 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 15:27:55 +0530 Subject: [PATCH 69/89] cpufreq: qcom: Migrate to dev_pm_opp_set_opp() dev_pm_opp_set_bw() is getting removed and dev_pm_opp_set_opp() should be used instead. Migrate to the new API. Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/cpufreq/qcom-cpufreq-hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 9ed5341dc515..7df18903b66c 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -54,7 +54,7 @@ static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, if (IS_ERR(opp)) return PTR_ERR(opp); - ret = dev_pm_opp_set_bw(dev, opp); + ret = dev_pm_opp_set_opp(dev, opp); dev_pm_opp_put(opp); return ret; } From 920b4a678099dd7429f03cb00649c5455f21cc67 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 15:27:55 +0530 Subject: [PATCH 70/89] drm: msm: Migrate to dev_pm_opp_set_opp() dev_pm_opp_set_bw() is getting removed and dev_pm_opp_set_opp() should be used instead. Migrate to the new API. Signed-off-by: Viresh Kumar --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index e6703ae98760..05e0ef58fe32 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -134,7 +134,7 @@ void a6xx_gmu_set_freq(struct msm_gpu *gpu, struct dev_pm_opp *opp) if (!gmu->legacy) { a6xx_hfi_set_freq(gmu, perf_index); - dev_pm_opp_set_bw(&gpu->pdev->dev, opp); + dev_pm_opp_set_opp(&gpu->pdev->dev, opp); pm_runtime_put(gmu->dev); return; } @@ -158,7 +158,7 @@ void a6xx_gmu_set_freq(struct msm_gpu *gpu, struct dev_pm_opp *opp) if (ret) dev_err(gmu->dev, "GMU set GPU frequency error: %d\n", ret); - dev_pm_opp_set_bw(&gpu->pdev->dev, opp); + dev_pm_opp_set_opp(&gpu->pdev->dev, opp); pm_runtime_put(gmu->dev); } @@ -866,7 +866,7 @@ static void a6xx_gmu_set_initial_bw(struct msm_gpu *gpu, struct a6xx_gmu *gmu) if (IS_ERR_OR_NULL(gpu_opp)) return; - dev_pm_opp_set_bw(&gpu->pdev->dev, gpu_opp); + dev_pm_opp_set_opp(&gpu->pdev->dev, gpu_opp); dev_pm_opp_put(gpu_opp); } @@ -1072,7 +1072,7 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) a6xx_gmu_shutdown(gmu); /* Remove the bus vote */ - dev_pm_opp_set_bw(&gpu->pdev->dev, NULL); + dev_pm_opp_set_opp(&gpu->pdev->dev, NULL); /* * Make sure the GX domain is off before turning off the GMU (CX) From c7f142190d91a7e8b3df0a6ef9fabb591fb83c71 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 15:27:55 +0530 Subject: [PATCH 71/89] devfreq: tegra30: Migrate to dev_pm_opp_set_opp() dev_pm_opp_set_bw() is getting removed and dev_pm_opp_set_opp() should be used instead. Migrate to the new API. We don't want the OPP core to manage the clk for this driver, migrate to dev_pm_opp_of_add_table_noclk() to make sure dev_pm_opp_set_opp() doesn't have any side effects. Signed-off-by: Viresh Kumar Acked-by: Chanwoo Choi Tested-by: Dmitry Osipenko --- drivers/devfreq/tegra30-devfreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c index 117cad7968ab..ce83f883ca65 100644 --- a/drivers/devfreq/tegra30-devfreq.c +++ b/drivers/devfreq/tegra30-devfreq.c @@ -647,7 +647,7 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq, return PTR_ERR(opp); } - ret = dev_pm_opp_set_bw(dev, opp); + ret = dev_pm_opp_set_opp(dev, opp); dev_pm_opp_put(opp); return ret; @@ -849,7 +849,7 @@ static int tegra_devfreq_probe(struct platform_device *pdev) return err; } - err = dev_pm_opp_of_add_table(&pdev->dev); + err = dev_pm_opp_of_add_table_noclk(&pdev->dev, 0); if (err) { dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err); goto put_hw; From 240ae50e23061cd1fe1937daab195c17226ffd2e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Jan 2021 15:27:55 +0530 Subject: [PATCH 72/89] opp: Remove dev_pm_opp_set_bw() All the users have migrated to dev_pm_opp_set_opp() now, get rid of the duplicate API, dev_pm_opp_set_bw(), which only performs a part of the new API. While at it, remove the unnecessary parameter to _set_opp_bw(). Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 41 +++++------------------------------------ include/linux/pm_opp.h | 6 ------ 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index fac84d5a1d45..6958a5cd2fd8 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -798,7 +798,7 @@ restore_voltage: } static int _set_opp_bw(const struct opp_table *opp_table, - struct dev_pm_opp *opp, struct device *dev, bool remove) + struct dev_pm_opp *opp, struct device *dev) { u32 avg, peak; int i, ret; @@ -807,7 +807,7 @@ static int _set_opp_bw(const struct opp_table *opp_table, return 0; for (i = 0; i < opp_table->path_count; i++) { - if (remove) { + if (!opp) { avg = 0; peak = 0; } else { @@ -817,7 +817,7 @@ static int _set_opp_bw(const struct opp_table *opp_table, ret = icc_set_bw(opp_table->paths[i], avg, peak); if (ret) { dev_err(dev, "Failed to %s bandwidth[%d]: %d\n", - remove ? "remove" : "set", i, ret); + opp ? "set" : "remove", i, ret); return ret; } } @@ -917,37 +917,6 @@ static int _set_required_opps(struct device *dev, return ret; } -/** - * dev_pm_opp_set_bw() - sets bandwidth levels corresponding to an opp - * @dev: device for which we do this operation - * @opp: opp based on which the bandwidth levels are to be configured - * - * This configures the bandwidth to the levels specified by the OPP. However - * if the OPP specified is NULL the bandwidth levels are cleared out. - * - * Return: 0 on success or a negative error value. - */ -int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) -{ - struct opp_table *opp_table; - int ret; - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "%s: device opp table doesn't exist\n", __func__); - return PTR_ERR(opp_table); - } - - if (opp) - ret = _set_opp_bw(opp_table, opp, dev, false); - else - ret = _set_opp_bw(opp_table, NULL, dev, true); - - dev_pm_opp_put_opp_table(opp_table); - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw); - static void _find_current_opp(struct device *dev, struct opp_table *opp_table) { struct dev_pm_opp *opp = ERR_PTR(-ENODEV); @@ -988,7 +957,7 @@ static int _disable_opp_table(struct device *dev, struct opp_table *opp_table) if (!_get_opp_count(opp_table)) return 0; - ret = _set_opp_bw(opp_table, NULL, dev, true); + ret = _set_opp_bw(opp_table, NULL, dev); if (ret) return ret; @@ -1056,7 +1025,7 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, } if (!ret) { - ret = _set_opp_bw(opp_table, opp, dev, false); + ret = _set_opp_bw(opp_table, opp, dev); if (!ret) { opp_table->enabled = true; dev_pm_opp_put(old_opp); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 7b1d47ab3fb3..25e47ab937b9 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -159,7 +159,6 @@ struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, const char **name int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp); -int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); void dev_pm_opp_remove_table(struct device *dev); @@ -383,11 +382,6 @@ static inline int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp) return -ENOTSUPP; } -static inline int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) -{ - return -EOPNOTSUPP; -} - static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask) { return -ENOTSUPP; From 7eba0c7641b0009818e469dbfcdd87a0155ab9d4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 25 Nov 2019 13:57:58 +0530 Subject: [PATCH 73/89] opp: Allow lazy-linking of required-opps The OPP core currently requires the required opp tables to be available before the dependent OPP table is added, as it needs to create links from the dependent OPP table to the required ones. This may not be convenient for all the platforms though, as this requires strict ordering for probing the drivers. This patch allows lazy-linking of the required-opps. The OPP tables for which the required-opp-tables aren't available at the time of their initialization, are added to a special list of OPP tables: lazy_opp_tables. Later on, whenever a new OPP table is registered with the OPP core, we check if it is required by an OPP table in the pending list; if yes, then we complete the linking then and there. An OPP table is marked unusable until the time all its required-opp tables are available. And if lazy-linking fails for an OPP table, the OPP core disables all of its OPPs to make sure no one can use them. Tested-by: Hsin-Yi Wang Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 45 +++++++++++++---- drivers/opp/of.c | 122 +++++++++++++++++++++++++++++++++++++++++++-- drivers/opp/opp.h | 10 +++- 3 files changed, 161 insertions(+), 16 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 6958a5cd2fd8..e03600547b98 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -27,6 +27,10 @@ * various states of availability. */ LIST_HEAD(opp_tables); + +/* OPP tables with uninitialized required OPPs */ +LIST_HEAD(lazy_opp_tables); + /* Lock to allow exclusive modification to the device and opp lists */ DEFINE_MUTEX(opp_table_lock); /* Flag indicating that opp_tables list is being updated at the moment */ @@ -163,6 +167,10 @@ unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, return 0; } + /* required-opps not fully initialized yet */ + if (lazy_linking_pending(opp->opp_table)) + return 0; + return opp->required_opps[index]->pstate; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_required_pstate); @@ -885,6 +893,10 @@ static int _set_required_opps(struct device *dev, if (!required_opp_tables) return 0; + /* required-opps not fully initialized yet */ + if (lazy_linking_pending(opp_table)) + return -EBUSY; + /* Single genpd case */ if (!genpd_virt_devs) return _set_required_opp(dev, dev, opp, 0); @@ -1181,6 +1193,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) mutex_init(&opp_table->lock); mutex_init(&opp_table->genpd_virt_dev_lock); INIT_LIST_HEAD(&opp_table->dev_list); + INIT_LIST_HEAD(&opp_table->lazy); /* Mark regulator count uninitialized */ opp_table->regulator_count = -1; @@ -1632,6 +1645,21 @@ static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp, return 0; } +void _required_opps_available(struct dev_pm_opp *opp, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (opp->required_opps[i]->available) + continue; + + opp->available = false; + pr_warn("%s: OPP not supported by required OPP %pOF (%lu)\n", + __func__, opp->required_opps[i]->np, opp->rate); + return; + } +} + /* * Returns: * 0: On success. And appropriate error message for duplicate OPPs. @@ -1646,7 +1674,6 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available) { struct list_head *head; - unsigned int i; int ret; mutex_lock(&opp_table->lock); @@ -1672,15 +1699,11 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, __func__, new_opp->rate); } - for (i = 0; i < opp_table->required_opp_count; i++) { - if (new_opp->required_opps[i]->available) - continue; + /* required-opps not fully initialized yet */ + if (lazy_linking_pending(opp_table)) + return 0; - new_opp->available = false; - dev_warn(dev, "%s: OPP not supported by required OPP %pOF (%lu)\n", - __func__, new_opp->required_opps[i]->np, new_opp->rate); - break; - } + _required_opps_available(new_opp, opp_table->required_opp_count); return 0; } @@ -2388,6 +2411,10 @@ int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, if (!src_table || !src_table->required_opp_count) return pstate; + /* required-opps not fully initialized yet */ + if (lazy_linking_pending(src_table)) + return -EBUSY; + for (i = 0; i < src_table->required_opp_count; i++) { if (src_table->required_opp_tables[i]->np == dst_table->np) break; diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 20ccdaab9384..f480c10e6314 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -144,7 +144,7 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table) for (i = 0; i < opp_table->required_opp_count; i++) { if (IS_ERR_OR_NULL(required_opp_tables[i])) - break; + continue; dev_pm_opp_put_opp_table(required_opp_tables[i]); } @@ -153,6 +153,7 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table) opp_table->required_opp_count = 0; opp_table->required_opp_tables = NULL; + list_del(&opp_table->lazy); } /* @@ -165,6 +166,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, { struct opp_table **required_opp_tables; struct device_node *required_np, *np; + bool lazy = false; int count, i; /* Traversing the first OPP node is all we need */ @@ -195,8 +197,10 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, required_opp_tables[i] = _find_table_of_opp_np(required_np); of_node_put(required_np); - if (IS_ERR(required_opp_tables[i])) - goto free_required_tables; + if (IS_ERR(required_opp_tables[i])) { + lazy = true; + continue; + } /* * We only support genpd's OPPs in the "required-opps" for now, @@ -210,6 +214,10 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, } } + /* Let's do the linking later on */ + if (lazy) + list_add(&opp_table->lazy, &lazy_opp_tables); + goto put_np; free_required_tables: @@ -278,14 +286,14 @@ void _of_opp_free_required_opps(struct opp_table *opp_table, for (i = 0; i < opp_table->required_opp_count; i++) { if (!required_opps[i]) - break; + continue; /* Put the reference back */ dev_pm_opp_put(required_opps[i]); } - kfree(required_opps); opp->required_opps = NULL; + kfree(required_opps); } /* Populate all required OPPs which are part of "required-opps" list */ @@ -309,6 +317,10 @@ static int _of_opp_alloc_required_opps(struct opp_table *opp_table, for (i = 0; i < count; i++) { required_table = opp_table->required_opp_tables[i]; + /* Required table not added yet, we will link later */ + if (IS_ERR_OR_NULL(required_table)) + continue; + np = of_parse_required_opp(opp->np, i); if (unlikely(!np)) { ret = -ENODEV; @@ -334,6 +346,104 @@ free_required_opps: return ret; } +/* Link required OPPs for an individual OPP */ +static int lazy_link_required_opps(struct opp_table *opp_table, + struct opp_table *new_table, int index) +{ + struct device_node *required_np; + struct dev_pm_opp *opp; + + list_for_each_entry(opp, &opp_table->opp_list, node) { + required_np = of_parse_required_opp(opp->np, index); + if (unlikely(!required_np)) + return -ENODEV; + + opp->required_opps[index] = _find_opp_of_np(new_table, required_np); + of_node_put(required_np); + + if (!opp->required_opps[index]) { + pr_err("%s: Unable to find required OPP node: %pOF (%d)\n", + __func__, opp->np, index); + return -ENODEV; + } + } + + return 0; +} + +/* Link required OPPs for all OPPs of the newly added OPP table */ +static void lazy_link_required_opp_table(struct opp_table *new_table) +{ + struct opp_table *opp_table, *temp, **required_opp_tables; + struct device_node *required_np, *opp_np, *required_table_np; + struct dev_pm_opp *opp; + int i, ret; + + /* + * We only support genpd's OPPs in the "required-opps" for now, + * as we don't know much about other cases. + */ + if (!new_table->is_genpd) + return; + + mutex_lock(&opp_table_lock); + + list_for_each_entry_safe(opp_table, temp, &lazy_opp_tables, lazy) { + bool lazy = false; + + /* opp_np can't be invalid here */ + opp_np = of_get_next_available_child(opp_table->np, NULL); + + for (i = 0; i < opp_table->required_opp_count; i++) { + required_opp_tables = opp_table->required_opp_tables; + + /* Required opp-table is already parsed */ + if (!IS_ERR(required_opp_tables[i])) + continue; + + /* required_np can't be invalid here */ + required_np = of_parse_required_opp(opp_np, i); + required_table_np = of_get_parent(required_np); + + of_node_put(required_table_np); + of_node_put(required_np); + + /* + * Newly added table isn't the required opp-table for + * opp_table. + */ + if (required_table_np != new_table->np) { + lazy = true; + continue; + } + + required_opp_tables[i] = new_table; + _get_opp_table_kref(new_table); + + /* Link OPPs now */ + ret = lazy_link_required_opps(opp_table, new_table, i); + if (ret) { + /* The OPPs will be marked unusable */ + lazy = false; + break; + } + } + + of_node_put(opp_np); + + /* All required opp-tables found, remove from lazy list */ + if (!lazy) { + list_del(&opp_table->lazy); + INIT_LIST_HEAD(&opp_table->lazy); + + list_for_each_entry(opp, &opp_table->opp_list, node) + _required_opps_available(opp, opp_table->required_opp_count); + } + } + + mutex_unlock(&opp_table_lock); +} + static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table) { struct device_node *np, *opp_np; @@ -889,6 +999,8 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table) } } + lazy_link_required_opp_table(opp_table); + return 0; remove_static_opp: diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 372df68e185b..9b9daf83b074 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -26,7 +26,7 @@ struct regulator; /* Lock to allow exclusive modification to the device and opp lists */ extern struct mutex opp_table_lock; -extern struct list_head opp_tables; +extern struct list_head opp_tables, lazy_opp_tables; /* * Internal data structure organization with the OPP layer library is as @@ -168,7 +168,7 @@ enum opp_table_access { * meant for book keeping and private to OPP library. */ struct opp_table { - struct list_head node; + struct list_head node, lazy; struct blocking_notifier_head head; struct list_head dev_list; @@ -229,6 +229,12 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long f void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk); void _put_opp_list_kref(struct opp_table *opp_table); +void _required_opps_available(struct dev_pm_opp *opp, int count); + +static inline bool lazy_linking_pending(struct opp_table *opp_table) +{ + return unlikely(!list_empty(&opp_table->lazy)); +} #ifdef CONFIG_OF void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index); From 870d5d963972ddefa83a09a7dbe4bef01f0b35b8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Jan 2021 15:30:00 +0530 Subject: [PATCH 74/89] opp: Update bandwidth requirements based on scaling up/down The bandwidth must be scaled at a different point in the code flow based on if we are scaling up or down the frequency, otherwise this may cause undesired effects as the device will try to use more of the memory bandwidth which may be shared across several devices. Much like how regulators and required-opps are programmed. Reported-by: Dmitry Osipenko Reported-by: Akhil P Oommen Signed-off-by: Viresh Kumar Tested-by: Dmitry Osipenko --- drivers/opp/core.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index e03600547b98..a518173fd64a 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1015,8 +1015,16 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, /* Scaling up? Configure required OPPs before frequency */ if (!scaling_down) { ret = _set_required_opps(dev, opp_table, opp, true); - if (ret) + if (ret) { + dev_err(dev, "Failed to set required opps: %d\n", ret); return ret; + } + + ret = _set_opp_bw(opp_table, opp, dev); + if (ret) { + dev_err(dev, "Failed to set bw: %d\n", ret); + return ret; + } } if (opp_table->set_opp) { @@ -1029,25 +1037,31 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); } + if (ret) + return ret; + /* Scaling down? Configure required OPPs after frequency */ - if (!ret && scaling_down) { - ret = _set_required_opps(dev, opp_table, opp, false); - if (ret) - dev_err(dev, "Failed to set required opps: %d\n", ret); - } - - if (!ret) { + if (scaling_down) { ret = _set_opp_bw(opp_table, opp, dev); - if (!ret) { - opp_table->enabled = true; - dev_pm_opp_put(old_opp); + if (ret) { + dev_err(dev, "Failed to set bw: %d\n", ret); + return ret; + } - /* Make sure current_opp doesn't get freed */ - dev_pm_opp_get(opp); - opp_table->current_opp = opp; + ret = _set_required_opps(dev, opp_table, opp, false); + if (ret) { + dev_err(dev, "Failed to set required opps: %d\n", ret); + return ret; } } + opp_table->enabled = true; + dev_pm_opp_put(old_opp); + + /* Make sure current_opp doesn't get freed */ + dev_pm_opp_get(opp); + opp_table->current_opp = opp; + return ret; } From d4a4c7a41153d701f23322ea5d39c766e9ff6eee Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 29 Jan 2021 16:12:04 +0530 Subject: [PATCH 75/89] opp: Don't ignore clk_get() errors other than -ENOENT Not all devices that need to use OPP core need to have clocks, a missing clock is fine in which case -ENOENT shall be returned by clk_get(). Anything else is an error and must be handled properly. Reported-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index a518173fd64a..dc95d29e94c1 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1252,6 +1252,8 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, struct opp_table *opp_table, bool getclk) { + int ret; + /* * Return early if we don't need to get clk or we have already tried it * earlier. @@ -1261,18 +1263,20 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, /* Find clk for the device */ opp_table->clk = clk_get(dev, NULL); - if (IS_ERR(opp_table->clk)) { - int ret = PTR_ERR(opp_table->clk); - if (ret == -EPROBE_DEFER) { - dev_pm_opp_put_opp_table(opp_table); - return ERR_PTR(ret); - } + ret = PTR_ERR_OR_ZERO(opp_table->clk); + if (!ret) + return opp_table; + if (ret == -ENOENT) { dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret); + return opp_table; } - return opp_table; + dev_pm_opp_put_opp_table(opp_table); + dev_err_probe(dev, ret, "Couldn't find clock\n"); + + return ERR_PTR(ret); } /* From f3988bc5d58b768c5cf0dadf5f0e49f7176432df Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 1 Feb 2021 10:35:07 +0530 Subject: [PATCH 76/89] opp: Fix "foo * bar" should be "foo *bar" Fix checkpatch warning: ERROR: "foo * bar" should be "foo *bar". Signed-off-by: Viresh Kumar --- include/linux/pm_opp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 25e47ab937b9..c6c7d73eb015 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -148,7 +148,7 @@ struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) void dev_pm_opp_put_prop_name(struct opp_table *opp_table); struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); void dev_pm_opp_put_regulators(struct opp_table *opp_table); -struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); +struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name); void dev_pm_opp_put_clkname(struct opp_table *opp_table); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); @@ -347,7 +347,7 @@ static inline struct opp_table *dev_pm_opp_set_regulators(struct device *dev, co static inline void dev_pm_opp_put_regulators(struct opp_table *opp_table) {} -static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name) +static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) { return ERR_PTR(-ENOTSUPP); } From 1d614920318b914f86c1fec2adec06ad2f7c3f55 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 1 Feb 2021 10:48:54 +0530 Subject: [PATCH 77/89] opp: Replace ENOTSUPP with EOPNOTSUPP Checkpatch gives following warning for new patches, and the new patches normally follow the existing standards for such stuff. Lets fix it properly. WARNING: ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP. Signed-off-by: Viresh Kumar --- include/linux/pm_opp.h | 64 +++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index c6c7d73eb015..ab1d15ce559d 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -167,12 +167,12 @@ int dev_pm_opp_sync_regulators(struct device *dev); #else static inline struct opp_table *dev_pm_opp_get_opp_table(struct device *dev) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *dev, int index) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_put_opp_table(struct opp_table *opp_table) {} @@ -232,37 +232,37 @@ static inline unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, unsigned int *level) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, unsigned long u_volt) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_put(struct dev_pm_opp *opp) {} @@ -270,7 +270,7 @@ static inline void dev_pm_opp_put(struct dev_pm_opp *opp) {} static inline int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline void dev_pm_opp_remove(struct device *dev, unsigned long freq) @@ -301,19 +301,19 @@ static inline int dev_pm_opp_disable(struct device *dev, unsigned long freq) static inline int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_unregister_notifier(struct device *dev, struct notifier_block *nb) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) {} @@ -321,7 +321,7 @@ static inline void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) {} static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {} @@ -330,33 +330,33 @@ static inline struct opp_table * devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_put_prop_name(struct opp_table *opp_table) {} static inline struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_put_regulators(struct opp_table *opp_table) {} static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {} static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {} @@ -364,27 +364,27 @@ static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {} static inline struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-EOPNOTSUPP); } static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) @@ -402,7 +402,7 @@ static inline void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask static inline int dev_pm_opp_sync_regulators(struct device *dev) { - return -ENOTSUPP; + return -EOPNOTSUPP; } #endif /* CONFIG_PM_OPP */ @@ -427,17 +427,17 @@ static inline void dev_pm_opp_of_unregister_em(struct device *dev) #else static inline int dev_pm_opp_of_add_table(struct device *dev) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_of_add_table_noclk(struct device *dev, int index) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline void dev_pm_opp_of_remove_table(struct device *dev) @@ -446,7 +446,7 @@ static inline void dev_pm_opp_of_remove_table(struct device *dev) static inline int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask) @@ -455,7 +455,7 @@ static inline void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpum static inline int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev) @@ -471,7 +471,7 @@ static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) static inline int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline void dev_pm_opp_of_unregister_em(struct device *dev) @@ -480,12 +480,12 @@ static inline void dev_pm_opp_of_unregister_em(struct device *dev) static inline int of_get_required_opp_performance_state(struct device_node *np, int index) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static inline int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_table *opp_table) { - return -ENOTSUPP; + return -EOPNOTSUPP; } #endif From fc1745c0e40cfc98c0bc466b95ddedf28e5019b4 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Tue, 2 Feb 2021 14:55:24 +0800 Subject: [PATCH 78/89] PM / devfreq: rk3399_dmc: Remove unneeded semicolon Eliminate the following coccicheck warning: ./drivers/devfreq/rk3399_dmc.c:403:2-3: Unneeded semicolon Reported-by: Abaci Robot Signed-off-by: Yang Li Signed-off-by: Chanwoo Choi --- drivers/devfreq/rk3399_dmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index 2e912166a993..9e9d3b4c6d48 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -400,7 +400,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) default: ret = -EINVAL; goto err_edev; - }; + } no_pmu: arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, From 7d8658ef65a4f891d0cff6340fa717b378384642 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Thu, 4 Feb 2021 16:14:22 +0800 Subject: [PATCH 79/89] OPP: Add function to look up required OPP's for a given OPP Add a function that allows looking up required OPPs given a source OPP table, destination OPP table and the source OPP. Signed-off-by: Saravana Kannan Signed-off-by: Hsin-Yi Wang [ Viresh: Rearranged code, fixed return errors ] Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 55 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 7 ++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index dc95d29e94c1..c3f3d9249cc5 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2398,6 +2398,61 @@ devm_pm_opp_attach_genpd(struct device *dev, const char **names, } EXPORT_SYMBOL_GPL(devm_pm_opp_attach_genpd); +/** + * dev_pm_opp_xlate_required_opp() - Find required OPP for @src_table OPP. + * @src_table: OPP table which has @dst_table as one of its required OPP table. + * @dst_table: Required OPP table of the @src_table. + * @src_opp: OPP from the @src_table. + * + * This function returns the OPP (present in @dst_table) pointed out by the + * "required-opps" property of the @src_opp (present in @src_table). + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + * + * Return: pointer to 'struct dev_pm_opp' on success and errorno otherwise. + */ +struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, + struct opp_table *dst_table, + struct dev_pm_opp *src_opp) +{ + struct dev_pm_opp *opp, *dest_opp = ERR_PTR(-ENODEV); + int i; + + if (!src_table || !dst_table || !src_opp || + !src_table->required_opp_tables) + return ERR_PTR(-EINVAL); + + /* required-opps not fully initialized yet */ + if (lazy_linking_pending(src_table)) + return ERR_PTR(-EBUSY); + + for (i = 0; i < src_table->required_opp_count; i++) { + if (src_table->required_opp_tables[i] == dst_table) { + mutex_lock(&src_table->lock); + + list_for_each_entry(opp, &src_table->opp_list, node) { + if (opp == src_opp) { + dest_opp = opp->required_opps[i]; + dev_pm_opp_get(dest_opp); + break; + } + } + + mutex_unlock(&src_table->lock); + break; + } + } + + if (IS_ERR(dest_opp)) { + pr_err("%s: Couldn't find matching OPP (%p: %p)\n", __func__, + src_table, dst_table); + } + + return dest_opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_xlate_required_opp); + /** * dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table. * @src_table: OPP table which has dst_table as one of its required OPP table. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index ab1d15ce559d..c0371efa4a0f 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -156,6 +156,7 @@ struct opp_table *devm_pm_opp_register_set_opp_helper(struct device *dev, int (* struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); void dev_pm_opp_detach_genpd(struct opp_table *opp_table); struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); +struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp); @@ -367,6 +368,12 @@ static inline struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, + struct opp_table *dst_table, struct dev_pm_opp *src_opp) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate) { return -EOPNOTSUPP; From 26f9c7cc42a6dc036edf871544fd0e6b3a0601c1 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Thu, 4 Feb 2021 16:14:23 +0800 Subject: [PATCH 80/89] PM / devfreq: Cache OPP table reference in devfreq The OPP table can be used often in devfreq. Trying to get it each time can be expensive, so cache it in the devfreq struct. Signed-off-by: Saravana Kannan Acked-by: MyungJoo Ham Acked-by: Chanwoo Choi Signed-off-by: Hsin-Yi Wang [ Viresh: Added a blank line ] Signed-off-by: Viresh Kumar --- drivers/devfreq/devfreq.c | 7 +++++++ include/linux/devfreq.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 6aa10de792b3..cefe84a10824 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -757,6 +757,9 @@ static void devfreq_dev_release(struct device *dev) if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); + if (devfreq->opp_table) + dev_pm_opp_put_opp_table(devfreq->opp_table); + mutex_destroy(&devfreq->lock); kfree(devfreq); } @@ -844,6 +847,10 @@ struct devfreq *devfreq_add_device(struct device *dev, } devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); + devfreq->opp_table = dev_pm_opp_get_opp_table(dev); + if (IS_ERR(devfreq->opp_table)) + devfreq->opp_table = NULL; + atomic_set(&devfreq->suspend_count, 0); dev_set_name(&devfreq->dev, "%s", dev_name(dev)); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index b6d3bae1c74d..26ea0850be9b 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -137,6 +137,7 @@ struct devfreq_stats { * using devfreq. * @profile: device-specific devfreq profile * @governor: method how to choose frequency based on the usage. + * @opp_table: Reference to OPP table of dev.parent, if one exists. * @nb: notifier block used to notify devfreq object that it should * reevaluate operable frequencies. Devfreq users may use * devfreq.nb to the corresponding register notifier call chain. @@ -173,6 +174,7 @@ struct devfreq { struct device dev; struct devfreq_dev_profile *profile; const struct devfreq_governor *governor; + struct opp_table *opp_table; struct notifier_block nb; struct delayed_work work; From 86ad9a24f21ea7aac7deed06fe9556392568d88a Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Thu, 4 Feb 2021 16:14:24 +0800 Subject: [PATCH 81/89] PM / devfreq: Add required OPPs support to passive governor Look at the required OPPs of the "parent" device to determine the OPP that is required from the slave device managed by the passive governor. This allows having mappings between a parent device and a slave device even when they don't have the same number of OPPs. While at it do a minor spell-fix and remove out label. Signed-off-by: Saravana Kannan Acked-by: MyungJoo Ham Acked-by: Chanwoo Choi Signed-off-by: Hsin-Yi Wang [ Viresh: Rearranged code and clean error paths ] Signed-off-by: Viresh Kumar --- drivers/devfreq/governor_passive.c | 44 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c index 63332e4a65ae..b094132bd20b 100644 --- a/drivers/devfreq/governor_passive.c +++ b/drivers/devfreq/governor_passive.c @@ -19,18 +19,16 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, = (struct devfreq_passive_data *)devfreq->data; struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent; unsigned long child_freq = ULONG_MAX; - struct dev_pm_opp *opp; - int i, count, ret = 0; + struct dev_pm_opp *opp, *p_opp; + int i, count; /* * If the devfreq device with passive governor has the specific method * to determine the next frequency, should use the get_target_freq() * of struct devfreq_passive_data. */ - if (p_data->get_target_freq) { - ret = p_data->get_target_freq(devfreq, freq); - goto out; - } + if (p_data->get_target_freq) + return p_data->get_target_freq(devfreq, freq); /* * If the parent and passive devfreq device uses the OPP table, @@ -56,26 +54,35 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, * list of parent device. Because in this case, *freq is temporary * value which is decided by ondemand governor. */ - opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - goto out; + if (devfreq->opp_table && parent_devfreq->opp_table) { + p_opp = devfreq_recommended_opp(parent_devfreq->dev.parent, + freq, 0); + if (IS_ERR(p_opp)) + return PTR_ERR(p_opp); + + opp = dev_pm_opp_xlate_required_opp(parent_devfreq->opp_table, + devfreq->opp_table, p_opp); + dev_pm_opp_put(p_opp); + + if (IS_ERR(opp)) + return PTR_ERR(opp); + + *freq = dev_pm_opp_get_freq(opp); + dev_pm_opp_put(opp); + + return 0; } - dev_pm_opp_put(opp); - /* - * Get the OPP table's index of decided freqeuncy by governor + * Get the OPP table's index of decided frequency by governor * of parent device. */ for (i = 0; i < parent_devfreq->profile->max_state; i++) if (parent_devfreq->profile->freq_table[i] == *freq) break; - if (i == parent_devfreq->profile->max_state) { - ret = -EINVAL; - goto out; - } + if (i == parent_devfreq->profile->max_state) + return -EINVAL; /* Get the suitable frequency by using index of parent device. */ if (i < devfreq->profile->max_state) { @@ -88,8 +95,7 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, /* Return the suitable frequency for passive device. */ *freq = child_freq; -out: - return ret; + return 0; } static int devfreq_passive_notifier_call(struct notifier_block *nb, From 5ae4a4b45d4396aa7f7c008c4ae9eca981d43f8c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 2 Feb 2021 10:25:11 +0530 Subject: [PATCH 82/89] cpufreq: Remove CPUFREQ_STICKY flag During cpufreq driver's registration, if the ->init() callback for all the CPUs fail then there is not much point in keeping the driver around as it will only account for more of unnecessary noise, for example cpufreq core will try to suspend/resume the driver which never got registered properly. The removal of such a driver is avoided if the driver carries the CPUFREQ_STICKY flag. This was added way back [1] in 2004 and perhaps no one should ever need it now. A lot of drivers do set this flag, probably because they just copied it from other drivers. This was added earlier for some platforms [2] because their cpufreq drivers were getting registered before the CPUs were registered with subsys framework. And hence they used to fail. The same isn't true anymore though. The current code flow in the kernel is: start_kernel() -> kernel_init() -> kernel_init_freeable() -> do_basic_setup() -> driver_init() -> cpu_dev_init() -> subsys_system_register() //For CPUs -> do_initcalls() -> cpufreq_register_driver() Clearly, the CPUs will always get registered with subsys framework before any cpufreq driver can get probed. Remove the flag and update the relevant drivers. Link: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/include/linux/cpufreq.h?id=7cc9f0d9a1ab04cedc60d64fd8dcf7df224a3b4d # [1] Link: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/arch/arm/mach-sa1100/cpu-sa1100.c?id=f59d3bbe35f6268d729f51be82af8325d62f20f5 # [2] Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-dt.c | 2 +- drivers/cpufreq/cpufreq.c | 3 +-- drivers/cpufreq/davinci-cpufreq.c | 2 +- drivers/cpufreq/loongson1-cpufreq.c | 2 +- drivers/cpufreq/mediatek-cpufreq.c | 2 +- drivers/cpufreq/omap-cpufreq.c | 2 +- drivers/cpufreq/qcom-cpufreq-hw.c | 2 +- drivers/cpufreq/s3c24xx-cpufreq.c | 2 +- drivers/cpufreq/s5pv210-cpufreq.c | 2 +- drivers/cpufreq/sa1100-cpufreq.c | 2 +- drivers/cpufreq/sa1110-cpufreq.c | 2 +- drivers/cpufreq/scmi-cpufreq.c | 2 +- drivers/cpufreq/scpi-cpufreq.c | 2 +- drivers/cpufreq/spear-cpufreq.c | 2 +- drivers/cpufreq/tegra186-cpufreq.c | 2 +- drivers/cpufreq/tegra194-cpufreq.c | 3 +-- drivers/cpufreq/vexpress-spc-cpufreq.c | 3 +-- include/linux/cpufreq.h | 17 +++++++---------- 18 files changed, 24 insertions(+), 30 deletions(-) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index ad4234518ef6..b1e1bdc63b01 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -175,7 +175,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy) } static struct cpufreq_driver dt_cpufreq_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_IS_COOLING_DEV, .verify = cpufreq_generic_frequency_table_verify, .target_index = set_target, diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d0a3525ce27f..7d0ae968def7 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2810,8 +2810,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) if (ret) goto err_boost_unreg; - if (!(cpufreq_driver->flags & CPUFREQ_STICKY) && - list_empty(&cpufreq_policy_list)) { + if (unlikely(list_empty(&cpufreq_policy_list))) { /* if all ->init() calls failed, unregister */ ret = -ENODEV; pr_debug("%s: No CPU initialized for driver %s\n", __func__, diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index 91f477a6cbc4..9e97f60f8199 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -95,7 +95,7 @@ static int davinci_cpu_init(struct cpufreq_policy *policy) } static struct cpufreq_driver davinci_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = davinci_target, .get = cpufreq_generic_get, diff --git a/drivers/cpufreq/loongson1-cpufreq.c b/drivers/cpufreq/loongson1-cpufreq.c index 86f612593e49..fb72d709db56 100644 --- a/drivers/cpufreq/loongson1-cpufreq.c +++ b/drivers/cpufreq/loongson1-cpufreq.c @@ -116,7 +116,7 @@ static int ls1x_cpufreq_exit(struct cpufreq_policy *policy) static struct cpufreq_driver ls1x_cpufreq_driver = { .name = "cpufreq-ls1x", - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = ls1x_cpufreq_target, .get = cpufreq_generic_get, diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index 022e3e966e71..f2e491b25b07 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -463,7 +463,7 @@ static int mtk_cpufreq_exit(struct cpufreq_policy *policy) } static struct cpufreq_driver mtk_cpufreq_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_IS_COOLING_DEV, .verify = cpufreq_generic_frequency_table_verify, diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 3694bb030df3..e035ee216b0f 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -144,7 +144,7 @@ static int omap_cpu_exit(struct cpufreq_policy *policy) } static struct cpufreq_driver omap_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = omap_target, .get = cpufreq_generic_get, diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 9ed5341dc515..2a3b4f44488b 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -374,7 +374,7 @@ static struct freq_attr *qcom_cpufreq_hw_attr[] = { }; static struct cpufreq_driver cpufreq_qcom_hw_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_IS_COOLING_DEV, .verify = cpufreq_generic_frequency_table_verify, diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 37efc0dc3f91..7380c32b238e 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -420,7 +420,7 @@ static int s3c_cpufreq_resume(struct cpufreq_policy *policy) #endif static struct cpufreq_driver s3c24xx_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .target = s3c_cpufreq_target, .get = cpufreq_generic_get, .init = s3c_cpufreq_init, diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index bed496cf8d24..69786e5bbf05 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -574,7 +574,7 @@ static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this, } static struct cpufreq_driver s5pv210_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = s5pv210_target, .get = cpufreq_generic_get, diff --git a/drivers/cpufreq/sa1100-cpufreq.c b/drivers/cpufreq/sa1100-cpufreq.c index 5c075ef6adc0..252b9fc26124 100644 --- a/drivers/cpufreq/sa1100-cpufreq.c +++ b/drivers/cpufreq/sa1100-cpufreq.c @@ -186,7 +186,7 @@ static int __init sa1100_cpu_init(struct cpufreq_policy *policy) } static struct cpufreq_driver sa1100_driver __refdata = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = cpufreq_generic_frequency_table_verify, .target_index = sa1100_target, diff --git a/drivers/cpufreq/sa1110-cpufreq.c b/drivers/cpufreq/sa1110-cpufreq.c index d9d04d935b3a..1a83c8678a63 100644 --- a/drivers/cpufreq/sa1110-cpufreq.c +++ b/drivers/cpufreq/sa1110-cpufreq.c @@ -310,7 +310,7 @@ static int __init sa1110_cpu_init(struct cpufreq_policy *policy) /* sa1110_driver needs __refdata because it must remain after init registers * it with cpufreq_register_driver() */ static struct cpufreq_driver sa1110_driver __refdata = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = cpufreq_generic_frequency_table_verify, .target_index = sa1110_target, diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 491a0a24fb1e..5bd03b59887f 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -217,7 +217,7 @@ static int scmi_cpufreq_exit(struct cpufreq_policy *policy) static struct cpufreq_driver scmi_cpufreq_driver = { .name = "scmi", - .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | + .flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_IS_COOLING_DEV, .verify = cpufreq_generic_frequency_table_verify, diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index e5140ad63db8..d6a698a1b5d1 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -191,7 +191,7 @@ static int scpi_cpufreq_exit(struct cpufreq_policy *policy) static struct cpufreq_driver scpi_cpufreq_driver = { .name = "scpi-cpufreq", - .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | + .flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_IS_COOLING_DEV, .verify = cpufreq_generic_frequency_table_verify, diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 73bd8dc47074..7d0d62a06bf3 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -160,7 +160,7 @@ static int spear_cpufreq_init(struct cpufreq_policy *policy) static struct cpufreq_driver spear_cpufreq_driver = { .name = "cpufreq-spear", - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = spear_cpufreq_target, .get = cpufreq_generic_get, diff --git a/drivers/cpufreq/tegra186-cpufreq.c b/drivers/cpufreq/tegra186-cpufreq.c index e566ea298b59..5d1943e787b0 100644 --- a/drivers/cpufreq/tegra186-cpufreq.c +++ b/drivers/cpufreq/tegra186-cpufreq.c @@ -117,7 +117,7 @@ static unsigned int tegra186_cpufreq_get(unsigned int cpu) static struct cpufreq_driver tegra186_cpufreq_driver = { .name = "tegra186", - .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | + .flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .get = tegra186_cpufreq_get, .verify = cpufreq_generic_frequency_table_verify, diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c index 6a67f36f3b80..a9620e4489ae 100644 --- a/drivers/cpufreq/tegra194-cpufreq.c +++ b/drivers/cpufreq/tegra194-cpufreq.c @@ -272,8 +272,7 @@ static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy, static struct cpufreq_driver tegra194_cpufreq_driver = { .name = "tegra194", - .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | - CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = tegra194_cpufreq_set_target, .get = tegra194_get_speed, diff --git a/drivers/cpufreq/vexpress-spc-cpufreq.c b/drivers/cpufreq/vexpress-spc-cpufreq.c index f711d8eaea6a..51dfa9ae6cf5 100644 --- a/drivers/cpufreq/vexpress-spc-cpufreq.c +++ b/drivers/cpufreq/vexpress-spc-cpufreq.c @@ -486,8 +486,7 @@ static void ve_spc_cpufreq_ready(struct cpufreq_policy *policy) static struct cpufreq_driver ve_spc_cpufreq_driver = { .name = "vexpress-spc", - .flags = CPUFREQ_STICKY | - CPUFREQ_HAVE_GOVERNOR_PER_POLICY | + .flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = ve_spc_cpufreq_set_target, diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 9c8b7437b6cd..c8e40e91fe9b 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -387,8 +387,13 @@ struct cpufreq_driver { /* flags */ -/* driver isn't removed even if all ->init() calls failed */ -#define CPUFREQ_STICKY BIT(0) +/* + * Set by drivers that need to update internale upper and lower boundaries along + * with the target frequency and so the core and governors should also invoke + * the diver if the target frequency does not change, but the policy min or max + * may have changed. + */ +#define CPUFREQ_NEED_UPDATE_LIMITS BIT(0) /* loops_per_jiffy or other kernel "constants" aren't affected by frequency transitions */ #define CPUFREQ_CONST_LOOPS BIT(1) @@ -432,14 +437,6 @@ struct cpufreq_driver { */ #define CPUFREQ_IS_COOLING_DEV BIT(7) -/* - * Set by drivers that need to update internale upper and lower boundaries along - * with the target frequency and so the core and governors should also invoke - * the diver if the target frequency does not change, but the policy min or max - * may have changed. - */ -#define CPUFREQ_NEED_UPDATE_LIMITS BIT(8) - int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); From 2f0531869fd22182e769b10dd6cf151861ede791 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 2 Feb 2021 11:11:55 +0530 Subject: [PATCH 83/89] cpufreq: Remove unused flag CPUFREQ_PM_NO_WARN This flag is set by one of the drivers but it isn't used in the code otherwise. Remove the unused flag and update the driver. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/pmac32-cpufreq.c | 3 +-- include/linux/cpufreq.h | 13 +++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c index 73621bc11976..4f20c6a9108d 100644 --- a/drivers/cpufreq/pmac32-cpufreq.c +++ b/drivers/cpufreq/pmac32-cpufreq.c @@ -439,8 +439,7 @@ static struct cpufreq_driver pmac_cpufreq_driver = { .init = pmac_cpufreq_cpu_init, .suspend = pmac_cpufreq_suspend, .resume = pmac_cpufreq_resume, - .flags = CPUFREQ_PM_NO_WARN | - CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, + .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .attr = cpufreq_generic_attr, .name = "powermac", }; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index c8e40e91fe9b..353969c7acd3 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -398,8 +398,11 @@ struct cpufreq_driver { /* loops_per_jiffy or other kernel "constants" aren't affected by frequency transitions */ #define CPUFREQ_CONST_LOOPS BIT(1) -/* don't warn on suspend/resume speed mismatches */ -#define CPUFREQ_PM_NO_WARN BIT(2) +/* + * Set by drivers that want the core to automatically register the cpufreq + * driver as a thermal cooling device. + */ +#define CPUFREQ_IS_COOLING_DEV BIT(2) /* * This should be set by platforms having multiple clock-domains, i.e. @@ -431,12 +434,6 @@ struct cpufreq_driver { */ #define CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING BIT(6) -/* - * Set by drivers that want the core to automatically register the cpufreq - * driver as a thermal cooling device. - */ -#define CPUFREQ_IS_COOLING_DEV BIT(7) - int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); From 3af2f0aa2ed04f07975ba1242002b66cd53e6290 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Tue, 9 Feb 2021 10:54:34 +0000 Subject: [PATCH 84/89] PM: EM: update Kconfig description and drop "default n" option Energy Model supports now other devices like GPUs, DSPs, not only CPUs. Thus, update the description in the config option. Remove also unneeded "default n". If the "default" line is removed, it defaults to 'n'. Signed-off-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- kernel/power/Kconfig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index a7320f07689d..56dbc2616d5c 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -322,15 +322,14 @@ config CPU_PM bool config ENERGY_MODEL - bool "Energy Model for CPUs" + bool "Energy Model for devices with DVFS (CPUs, GPUs, etc)" depends on SMP depends on CPU_FREQ - default n help Several subsystems (thermal and/or the task scheduler for example) - can leverage information about the energy consumed by CPUs to make - smarter decisions. This config option enables the framework from - which subsystems can access the energy models. + can leverage information about the energy consumed by devices to + make smarter decisions. This config option enables the framework + from which subsystems can access the energy models. The exact usage of the energy model is subsystem-dependent. From c4cc3141b6f8e0097a03f6885cafac957421df9e Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Tue, 9 Feb 2021 10:54:35 +0000 Subject: [PATCH 85/89] PM: Kconfig: remove unneeded "default n" options Remove "default n" options. If the "default" line is removed, it defaults to 'n'. Signed-off-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- kernel/power/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 56dbc2616d5c..6bfe3ead10ad 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -139,7 +139,6 @@ config PM_SLEEP_SMP_NONZERO_CPU config PM_AUTOSLEEP bool "Opportunistic sleep" depends on PM_SLEEP - default n help Allow the kernel to trigger a system transition into a global sleep state automatically whenever there are no active wakeup sources. @@ -147,7 +146,6 @@ config PM_AUTOSLEEP config PM_WAKELOCKS bool "User space wakeup sources interface" depends on PM_SLEEP - default n help Allow user space to create, activate and deactivate wakeup source objects with the help of a sysfs-based interface. @@ -293,7 +291,6 @@ config PM_GENERIC_DOMAINS config WQ_POWER_EFFICIENT_DEFAULT bool "Enable workqueue power-efficient mode by default" depends on PM - default n help Per-cpu workqueues are generally preferred because they show better performance thanks to cache locality; unfortunately, From 1556057413a304b3020180240d798ec135d90844 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Mon, 1 Feb 2021 23:57:35 +0100 Subject: [PATCH 86/89] PM: sleep: Constify static struct attribute_group The only usage of suspend_attr_group is to put its address in an array of pointers to const attribute_group structs. Make it const to allow the compiler to put it into read-only memory. Signed-off-by: Rikard Falkeborn Signed-off-by: Rafael J. Wysocki --- kernel/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/power/main.c b/kernel/power/main.c index 0aefd6f57e0a..12c7e1bb442f 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -387,7 +387,7 @@ static struct attribute *suspend_attrs[] = { NULL, }; -static struct attribute_group suspend_attr_group = { +static const struct attribute_group suspend_attr_group = { .name = "suspend_stats", .attrs = suspend_attrs, }; From 88ffce95764603e13eda4be003ec919e124ec365 Mon Sep 17 00:00:00 2001 From: Yunfeng Ye Date: Sat, 23 Jan 2021 05:06:07 -0500 Subject: [PATCH 87/89] powercap: intel_rapl: Use topology interface in rapl_add_package() It's not a good idea to access phys_proc_id and cpu_die_id directly. Use topology_physical_package_id(cpu) and topology_die_id(cpu) instead. Signed-off-by: Yunfeng Ye [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index f0799837c2dd..b6c7bedf517e 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -1310,7 +1310,6 @@ struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv) { int id = topology_logical_die_id(cpu); struct rapl_package *rp; - struct cpuinfo_x86 *c = &cpu_data(cpu); int ret; if (!rapl_defaults) @@ -1327,10 +1326,11 @@ struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv) if (topology_max_die_per_package() > 1) snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, - "package-%d-die-%d", c->phys_proc_id, c->cpu_die_id); + "package-%d-die-%d", + topology_physical_package_id(cpu), topology_die_id(cpu)); else snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d", - c->phys_proc_id); + topology_physical_package_id(cpu)); /* check if the package contains valid domains */ if (rapl_detect_domains(rp, cpu) || rapl_defaults->check_unit(rp, cpu)) { From 65348ba259e27ad4b69459ef477facd4c702bbf6 Mon Sep 17 00:00:00 2001 From: Yunfeng Ye Date: Sat, 23 Jan 2021 05:06:08 -0500 Subject: [PATCH 88/89] powercap: intel_rapl: Use topology interface in rapl_init_domains() It's not a good idea to access the phys_proc_id of cpuinfo directly. Use topology_physical_package_id(cpu) instead. Signed-off-by: Yunfeng Ye [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index b6c7bedf517e..fdda2a737186 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -547,7 +547,7 @@ static void rapl_init_domains(struct rapl_package *rp) if (i == RAPL_DOMAIN_PLATFORM && rp->id > 0) { snprintf(rd->name, RAPL_DOMAIN_NAME_LENGTH, "psys-%d", - cpu_data(rp->lead_cpu).phys_proc_id); + topology_physical_package_id(rp->lead_cpu)); } else snprintf(rd->name, RAPL_DOMAIN_NAME_LENGTH, "%s", rapl_domain_names[i]); From e1d3209f95a19df16080b069265e172738189807 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 10 Feb 2021 18:22:08 +0100 Subject: [PATCH 89/89] MAINTAINERS: cpuidle: exynos: include header in file pattern Include the platform data header in Exynos cpuidle maintainer entry. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 00836f6452f0..ff3a64e31d2b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4621,6 +4621,7 @@ L: linux-samsung-soc@vger.kernel.org S: Supported F: arch/arm/mach-exynos/pm.c F: drivers/cpuidle/cpuidle-exynos.c +F: include/linux/platform_data/cpuidle-exynos.h CPUIDLE DRIVER - ARM PSCI M: Lorenzo Pieralisi