Commit 46501add authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge tag 'dtpm-v5.18' of https://git.linaro.org/people/daniel.lezcano/linux

Pull DTPM (Dynamic Thermal Power Management) changes for 5.18-rc1 from
Daniel Lezcano:

"- Added dtpm hierarchy description (Daniel Lezcano)

 - Changed the locking scheme (Daniel Lezcano)

 - Fixed dtpm_cpu cleanup at exit time and missing virtual dtpm pointer
   release (Daniel Lezcano)"

* tag 'dtpm-v5.18' of https://git.linaro.org/people/daniel.lezcano/linux:
  dtpm/soc/rk3399: Add the ability to unload the module
  powercap/dtpm_cpu: Add exit function
  powercap/dtpm: Move the 'root' reset place
  powercap/dtpm: Destroy hierarchy function
  powercap/dtpm: Fixup kfree for virtual node
  powercap/dtpm_cpu: Reset per_cpu variable in the release function
  powercap/dtpm: Change locking scheme
  rockchip/soc/drivers: Add DTPM description for rk3399
  powercap/drivers/dtpm: Add dtpm devfreq with energy model support
  powercap/drivers/dtpm: Add CPU DT initialization support
  powercap/drivers/dtpm: Add hierarchy creation
  powercap/drivers/dtpm: Convert the init table section to a simple array
parents cfb92440 f1ebef9e
......@@ -46,6 +46,7 @@ config IDLE_INJECT
config DTPM
bool "Power capping for Dynamic Thermal Power Management (EXPERIMENTAL)"
depends on OF
help
This enables support for the power capping for the dynamic
thermal power management userspace engine.
......@@ -56,4 +57,11 @@ config DTPM_CPU
help
This enables support for CPU power limitation based on
energy model.
config DTPM_DEVFREQ
bool "Add device power capping based on the energy model"
depends on DTPM && ENERGY_MODEL
help
This enables support for device power limitation based on
energy model.
endif
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DTPM) += dtpm.o
obj-$(CONFIG_DTPM_CPU) += dtpm_cpu.o
obj-$(CONFIG_DTPM_DEVFREQ) += dtpm_devfreq.o
obj-$(CONFIG_POWERCAP) += powercap_sys.o
obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o
obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o
......
This diff is collapsed.
......@@ -21,6 +21,7 @@
#include <linux/cpuhotplug.h>
#include <linux/dtpm.h>
#include <linux/energy_model.h>
#include <linux/of.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/units.h>
......@@ -150,10 +151,17 @@ static int update_pd_power_uw(struct dtpm *dtpm)
static void pd_release(struct dtpm *dtpm)
{
struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm);
struct cpufreq_policy *policy;
if (freq_qos_request_active(&dtpm_cpu->qos_req))
freq_qos_remove_request(&dtpm_cpu->qos_req);
policy = cpufreq_cpu_get(dtpm_cpu->cpu);
if (policy) {
for_each_cpu(dtpm_cpu->cpu, policy->related_cpus)
per_cpu(dtpm_per_cpu, dtpm_cpu->cpu) = NULL;
}
kfree(dtpm_cpu);
}
......@@ -176,6 +184,17 @@ static int cpuhp_dtpm_cpu_offline(unsigned int cpu)
}
static int cpuhp_dtpm_cpu_online(unsigned int cpu)
{
struct dtpm_cpu *dtpm_cpu;
dtpm_cpu = per_cpu(dtpm_per_cpu, cpu);
if (dtpm_cpu)
return dtpm_update_power(&dtpm_cpu->dtpm);
return 0;
}
static int __dtpm_cpu_setup(int cpu, struct dtpm *parent)
{
struct dtpm_cpu *dtpm_cpu;
struct cpufreq_policy *policy;
......@@ -183,6 +202,10 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)
char name[CPUFREQ_NAME_LEN];
int ret = -ENOMEM;
dtpm_cpu = per_cpu(dtpm_per_cpu, cpu);
if (dtpm_cpu)
return 0;
policy = cpufreq_cpu_get(cpu);
if (!policy)
return 0;
......@@ -191,10 +214,6 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)
if (!pd)
return -EINVAL;
dtpm_cpu = per_cpu(dtpm_per_cpu, cpu);
if (dtpm_cpu)
return dtpm_update_power(&dtpm_cpu->dtpm);
dtpm_cpu = kzalloc(sizeof(*dtpm_cpu), GFP_KERNEL);
if (!dtpm_cpu)
return -ENOMEM;
......@@ -207,7 +226,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)
snprintf(name, sizeof(name), "cpu%d-cpufreq", dtpm_cpu->cpu);
ret = dtpm_register(name, &dtpm_cpu->dtpm, NULL);
ret = dtpm_register(name, &dtpm_cpu->dtpm, parent);
if (ret)
goto out_kfree_dtpm_cpu;
......@@ -231,7 +250,18 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)
return ret;
}
static int __init dtpm_cpu_init(void)
static int dtpm_cpu_setup(struct dtpm *dtpm, struct device_node *np)
{
int cpu;
cpu = of_cpu_node_to_id(np);
if (cpu < 0)
return 0;
return __dtpm_cpu_setup(cpu, dtpm);
}
static int dtpm_cpu_init(void)
{
int ret;
......@@ -269,4 +299,15 @@ static int __init dtpm_cpu_init(void)
return 0;
}
DTPM_DECLARE(dtpm_cpu, dtpm_cpu_init);
static void dtpm_cpu_exit(void)
{
cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
cpuhp_remove_state_nocalls(CPUHP_AP_DTPM_CPU_DEAD);
}
struct dtpm_subsys_ops dtpm_cpu_ops = {
.name = KBUILD_MODNAME,
.init = dtpm_cpu_init,
.exit = dtpm_cpu_exit,
.setup = dtpm_cpu_setup,
};
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2021 Linaro Limited
*
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
*
* The devfreq device combined with the energy model and the load can
* give an estimation of the power consumption as well as limiting the
* power.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/cpumask.h>
#include <linux/devfreq.h>
#include <linux/dtpm.h>
#include <linux/energy_model.h>
#include <linux/of.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/units.h>
struct dtpm_devfreq {
struct dtpm dtpm;
struct dev_pm_qos_request qos_req;
struct devfreq *devfreq;
};
static struct dtpm_devfreq *to_dtpm_devfreq(struct dtpm *dtpm)
{
return container_of(dtpm, struct dtpm_devfreq, dtpm);
}
static int update_pd_power_uw(struct dtpm *dtpm)
{
struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
struct devfreq *devfreq = dtpm_devfreq->devfreq;
struct device *dev = devfreq->dev.parent;
struct em_perf_domain *pd = em_pd_get(dev);
dtpm->power_min = pd->table[0].power;
dtpm->power_min *= MICROWATT_PER_MILLIWATT;
dtpm->power_max = pd->table[pd->nr_perf_states - 1].power;
dtpm->power_max *= MICROWATT_PER_MILLIWATT;
return 0;
}
static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
{
struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
struct devfreq *devfreq = dtpm_devfreq->devfreq;
struct device *dev = devfreq->dev.parent;
struct em_perf_domain *pd = em_pd_get(dev);
unsigned long freq;
u64 power;
int i;
for (i = 0; i < pd->nr_perf_states; i++) {
power = pd->table[i].power * MICROWATT_PER_MILLIWATT;
if (power > power_limit)
break;
}
freq = pd->table[i - 1].frequency;
dev_pm_qos_update_request(&dtpm_devfreq->qos_req, freq);
power_limit = pd->table[i - 1].power * MICROWATT_PER_MILLIWATT;
return power_limit;
}
static void _normalize_load(struct devfreq_dev_status *status)
{
if (status->total_time > 0xfffff) {
status->total_time >>= 10;
status->busy_time >>= 10;
}
status->busy_time <<= 10;
status->busy_time /= status->total_time ? : 1;
status->busy_time = status->busy_time ? : 1;
status->total_time = 1024;
}
static u64 get_pd_power_uw(struct dtpm *dtpm)
{
struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
struct devfreq *devfreq = dtpm_devfreq->devfreq;
struct device *dev = devfreq->dev.parent;
struct em_perf_domain *pd = em_pd_get(dev);
struct devfreq_dev_status status;
unsigned long freq;
u64 power;
int i;
mutex_lock(&devfreq->lock);
status = devfreq->last_status;
mutex_unlock(&devfreq->lock);
freq = DIV_ROUND_UP(status.current_frequency, HZ_PER_KHZ);
_normalize_load(&status);
for (i = 0; i < pd->nr_perf_states; i++) {
if (pd->table[i].frequency < freq)
continue;
power = pd->table[i].power * MICROWATT_PER_MILLIWATT;
power *= status.busy_time;
power >>= 10;
return power;
}
return 0;
}
static void pd_release(struct dtpm *dtpm)
{
struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm);
if (dev_pm_qos_request_active(&dtpm_devfreq->qos_req))
dev_pm_qos_remove_request(&dtpm_devfreq->qos_req);
kfree(dtpm_devfreq);
}
static struct dtpm_ops dtpm_ops = {
.set_power_uw = set_pd_power_limit,
.get_power_uw = get_pd_power_uw,
.update_power_uw = update_pd_power_uw,
.release = pd_release,
};
static int __dtpm_devfreq_setup(struct devfreq *devfreq, struct dtpm *parent)
{
struct device *dev = devfreq->dev.parent;
struct dtpm_devfreq *dtpm_devfreq;
struct em_perf_domain *pd;
int ret = -ENOMEM;
pd = em_pd_get(dev);
if (!pd) {
ret = dev_pm_opp_of_register_em(dev, NULL);
if (ret) {
pr_err("No energy model available for '%s'\n", dev_name(dev));
return -EINVAL;
}
}
dtpm_devfreq = kzalloc(sizeof(*dtpm_devfreq), GFP_KERNEL);
if (!dtpm_devfreq)
return -ENOMEM;
dtpm_init(&dtpm_devfreq->dtpm, &dtpm_ops);
dtpm_devfreq->devfreq = devfreq;
ret = dtpm_register(dev_name(dev), &dtpm_devfreq->dtpm, parent);
if (ret) {
pr_err("Failed to register '%s': %d\n", dev_name(dev), ret);
kfree(dtpm_devfreq);
return ret;
}
ret = dev_pm_qos_add_request(dev, &dtpm_devfreq->qos_req,
DEV_PM_QOS_MAX_FREQUENCY,
PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
if (ret) {
pr_err("Failed to add QoS request: %d\n", ret);
goto out_dtpm_unregister;
}
dtpm_update_power(&dtpm_devfreq->dtpm);
return 0;
out_dtpm_unregister:
dtpm_unregister(&dtpm_devfreq->dtpm);
return ret;
}
static int dtpm_devfreq_setup(struct dtpm *dtpm, struct device_node *np)
{
struct devfreq *devfreq;
devfreq = devfreq_get_devfreq_by_node(np);
if (IS_ERR(devfreq))
return 0;
return __dtpm_devfreq_setup(devfreq, dtpm);
}
struct dtpm_subsys_ops dtpm_devfreq_ops = {
.name = KBUILD_MODNAME,
.setup = dtpm_devfreq_setup,
};
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2022 Linaro Ltd
*
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
*/
#ifndef ___DTPM_SUBSYS_H__
#define ___DTPM_SUBSYS_H__
extern struct dtpm_subsys_ops dtpm_cpu_ops;
extern struct dtpm_subsys_ops dtpm_devfreq_ops;
struct dtpm_subsys_ops *dtpm_subsys[] = {
#ifdef CONFIG_DTPM_CPU
&dtpm_cpu_ops,
#endif
#ifdef CONFIG_DTPM_DEVFREQ
&dtpm_devfreq_ops,
#endif
};
#endif
......@@ -34,4 +34,12 @@ config ROCKCHIP_PM_DOMAINS
If unsure, say N.
config ROCKCHIP_DTPM
tristate "Rockchip DTPM hierarchy"
depends on DTPM && m
help
Describe the hierarchy for the Dynamic Thermal Power
Management tree on this platform. That will create all the
power capping capable devices.
endif
......@@ -5,3 +5,4 @@
obj-$(CONFIG_ROCKCHIP_GRF) += grf.o
obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o
obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o
obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2021 Linaro Limited
*
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
*
* DTPM hierarchy description
*/
#include <linux/dtpm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
static struct dtpm_node __initdata rk3399_hierarchy[] = {
[0]{ .name = "rk3399",
.type = DTPM_NODE_VIRTUAL },
[1]{ .name = "package",
.type = DTPM_NODE_VIRTUAL,
.parent = &rk3399_hierarchy[0] },
[2]{ .name = "/cpus/cpu@0",
.type = DTPM_NODE_DT,
.parent = &rk3399_hierarchy[1] },
[3]{ .name = "/cpus/cpu@1",
.type = DTPM_NODE_DT,
.parent = &rk3399_hierarchy[1] },
[4]{ .name = "/cpus/cpu@2",
.type = DTPM_NODE_DT,
.parent = &rk3399_hierarchy[1] },
[5]{ .name = "/cpus/cpu@3",
.type = DTPM_NODE_DT,
.parent = &rk3399_hierarchy[1] },
[6]{ .name = "/cpus/cpu@100",
.type = DTPM_NODE_DT,
.parent = &rk3399_hierarchy[1] },
[7]{ .name = "/cpus/cpu@101",
.type = DTPM_NODE_DT,
.parent = &rk3399_hierarchy[1] },
[8]{ .name = "/gpu@ff9a0000",
.type = DTPM_NODE_DT,
.parent = &rk3399_hierarchy[1] },
[9]{ /* sentinel */ }
};
static struct of_device_id __initdata rockchip_dtpm_match_table[] = {
{ .compatible = "rockchip,rk3399", .data = rk3399_hierarchy },
{},
};
static int __init rockchip_dtpm_init(void)
{
return dtpm_create_hierarchy(rockchip_dtpm_match_table);
}
module_init(rockchip_dtpm_init);
static void __exit rockchip_dtpm_exit(void)
{
return dtpm_destroy_hierarchy();
}
module_exit(rockchip_dtpm_exit);
MODULE_SOFTDEP("pre: panfrost cpufreq-dt");
MODULE_DESCRIPTION("Rockchip DTPM driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dtpm");
MODULE_AUTHOR("Daniel Lezcano <daniel.lezcano@kernel.org");
......@@ -321,16 +321,6 @@
#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 = .; \
......@@ -723,7 +713,6 @@
ACPI_PROBE_TABLE(irqchip) \
ACPI_PROBE_TABLE(timer) \
THERMAL_TABLE(governor) \
DTPM_TABLE() \
EARLYCON_TABLE() \
LSM_TABLE() \
EARLY_LSM_TABLE() \
......
......@@ -32,28 +32,25 @@ struct dtpm_ops {
void (*release)(struct dtpm *);
};
typedef int (*dtpm_init_t)(void);
struct device_node;
struct dtpm_descr {
dtpm_init_t init;
struct dtpm_subsys_ops {
const char *name;
int (*init)(void);
void (*exit)(void);
int (*setup)(struct dtpm *, struct device_node *);
};
/* Init section thermal table */
extern struct dtpm_descr __dtpm_table[];
extern struct dtpm_descr __dtpm_table_end[];
#define DTPM_TABLE_ENTRY(name, __init) \
static struct dtpm_descr __dtpm_table_entry_##name \
__used __section("__dtpm_table") = { \
.init = __init, \
}
#define DTPM_DECLARE(name, init) DTPM_TABLE_ENTRY(name, init)
enum DTPM_NODE_TYPE {
DTPM_NODE_VIRTUAL = 0,
DTPM_NODE_DT,
};
#define for_each_dtpm_table(__dtpm) \
for (__dtpm = __dtpm_table; \
__dtpm < __dtpm_table_end; \
__dtpm++)
struct dtpm_node {
enum DTPM_NODE_TYPE type;
const char *name;
struct dtpm_node *parent;
};
static inline struct dtpm *to_dtpm(struct powercap_zone *zone)
{
......@@ -70,4 +67,7 @@ void dtpm_unregister(struct dtpm *dtpm);
int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent);
int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table);
void dtpm_destroy_hierarchy(void);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment