* [PATCH V2 06/13] intel_rapl: abstract register access operations
From: Zhang Rui @ 2019-07-04 16:34 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1562258085-3165-1-git-send-email-rui.zhang@intel.com>
MSR and MMIO RAPL interfaces have different ways to access the registers,
thus in order to abstract the register access operations, two callbacks,
.read_raw()/.write_raw() are introduced, and they should be implemented by
MSR RAPL and MMIO RAPL interface driver respectly.
This patch implements them for the MSR I/F only.
Reviewed-and-tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 110 ++++++++++++++++++++++--------------------
include/linux/intel_rapl.h | 13 +++++
2 files changed, 70 insertions(+), 53 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 9f22aed..d3b9d1c 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -93,13 +93,6 @@ static struct rapl_if_priv rapl_msr_priv = {
/* per domain data, some are optional */
#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2)
-struct msrl_action {
- u32 msr_no;
- u64 clear_mask;
- u64 set_mask;
- int err;
-};
-
#define DOMAIN_STATE_INACTIVE BIT(0)
#define DOMAIN_STATE_POWER_LIMIT_SET BIT(1)
#define DOMAIN_STATE_BIOS_LOCKED BIT(2)
@@ -692,16 +685,16 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim,
bool xlate, u64 *data)
{
- u64 value, final;
- u32 msr;
+ u64 value;
struct rapl_primitive_info *rp = &rpi[prim];
+ struct reg_action ra;
int cpu;
if (!rp->name || rp->flag & RAPL_PRIMITIVE_DUMMY)
return -EINVAL;
- msr = rd->regs[rp->id];
- if (!msr)
+ ra.reg = rd->regs[rp->id];
+ if (!ra.reg)
return -EINVAL;
cpu = rd->rp->lead_cpu;
@@ -717,47 +710,23 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
return 0;
}
- if (rdmsrl_safe_on_cpu(cpu, msr, &value)) {
- pr_debug("failed to read msr 0x%x on cpu %d\n", msr, cpu);
+ ra.mask = rp->mask;
+
+ if (rd->rp->priv->read_raw(cpu, &ra)) {
+ pr_debug("failed to read reg 0x%x on cpu %d\n", ra.reg, cpu);
return -EIO;
}
- final = value & rp->mask;
- final = final >> rp->shift;
+ value = ra.value >> rp->shift;
+
if (xlate)
- *data = rapl_unit_xlate(rd, rp->unit, final, 0);
+ *data = rapl_unit_xlate(rd, rp->unit, value, 0);
else
- *data = final;
+ *data = value;
return 0;
}
-
-static int msrl_update_safe(u32 msr_no, u64 clear_mask, u64 set_mask)
-{
- int err;
- u64 val;
-
- err = rdmsrl_safe(msr_no, &val);
- if (err)
- goto out;
-
- val &= ~clear_mask;
- val |= set_mask;
-
- err = wrmsrl_safe(msr_no, val);
-
-out:
- return err;
-}
-
-static void msrl_update_func(void *info)
-{
- struct msrl_action *ma = info;
-
- ma->err = msrl_update_safe(ma->msr_no, ma->clear_mask, ma->set_mask);
-}
-
/* Similar use of primitive info in the read counterpart */
static int rapl_write_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim,
@@ -766,7 +735,7 @@ static int rapl_write_data_raw(struct rapl_domain *rd,
struct rapl_primitive_info *rp = &rpi[prim];
int cpu;
u64 bits;
- struct msrl_action ma;
+ struct reg_action ra;
int ret;
cpu = rd->rp->lead_cpu;
@@ -774,17 +743,13 @@ static int rapl_write_data_raw(struct rapl_domain *rd,
bits <<= rp->shift;
bits &= rp->mask;
- memset(&ma, 0, sizeof(ma));
+ memset(&ra, 0, sizeof(ra));
- ma.msr_no = rd->regs[rp->id];
- ma.clear_mask = rp->mask;
- ma.set_mask = bits;
+ ra.reg = rd->regs[rp->id];
+ ra.mask = rp->mask;
+ ra.value = bits;
- ret = smp_call_function_single(cpu, msrl_update_func, &ma, 1);
- if (ret)
- WARN_ON_ONCE(ret);
- else
- ret = ma.err;
+ ret = rd->rp->priv->write_raw(cpu, &ra);
return ret;
}
@@ -1507,6 +1472,43 @@ static struct notifier_block rapl_pm_notifier = {
.notifier_call = rapl_pm_callback,
};
+static int rapl_msr_read_raw(int cpu, struct reg_action *ra)
+{
+ if (rdmsrl_safe_on_cpu(cpu, ra->reg, &ra->value)) {
+ pr_debug("failed to read msr 0x%x on cpu %d\n", ra->reg, cpu);
+ return -EIO;
+ }
+ ra->value &= ra->mask;
+ return 0;
+}
+
+static void rapl_msr_update_func(void *info)
+{
+ struct reg_action *ra = info;
+ u64 val;
+
+ ra->err = rdmsrl_safe(ra->reg, &val);
+ if (ra->err)
+ return;
+
+ val &= ~ra->mask;
+ val |= ra->value;
+
+ ra->err = wrmsrl_safe(ra->reg, val);
+}
+
+
+static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
+{
+ int ret;
+
+ ret = smp_call_function_single(cpu, rapl_msr_update_func, ra, 1);
+ if (WARN_ON_ONCE(ret))
+ return ret;
+
+ return ra->err;
+}
+
static int __init rapl_init(void)
{
const struct x86_cpu_id *id;
@@ -1522,6 +1524,8 @@ static int __init rapl_init(void)
rapl_defaults = (struct rapl_defaults *)id->driver_data;
+ rapl_msr_priv.read_raw = rapl_msr_read_raw;
+ rapl_msr_priv.write_raw = rapl_msr_write_raw;
ret = rapl_register_powercap();
if (ret)
return ret;
diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h
index ec2c9e8..ff215d6 100644
--- a/include/linux/intel_rapl.h
+++ b/include/linux/intel_rapl.h
@@ -88,6 +88,13 @@ struct rapl_domain {
struct rapl_package *rp;
};
+struct reg_action {
+ u32 reg;
+ u64 mask;
+ u64 value;
+ int err;
+};
+
/**
* struct rapl_if_priv: private data for different RAPL interfaces
* @control_type: Each RAPL interface must have its own powercap
@@ -97,6 +104,10 @@ struct rapl_domain {
* @pcap_rapl_online: CPU hotplug state for each RAPL interface.
* @reg_unit: Register for getting energy/power/time unit.
* @regs: Register sets for different RAPL Domains.
+ * @read_raw: Callback for reading RAPL interface specific
+ * registers.
+ * @write_raw: Callback for writing RAPL interface specific
+ * registers.
*/
struct rapl_if_priv {
struct powercap_control_type *control_type;
@@ -104,6 +115,8 @@ struct rapl_if_priv {
enum cpuhp_state pcap_rapl_online;
u32 reg_unit;
u32 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
+ int (*read_raw)(int cpu, struct reg_action *ra);
+ int (*write_raw)(int cpu, struct reg_action *ra);
};
/* maximum rapl package domain name: package-%d-die-%d */
--
2.7.4
^ permalink raw reply related
* [PATCH V2 03/13] intel_rapl: introduce intel_rapl.h
From: Zhang Rui @ 2019-07-04 16:34 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1562258085-3165-1-git-send-email-rui.zhang@intel.com>
Create a new header file for the common definitions that might be used
by different RAPL Interface.
Reviewed-and-tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
MAINTAINERS | 1 +
drivers/powercap/intel_rapl.c | 101 +------------------------------------
include/linux/intel_rapl.h | 113 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 116 insertions(+), 99 deletions(-)
create mode 100644 include/linux/intel_rapl.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 57f496c..b51f2b5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12601,6 +12601,7 @@ F: drivers/base/power/
F: include/linux/pm.h
F: include/linux/pm_*
F: include/linux/powercap.h
+F: include/linux/intel_rapl.h
F: drivers/powercap/
F: kernel/configs/nopm.config
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 9be9f20..adb35ec 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -18,8 +18,9 @@
#include <linux/cpu.h>
#include <linux/powercap.h>
#include <linux/suspend.h>
-#include <asm/iosf_mbi.h>
+#include <linux/intel_rapl.h>
+#include <asm/iosf_mbi.h>
#include <asm/processor.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
@@ -74,59 +75,9 @@ enum unit_type {
TIME_UNIT,
};
-enum rapl_domain_type {
- RAPL_DOMAIN_PACKAGE, /* entire package/socket */
- RAPL_DOMAIN_PP0, /* core power plane */
- RAPL_DOMAIN_PP1, /* graphics uncore */
- RAPL_DOMAIN_DRAM,/* DRAM control_type */
- RAPL_DOMAIN_PLATFORM, /* PSys control_type */
- RAPL_DOMAIN_MAX,
-};
-
-enum rapl_domain_reg_id {
- RAPL_DOMAIN_REG_LIMIT,
- RAPL_DOMAIN_REG_STATUS,
- RAPL_DOMAIN_REG_PERF,
- RAPL_DOMAIN_REG_POLICY,
- RAPL_DOMAIN_REG_INFO,
- RAPL_DOMAIN_REG_MAX,
-};
-
/* per domain data, some are optional */
-enum rapl_primitives {
- ENERGY_COUNTER,
- POWER_LIMIT1,
- POWER_LIMIT2,
- FW_LOCK,
-
- PL1_ENABLE, /* power limit 1, aka long term */
- PL1_CLAMP, /* allow frequency to go below OS request */
- PL2_ENABLE, /* power limit 2, aka short term, instantaneous */
- PL2_CLAMP,
-
- TIME_WINDOW1, /* long term */
- TIME_WINDOW2, /* short term */
- THERMAL_SPEC_POWER,
- MAX_POWER,
-
- MIN_POWER,
- MAX_TIME_WINDOW,
- THROTTLED_TIME,
- PRIORITY_LEVEL,
-
- /* below are not raw primitive data */
- AVERAGE_POWER,
- NR_RAPL_PRIMITIVES,
-};
-
#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2)
-/* Can be expanded to include events, etc.*/
-struct rapl_domain_data {
- u64 primitives[NR_RAPL_PRIMITIVES];
- unsigned long timestamp;
-};
-
struct msrl_action {
u32 msr_no;
u64 clear_mask;
@@ -138,60 +89,12 @@ struct msrl_action {
#define DOMAIN_STATE_POWER_LIMIT_SET BIT(1)
#define DOMAIN_STATE_BIOS_LOCKED BIT(2)
-#define NR_POWER_LIMITS (2)
-struct rapl_power_limit {
- struct powercap_zone_constraint *constraint;
- int prim_id; /* primitive ID used to enable */
- struct rapl_domain *domain;
- const char *name;
- u64 last_power_limit;
-};
-
static const char pl1_name[] = "long_term";
static const char pl2_name[] = "short_term";
-struct rapl_package;
-struct rapl_domain {
- const char *name;
- enum rapl_domain_type id;
- int regs[RAPL_DOMAIN_REG_MAX];
- struct powercap_zone power_zone;
- struct rapl_domain_data rdd;
- struct rapl_power_limit rpl[NR_POWER_LIMITS];
- u64 attr_map; /* track capabilities */
- unsigned int state;
- unsigned int domain_energy_unit;
- struct rapl_package *rp;
-};
#define power_zone_to_rapl_domain(_zone) \
container_of(_zone, struct rapl_domain, power_zone)
-/* maximum rapl package domain name: package-%d-die-%d */
-#define PACKAGE_DOMAIN_NAME_LENGTH 30
-
-
-/* Each rapl package contains multiple domains, these are the common
- * data across RAPL domains within a package.
- */
-struct rapl_package {
- unsigned int id; /* logical die id, equals physical 1-die systems */
- unsigned int nr_domains;
- unsigned long domain_map; /* bit map of active domains */
- unsigned int power_unit;
- unsigned int energy_unit;
- unsigned int time_unit;
- struct rapl_domain *domains; /* array of domains, sized at runtime */
- struct powercap_zone *power_zone; /* keep track of parent zone */
- unsigned long power_limit_irq; /* keep track of package power limit
- * notify interrupt enable status.
- */
- struct list_head plist;
- int lead_cpu; /* one active cpu per package for access */
- /* Track active cpus */
- struct cpumask cpumask;
- char name[PACKAGE_DOMAIN_NAME_LENGTH];
-};
-
struct rapl_defaults {
u8 floor_freq_reg_addr;
int (*check_unit)(struct rapl_package *rp, int cpu);
diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h
new file mode 100644
index 0000000..9471603
--- /dev/null
+++ b/include/linux/intel_rapl.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Data types and headers for RAPL support
+ *
+ * Copyright (C) 2019 Intel Corporation.
+ *
+ * Author: Zhang Rui <rui.zhang@intel.com>
+ */
+
+#ifndef __INTEL_RAPL_H__
+#define __INTEL_RAPL_H__
+
+#include <linux/types.h>
+#include <linux/powercap.h>
+
+enum rapl_domain_type {
+ RAPL_DOMAIN_PACKAGE, /* entire package/socket */
+ RAPL_DOMAIN_PP0, /* core power plane */
+ RAPL_DOMAIN_PP1, /* graphics uncore */
+ RAPL_DOMAIN_DRAM, /* DRAM control_type */
+ RAPL_DOMAIN_PLATFORM, /* PSys control_type */
+ RAPL_DOMAIN_MAX,
+};
+
+enum rapl_domain_reg_id {
+ RAPL_DOMAIN_REG_LIMIT,
+ RAPL_DOMAIN_REG_STATUS,
+ RAPL_DOMAIN_REG_PERF,
+ RAPL_DOMAIN_REG_POLICY,
+ RAPL_DOMAIN_REG_INFO,
+ RAPL_DOMAIN_REG_MAX,
+};
+
+struct rapl_package;
+
+enum rapl_primitives {
+ ENERGY_COUNTER,
+ POWER_LIMIT1,
+ POWER_LIMIT2,
+ FW_LOCK,
+
+ PL1_ENABLE, /* power limit 1, aka long term */
+ PL1_CLAMP, /* allow frequency to go below OS request */
+ PL2_ENABLE, /* power limit 2, aka short term, instantaneous */
+ PL2_CLAMP,
+
+ TIME_WINDOW1, /* long term */
+ TIME_WINDOW2, /* short term */
+ THERMAL_SPEC_POWER,
+ MAX_POWER,
+
+ MIN_POWER,
+ MAX_TIME_WINDOW,
+ THROTTLED_TIME,
+ PRIORITY_LEVEL,
+
+ /* below are not raw primitive data */
+ AVERAGE_POWER,
+ NR_RAPL_PRIMITIVES,
+};
+
+struct rapl_domain_data {
+ u64 primitives[NR_RAPL_PRIMITIVES];
+ unsigned long timestamp;
+};
+
+#define NR_POWER_LIMITS (2)
+struct rapl_power_limit {
+ struct powercap_zone_constraint *constraint;
+ int prim_id; /* primitive ID used to enable */
+ struct rapl_domain *domain;
+ const char *name;
+ u64 last_power_limit;
+};
+
+struct rapl_package;
+
+struct rapl_domain {
+ const char *name;
+ enum rapl_domain_type id;
+ int regs[RAPL_DOMAIN_REG_MAX];
+ struct powercap_zone power_zone;
+ struct rapl_domain_data rdd;
+ struct rapl_power_limit rpl[NR_POWER_LIMITS];
+ u64 attr_map; /* track capabilities */
+ unsigned int state;
+ unsigned int domain_energy_unit;
+ struct rapl_package *rp;
+};
+
+/* maximum rapl package domain name: package-%d-die-%d */
+#define PACKAGE_DOMAIN_NAME_LENGTH 30
+
+struct rapl_package {
+ unsigned int id; /* logical die id, equals physical 1-die systems */
+ unsigned int nr_domains;
+ unsigned long domain_map; /* bit map of active domains */
+ unsigned int power_unit;
+ unsigned int energy_unit;
+ unsigned int time_unit;
+ struct rapl_domain *domains; /* array of domains, sized at runtime */
+ struct powercap_zone *power_zone; /* keep track of parent zone */
+ unsigned long power_limit_irq; /* keep track of package power limit
+ * notify interrupt enable status.
+ */
+ struct list_head plist;
+ int lead_cpu; /* one active cpu per package for access */
+ /* Track active cpus */
+ struct cpumask cpumask;
+ char name[PACKAGE_DOMAIN_NAME_LENGTH];
+};
+
+#endif /* __INTEL_RAPL_H__ */
--
2.7.4
^ permalink raw reply related
* [PATCH V2 00/13] intel_rapl: RAPL abstraction and MMIO RAPL support
From: Zhang Rui @ 2019-07-04 16:34 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
Besideis MSR interface, RAPL can also be controlled via the MMIO interface,
by accessing the MCHBar registers exposed by the processor thermal device.
Currently, we only have RAPL MSR interface in Linux kernel, this brings
problems on some platforms that BIOS performs a low power limits via the
MMIO interface by default. This results in poor system performance,
and there is no way for us to change the MMIO MSR setting in Linux.
To fix this, RAPL MMIO interface support is introduced in this patch set.
Patch 1/13 to patch 11/13 abstract the RAPL code, and move all the shared
code into a separate file, intel_rapl_common.c, so that it can be used
by both MSR and MMIO interfaces.
Patch 12/13 introduced RAPL support via MMIO registers, exposed by the
processor thermal devices.
Patch 13/13 fixes a module autoloading issue found later.
The patch series has been tested on Dell XPS 9360, a SKL platform.
Note that this patch series are based on the -tip tree, which contains the
latest RAPL changes for multi-die support.
Changes in V2:
- add kerneldoc for struct rapl_if_priv.
- use intel_rapl_msr.c for RAPL MSR I/F driver, instead of intel_rapl.c.
- changelog and coding style update.
thanks,
rui
^ permalink raw reply
* [PATCH V2 05/13] intel_rapl: abstract register address
From: Zhang Rui @ 2019-07-04 16:34 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1562258085-3165-1-git-send-email-rui.zhang@intel.com>
MSR and MMIO RAPL interface have different sets of registers, thus the
RAPL register address should be obtained from interface specific
structure, i.e. struct rapl_if_private, instead.
Reviewed-and-tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 73 +++++++++++++++++++------------------------
include/linux/intel_rapl.h | 4 +++
2 files changed, 37 insertions(+), 40 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index e05d92d..9f22aed 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -76,7 +76,19 @@ enum unit_type {
};
/* private data for RAPL MSR Interface */
-static struct rapl_if_priv rapl_msr_priv;
+static struct rapl_if_priv rapl_msr_priv = {
+ .reg_unit = MSR_RAPL_POWER_UNIT,
+ .regs[RAPL_DOMAIN_PACKAGE] = {
+ MSR_PKG_POWER_LIMIT, MSR_PKG_ENERGY_STATUS, MSR_PKG_PERF_STATUS, 0, MSR_PKG_POWER_INFO },
+ .regs[RAPL_DOMAIN_PP0] = {
+ MSR_PP0_POWER_LIMIT, MSR_PP0_ENERGY_STATUS, 0, MSR_PP0_POLICY, 0 },
+ .regs[RAPL_DOMAIN_PP1] = {
+ MSR_PP1_POWER_LIMIT, MSR_PP1_ENERGY_STATUS, 0, MSR_PP1_POLICY, 0 },
+ .regs[RAPL_DOMAIN_DRAM] = {
+ MSR_DRAM_POWER_LIMIT, MSR_DRAM_ENERGY_STATUS, MSR_DRAM_PERF_STATUS, 0, MSR_DRAM_POWER_INFO },
+ .regs[RAPL_DOMAIN_PLATFORM] = {
+ MSR_PLATFORM_POWER_LIMIT, MSR_PLATFORM_ENERGY_STATUS, 0, 0, 0},
+};
/* per domain data, some are optional */
#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2)
@@ -541,15 +553,17 @@ static void rapl_init_domains(struct rapl_package *rp)
for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
unsigned int mask = rp->domain_map & (1 << i);
+
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = rp->priv->regs[i][RAPL_DOMAIN_REG_LIMIT];
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = rp->priv->regs[i][RAPL_DOMAIN_REG_STATUS];
+ rd->regs[RAPL_DOMAIN_REG_PERF] = rp->priv->regs[i][RAPL_DOMAIN_REG_PERF];
+ rd->regs[RAPL_DOMAIN_REG_POLICY] = rp->priv->regs[i][RAPL_DOMAIN_REG_POLICY];
+ rd->regs[RAPL_DOMAIN_REG_INFO] = rp->priv->regs[i][RAPL_DOMAIN_REG_INFO];
+
switch (mask) {
case BIT(RAPL_DOMAIN_PACKAGE):
rd->name = rapl_domain_names[RAPL_DOMAIN_PACKAGE];
rd->id = RAPL_DOMAIN_PACKAGE;
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PKG_POWER_LIMIT;
- rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PKG_ENERGY_STATUS;
- rd->regs[RAPL_DOMAIN_REG_PERF] = MSR_PKG_PERF_STATUS;
- rd->regs[RAPL_DOMAIN_REG_POLICY] = 0;
- rd->regs[RAPL_DOMAIN_REG_INFO] = MSR_PKG_POWER_INFO;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
@@ -558,33 +572,18 @@ static void rapl_init_domains(struct rapl_package *rp)
case BIT(RAPL_DOMAIN_PP0):
rd->name = rapl_domain_names[RAPL_DOMAIN_PP0];
rd->id = RAPL_DOMAIN_PP0;
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PP0_POWER_LIMIT;
- rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PP0_ENERGY_STATUS;
- rd->regs[RAPL_DOMAIN_REG_PERF] = 0;
- rd->regs[RAPL_DOMAIN_REG_POLICY] = MSR_PP0_POLICY;
- rd->regs[RAPL_DOMAIN_REG_INFO] = 0;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
break;
case BIT(RAPL_DOMAIN_PP1):
rd->name = rapl_domain_names[RAPL_DOMAIN_PP1];
rd->id = RAPL_DOMAIN_PP1;
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PP1_POWER_LIMIT;
- rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PP1_ENERGY_STATUS;
- rd->regs[RAPL_DOMAIN_REG_PERF] = 0;
- rd->regs[RAPL_DOMAIN_REG_POLICY] = MSR_PP1_POLICY;
- rd->regs[RAPL_DOMAIN_REG_INFO] = 0;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
break;
case BIT(RAPL_DOMAIN_DRAM):
rd->name = rapl_domain_names[RAPL_DOMAIN_DRAM];
rd->id = RAPL_DOMAIN_DRAM;
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_DRAM_POWER_LIMIT;
- rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_DRAM_ENERGY_STATUS;
- rd->regs[RAPL_DOMAIN_REG_PERF] = MSR_DRAM_PERF_STATUS;
- rd->regs[RAPL_DOMAIN_REG_POLICY] = 0;
- rd->regs[RAPL_DOMAIN_REG_INFO] = MSR_DRAM_POWER_INFO;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->domain_energy_unit =
@@ -806,9 +805,9 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
u64 msr_val;
u32 value;
- if (rdmsrl_safe_on_cpu(cpu, MSR_RAPL_POWER_UNIT, &msr_val)) {
+ if (rdmsrl_safe_on_cpu(cpu, rp->priv->reg_unit, &msr_val)) {
pr_err("Failed to read power unit MSR 0x%x on CPU %d, exit.\n",
- MSR_RAPL_POWER_UNIT, cpu);
+ rp->priv->reg_unit, cpu);
return -ENODEV;
}
@@ -832,9 +831,9 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu)
u64 msr_val;
u32 value;
- if (rdmsrl_safe_on_cpu(cpu, MSR_RAPL_POWER_UNIT, &msr_val)) {
+ if (rdmsrl_safe_on_cpu(cpu, rp->priv->reg_unit, &msr_val)) {
pr_err("Failed to read power unit MSR 0x%x on CPU %d, exit.\n",
- MSR_RAPL_POWER_UNIT, cpu);
+ rp->priv->reg_unit, cpu);
return -ENODEV;
}
value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
@@ -1173,10 +1172,10 @@ static int __init rapl_register_psys(void)
struct powercap_zone *power_zone;
u64 val;
- if (rdmsrl_safe_on_cpu(0, MSR_PLATFORM_ENERGY_STATUS, &val) || !val)
+ if (rdmsrl_safe_on_cpu(0, rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS], &val) || !val)
return -ENODEV;
- if (rdmsrl_safe_on_cpu(0, MSR_PLATFORM_POWER_LIMIT, &val) || !val)
+ if (rdmsrl_safe_on_cpu(0, rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT], &val) || !val)
return -ENODEV;
rd = kzalloc(sizeof(*rd), GFP_KERNEL);
@@ -1185,8 +1184,8 @@ static int __init rapl_register_psys(void)
rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM];
rd->id = RAPL_DOMAIN_PLATFORM;
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PLATFORM_POWER_LIMIT;
- rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PLATFORM_ENERGY_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
@@ -1218,23 +1217,17 @@ static int __init rapl_register_powercap(void)
return 0;
}
-static int rapl_check_domain(int cpu, int domain)
+static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
{
- unsigned msr;
+ u32 reg;
u64 val = 0;
switch (domain) {
case RAPL_DOMAIN_PACKAGE:
- msr = MSR_PKG_ENERGY_STATUS;
- break;
case RAPL_DOMAIN_PP0:
- msr = MSR_PP0_ENERGY_STATUS;
- break;
case RAPL_DOMAIN_PP1:
- msr = MSR_PP1_ENERGY_STATUS;
- break;
case RAPL_DOMAIN_DRAM:
- msr = MSR_DRAM_ENERGY_STATUS;
+ reg = rp->priv->regs[domain][RAPL_DOMAIN_REG_STATUS];
break;
case RAPL_DOMAIN_PLATFORM:
/* PSYS(PLATFORM) is not a CPU domain, so avoid printng error */
@@ -1246,7 +1239,7 @@ static int rapl_check_domain(int cpu, int domain)
/* make sure domain counters are available and contains non-zero
* values, otherwise skip it.
*/
- if (rdmsrl_safe_on_cpu(cpu, msr, &val) || !val)
+ if (rdmsrl_safe_on_cpu(cpu, reg, &val) || !val)
return -ENODEV;
return 0;
@@ -1293,7 +1286,7 @@ static int rapl_detect_domains(struct rapl_package *rp, int cpu)
for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
/* use physical package id to read counters */
- if (!rapl_check_domain(cpu, i)) {
+ if (!rapl_check_domain(cpu, i, rp)) {
rp->domain_map |= 1 << i;
pr_info("Found RAPL domain %s\n", rapl_domain_names[i]);
}
diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h
index 7bf1683e4..ec2c9e8 100644
--- a/include/linux/intel_rapl.h
+++ b/include/linux/intel_rapl.h
@@ -95,11 +95,15 @@ struct rapl_domain {
* @platform_rapl_domain: Optional. Some RAPL interface may have platform
* level RAPL control.
* @pcap_rapl_online: CPU hotplug state for each RAPL interface.
+ * @reg_unit: Register for getting energy/power/time unit.
+ * @regs: Register sets for different RAPL Domains.
*/
struct rapl_if_priv {
struct powercap_control_type *control_type;
struct rapl_domain *platform_rapl_domain;
enum cpuhp_state pcap_rapl_online;
+ u32 reg_unit;
+ u32 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
};
/* maximum rapl package domain name: package-%d-die-%d */
--
2.7.4
^ permalink raw reply related
* [PATCH V2 04/13] intel_rapl: introduce struct rapl_if_private
From: Zhang Rui @ 2019-07-04 16:34 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1562258085-3165-1-git-send-email-rui.zhang@intel.com>
Introduce a new structure, rapl_if_private, to save the private data
for different RAPL Interface.
Reviewed-and-tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 59 +++++++++++++++++++++----------------------
include/linux/intel_rapl.h | 15 +++++++++++
2 files changed, 44 insertions(+), 30 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index adb35ec..e05d92d 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -75,6 +75,9 @@ enum unit_type {
TIME_UNIT,
};
+/* private data for RAPL MSR Interface */
+static struct rapl_if_priv rapl_msr_priv;
+
/* per domain data, some are optional */
#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2)
@@ -155,17 +158,14 @@ static const char * const rapl_domain_names[] = {
"psys",
};
-static struct powercap_control_type *control_type; /* PowerCap Controller */
-static struct rapl_domain *platform_rapl_domain; /* Platform (PSys) domain */
-
/* caller to ensure CPU hotplug lock is held */
-static struct rapl_package *rapl_find_package_domain(int cpu)
+static struct rapl_package *rapl_find_package_domain(int cpu, struct rapl_if_priv *priv)
{
int id = topology_logical_die_id(cpu);
struct rapl_package *rp;
list_for_each_entry(rp, &rapl_packages, plist) {
- if (rp->id == id)
+ if (rp->id == id && rp->priv->control_type == priv->control_type)
return rp;
}
@@ -1090,12 +1090,12 @@ static void rapl_update_domain_data(struct rapl_package *rp)
static void rapl_unregister_powercap(void)
{
- if (platform_rapl_domain) {
- powercap_unregister_zone(control_type,
- &platform_rapl_domain->power_zone);
- kfree(platform_rapl_domain);
+ if (&rapl_msr_priv.platform_rapl_domain) {
+ powercap_unregister_zone(rapl_msr_priv.control_type,
+ &rapl_msr_priv.platform_rapl_domain->power_zone);
+ kfree(rapl_msr_priv.platform_rapl_domain);
}
- powercap_unregister_control_type(control_type);
+ powercap_unregister_control_type(rapl_msr_priv.control_type);
}
static int rapl_package_register_powercap(struct rapl_package *rp)
@@ -1113,7 +1113,7 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
nr_pl = find_nr_power_limit(rd);
pr_debug("register package domain %s\n", rp->name);
power_zone = powercap_register_zone(&rd->power_zone,
- control_type,
+ rp->priv->control_type,
rp->name, NULL,
&zone_ops[rd->id],
nr_pl,
@@ -1140,7 +1140,7 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
/* number of power limits per domain varies */
nr_pl = find_nr_power_limit(rd);
power_zone = powercap_register_zone(&rd->power_zone,
- control_type, rd->name,
+ rp->priv->control_type, rd->name,
rp->power_zone,
&zone_ops[rd->id], nr_pl,
&constraint_ops);
@@ -1161,7 +1161,7 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
*/
while (--rd >= rp->domains) {
pr_debug("unregister %s domain %s\n", rp->name, rd->name);
- powercap_unregister_zone(control_type, &rd->power_zone);
+ powercap_unregister_zone(rp->priv->control_type, &rd->power_zone);
}
return ret;
@@ -1191,9 +1191,9 @@ static int __init rapl_register_psys(void)
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
rd->rpl[1].name = pl2_name;
- rd->rp = rapl_find_package_domain(0);
+ rd->rp = rapl_find_package_domain(0, &rapl_msr_priv);
- power_zone = powercap_register_zone(&rd->power_zone, control_type,
+ power_zone = powercap_register_zone(&rd->power_zone, rapl_msr_priv.control_type,
"psys", NULL,
&zone_ops[RAPL_DOMAIN_PLATFORM],
2, &constraint_ops);
@@ -1203,17 +1203,17 @@ static int __init rapl_register_psys(void)
return PTR_ERR(power_zone);
}
- platform_rapl_domain = rd;
+ rapl_msr_priv.platform_rapl_domain = rd;
return 0;
}
static int __init rapl_register_powercap(void)
{
- control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
- if (IS_ERR(control_type)) {
+ rapl_msr_priv.control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
+ if (IS_ERR(rapl_msr_priv.control_type)) {
pr_debug("failed to register powercap control_type.\n");
- return PTR_ERR(control_type);
+ return PTR_ERR(rapl_msr_priv.control_type);
}
return 0;
}
@@ -1338,16 +1338,16 @@ static void rapl_remove_package(struct rapl_package *rp)
}
pr_debug("remove package, undo power limit on %s: %s\n",
rp->name, rd->name);
- powercap_unregister_zone(control_type, &rd->power_zone);
+ powercap_unregister_zone(rp->priv->control_type, &rd->power_zone);
}
/* do parent zone last */
- powercap_unregister_zone(control_type, &rd_package->power_zone);
+ powercap_unregister_zone(rp->priv->control_type, &rd_package->power_zone);
list_del(&rp->plist);
kfree(rp);
}
/* called from CPU hotplug notifier, hotplug lock held */
-static struct rapl_package *rapl_add_package(int cpu)
+static struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv)
{
int id = topology_logical_die_id(cpu);
struct rapl_package *rp;
@@ -1361,6 +1361,7 @@ static struct rapl_package *rapl_add_package(int cpu)
/* add the new package to the list */
rp->id = id;
rp->lead_cpu = cpu;
+ rp->priv = priv;
if (topology_max_die_per_package() > 1)
snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH,
@@ -1399,9 +1400,9 @@ static int rapl_cpu_online(unsigned int cpu)
{
struct rapl_package *rp;
- rp = rapl_find_package_domain(cpu);
+ rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp) {
- rp = rapl_add_package(cpu);
+ rp = rapl_add_package(cpu, &rapl_msr_priv);
if (IS_ERR(rp))
return PTR_ERR(rp);
}
@@ -1414,7 +1415,7 @@ static int rapl_cpu_down_prep(unsigned int cpu)
struct rapl_package *rp;
int lead_cpu;
- rp = rapl_find_package_domain(cpu);
+ rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp)
return 0;
@@ -1427,8 +1428,6 @@ static int rapl_cpu_down_prep(unsigned int cpu)
return 0;
}
-static enum cpuhp_state pcap_rapl_online;
-
static void power_limit_state_save(void)
{
struct rapl_package *rp;
@@ -1538,7 +1537,7 @@ static int __init rapl_init(void)
rapl_cpu_online, rapl_cpu_down_prep);
if (ret < 0)
goto err_unreg;
- pcap_rapl_online = ret;
+ rapl_msr_priv.pcap_rapl_online = ret;
/* Don't bail out if PSys is not supported */
rapl_register_psys();
@@ -1550,7 +1549,7 @@ static int __init rapl_init(void)
return 0;
err_unreg_all:
- cpuhp_remove_state(pcap_rapl_online);
+ cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
err_unreg:
rapl_unregister_powercap();
@@ -1560,7 +1559,7 @@ static int __init rapl_init(void)
static void __exit rapl_exit(void)
{
unregister_pm_notifier(&rapl_pm_notifier);
- cpuhp_remove_state(pcap_rapl_online);
+ cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
rapl_unregister_powercap();
}
diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h
index 9471603..7bf1683e4 100644
--- a/include/linux/intel_rapl.h
+++ b/include/linux/intel_rapl.h
@@ -88,6 +88,20 @@ struct rapl_domain {
struct rapl_package *rp;
};
+/**
+ * struct rapl_if_priv: private data for different RAPL interfaces
+ * @control_type: Each RAPL interface must have its own powercap
+ * control type.
+ * @platform_rapl_domain: Optional. Some RAPL interface may have platform
+ * level RAPL control.
+ * @pcap_rapl_online: CPU hotplug state for each RAPL interface.
+ */
+struct rapl_if_priv {
+ struct powercap_control_type *control_type;
+ struct rapl_domain *platform_rapl_domain;
+ enum cpuhp_state pcap_rapl_online;
+};
+
/* maximum rapl package domain name: package-%d-die-%d */
#define PACKAGE_DOMAIN_NAME_LENGTH 30
@@ -108,6 +122,7 @@ struct rapl_package {
/* Track active cpus */
struct cpumask cpumask;
char name[PACKAGE_DOMAIN_NAME_LENGTH];
+ struct rapl_if_priv *priv;
};
#endif /* __INTEL_RAPL_H__ */
--
2.7.4
^ permalink raw reply related
* [PATCH V2 02/13] intel_rapl: remove hardcoded register index
From: Zhang Rui @ 2019-07-04 16:34 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1562258085-3165-1-git-send-email-rui.zhang@intel.com>
enum rapl_domain_reg_id is defined for the RAPL registers for each RAPL
domain, thus use it whenever possible.
Reviewed-and-tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 44 +++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 45d5f22..9be9f20 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -642,11 +642,11 @@ static void rapl_init_domains(struct rapl_package *rp)
case BIT(RAPL_DOMAIN_PACKAGE):
rd->name = rapl_domain_names[RAPL_DOMAIN_PACKAGE];
rd->id = RAPL_DOMAIN_PACKAGE;
- rd->regs[0] = MSR_PKG_POWER_LIMIT;
- rd->regs[1] = MSR_PKG_ENERGY_STATUS;
- rd->regs[2] = MSR_PKG_PERF_STATUS;
- rd->regs[3] = 0;
- rd->regs[4] = MSR_PKG_POWER_INFO;
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PKG_POWER_LIMIT;
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PKG_ENERGY_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_PERF] = MSR_PKG_PERF_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_POLICY] = 0;
+ rd->regs[RAPL_DOMAIN_REG_INFO] = MSR_PKG_POWER_INFO;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
@@ -655,33 +655,33 @@ static void rapl_init_domains(struct rapl_package *rp)
case BIT(RAPL_DOMAIN_PP0):
rd->name = rapl_domain_names[RAPL_DOMAIN_PP0];
rd->id = RAPL_DOMAIN_PP0;
- rd->regs[0] = MSR_PP0_POWER_LIMIT;
- rd->regs[1] = MSR_PP0_ENERGY_STATUS;
- rd->regs[2] = 0;
- rd->regs[3] = MSR_PP0_POLICY;
- rd->regs[4] = 0;
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PP0_POWER_LIMIT;
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PP0_ENERGY_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_PERF] = 0;
+ rd->regs[RAPL_DOMAIN_REG_POLICY] = MSR_PP0_POLICY;
+ rd->regs[RAPL_DOMAIN_REG_INFO] = 0;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
break;
case BIT(RAPL_DOMAIN_PP1):
rd->name = rapl_domain_names[RAPL_DOMAIN_PP1];
rd->id = RAPL_DOMAIN_PP1;
- rd->regs[0] = MSR_PP1_POWER_LIMIT;
- rd->regs[1] = MSR_PP1_ENERGY_STATUS;
- rd->regs[2] = 0;
- rd->regs[3] = MSR_PP1_POLICY;
- rd->regs[4] = 0;
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PP1_POWER_LIMIT;
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PP1_ENERGY_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_PERF] = 0;
+ rd->regs[RAPL_DOMAIN_REG_POLICY] = MSR_PP1_POLICY;
+ rd->regs[RAPL_DOMAIN_REG_INFO] = 0;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
break;
case BIT(RAPL_DOMAIN_DRAM):
rd->name = rapl_domain_names[RAPL_DOMAIN_DRAM];
rd->id = RAPL_DOMAIN_DRAM;
- rd->regs[0] = MSR_DRAM_POWER_LIMIT;
- rd->regs[1] = MSR_DRAM_ENERGY_STATUS;
- rd->regs[2] = MSR_DRAM_PERF_STATUS;
- rd->regs[3] = 0;
- rd->regs[4] = MSR_DRAM_POWER_INFO;
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_DRAM_POWER_LIMIT;
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_DRAM_ENERGY_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_PERF] = MSR_DRAM_PERF_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_POLICY] = 0;
+ rd->regs[RAPL_DOMAIN_REG_INFO] = MSR_DRAM_POWER_INFO;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->domain_energy_unit =
@@ -1282,8 +1282,8 @@ static int __init rapl_register_psys(void)
rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM];
rd->id = RAPL_DOMAIN_PLATFORM;
- rd->regs[0] = MSR_PLATFORM_POWER_LIMIT;
- rd->regs[1] = MSR_PLATFORM_ENERGY_STATUS;
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = MSR_PLATFORM_POWER_LIMIT;
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = MSR_PLATFORM_ENERGY_STATUS;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
--
2.7.4
^ permalink raw reply related
* [PATCH V2 01/13] intel_rapl: use reg instead of msr
From: Zhang Rui @ 2019-07-04 16:34 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1562258085-3165-1-git-send-email-rui.zhang@intel.com>
To support both MSR and MMIO Interface, use 'reg' to discribe RAPL
registers instead of 'msr'.
Reviewed-and-tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 98 +++++++++++++++++++++----------------------
1 file changed, 49 insertions(+), 49 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 8692f6b..45d5f22 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -83,13 +83,13 @@ enum rapl_domain_type {
RAPL_DOMAIN_MAX,
};
-enum rapl_domain_msr_id {
- RAPL_DOMAIN_MSR_LIMIT,
- RAPL_DOMAIN_MSR_STATUS,
- RAPL_DOMAIN_MSR_PERF,
- RAPL_DOMAIN_MSR_POLICY,
- RAPL_DOMAIN_MSR_INFO,
- RAPL_DOMAIN_MSR_MAX,
+enum rapl_domain_reg_id {
+ RAPL_DOMAIN_REG_LIMIT,
+ RAPL_DOMAIN_REG_STATUS,
+ RAPL_DOMAIN_REG_PERF,
+ RAPL_DOMAIN_REG_POLICY,
+ RAPL_DOMAIN_REG_INFO,
+ RAPL_DOMAIN_REG_MAX,
};
/* per domain data, some are optional */
@@ -154,7 +154,7 @@ struct rapl_package;
struct rapl_domain {
const char *name;
enum rapl_domain_type id;
- int msrs[RAPL_DOMAIN_MSR_MAX];
+ int regs[RAPL_DOMAIN_REG_MAX];
struct powercap_zone power_zone;
struct rapl_domain_data rdd;
struct rapl_power_limit rpl[NR_POWER_LIMITS];
@@ -216,7 +216,7 @@ struct rapl_primitive_info {
const char *name;
u64 mask;
int shift;
- enum rapl_domain_msr_id id;
+ enum rapl_domain_reg_id id;
enum unit_type unit;
u32 flag;
};
@@ -642,11 +642,11 @@ static void rapl_init_domains(struct rapl_package *rp)
case BIT(RAPL_DOMAIN_PACKAGE):
rd->name = rapl_domain_names[RAPL_DOMAIN_PACKAGE];
rd->id = RAPL_DOMAIN_PACKAGE;
- rd->msrs[0] = MSR_PKG_POWER_LIMIT;
- rd->msrs[1] = MSR_PKG_ENERGY_STATUS;
- rd->msrs[2] = MSR_PKG_PERF_STATUS;
- rd->msrs[3] = 0;
- rd->msrs[4] = MSR_PKG_POWER_INFO;
+ rd->regs[0] = MSR_PKG_POWER_LIMIT;
+ rd->regs[1] = MSR_PKG_ENERGY_STATUS;
+ rd->regs[2] = MSR_PKG_PERF_STATUS;
+ rd->regs[3] = 0;
+ rd->regs[4] = MSR_PKG_POWER_INFO;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
@@ -655,33 +655,33 @@ static void rapl_init_domains(struct rapl_package *rp)
case BIT(RAPL_DOMAIN_PP0):
rd->name = rapl_domain_names[RAPL_DOMAIN_PP0];
rd->id = RAPL_DOMAIN_PP0;
- rd->msrs[0] = MSR_PP0_POWER_LIMIT;
- rd->msrs[1] = MSR_PP0_ENERGY_STATUS;
- rd->msrs[2] = 0;
- rd->msrs[3] = MSR_PP0_POLICY;
- rd->msrs[4] = 0;
+ rd->regs[0] = MSR_PP0_POWER_LIMIT;
+ rd->regs[1] = MSR_PP0_ENERGY_STATUS;
+ rd->regs[2] = 0;
+ rd->regs[3] = MSR_PP0_POLICY;
+ rd->regs[4] = 0;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
break;
case BIT(RAPL_DOMAIN_PP1):
rd->name = rapl_domain_names[RAPL_DOMAIN_PP1];
rd->id = RAPL_DOMAIN_PP1;
- rd->msrs[0] = MSR_PP1_POWER_LIMIT;
- rd->msrs[1] = MSR_PP1_ENERGY_STATUS;
- rd->msrs[2] = 0;
- rd->msrs[3] = MSR_PP1_POLICY;
- rd->msrs[4] = 0;
+ rd->regs[0] = MSR_PP1_POWER_LIMIT;
+ rd->regs[1] = MSR_PP1_ENERGY_STATUS;
+ rd->regs[2] = 0;
+ rd->regs[3] = MSR_PP1_POLICY;
+ rd->regs[4] = 0;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
break;
case BIT(RAPL_DOMAIN_DRAM):
rd->name = rapl_domain_names[RAPL_DOMAIN_DRAM];
rd->id = RAPL_DOMAIN_DRAM;
- rd->msrs[0] = MSR_DRAM_POWER_LIMIT;
- rd->msrs[1] = MSR_DRAM_ENERGY_STATUS;
- rd->msrs[2] = MSR_DRAM_PERF_STATUS;
- rd->msrs[3] = 0;
- rd->msrs[4] = MSR_DRAM_POWER_INFO;
+ rd->regs[0] = MSR_DRAM_POWER_LIMIT;
+ rd->regs[1] = MSR_DRAM_ENERGY_STATUS;
+ rd->regs[2] = MSR_DRAM_PERF_STATUS;
+ rd->regs[3] = 0;
+ rd->regs[4] = MSR_DRAM_POWER_INFO;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->domain_energy_unit =
@@ -736,37 +736,37 @@ static u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type,
static struct rapl_primitive_info rpi[] = {
/* name, mask, shift, msr index, unit divisor */
PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0,
- RAPL_DOMAIN_MSR_STATUS, ENERGY_UNIT, 0),
+ RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0),
PRIMITIVE_INFO_INIT(POWER_LIMIT1, POWER_LIMIT1_MASK, 0,
- RAPL_DOMAIN_MSR_LIMIT, POWER_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32,
- RAPL_DOMAIN_MSR_LIMIT, POWER_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(FW_LOCK, POWER_PP_LOCK, 31,
- RAPL_DOMAIN_MSR_LIMIT, ARBITRARY_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15,
- RAPL_DOMAIN_MSR_LIMIT, ARBITRARY_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL1_CLAMP, POWER_LIMIT1_CLAMP, 16,
- RAPL_DOMAIN_MSR_LIMIT, ARBITRARY_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL2_ENABLE, POWER_LIMIT2_ENABLE, 47,
- RAPL_DOMAIN_MSR_LIMIT, ARBITRARY_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48,
- RAPL_DOMAIN_MSR_LIMIT, ARBITRARY_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17,
- RAPL_DOMAIN_MSR_LIMIT, TIME_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49,
- RAPL_DOMAIN_MSR_LIMIT, TIME_UNIT, 0),
+ RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, POWER_INFO_THERMAL_SPEC_MASK,
- 0, RAPL_DOMAIN_MSR_INFO, POWER_UNIT, 0),
+ 0, RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(MAX_POWER, POWER_INFO_MAX_MASK, 32,
- RAPL_DOMAIN_MSR_INFO, POWER_UNIT, 0),
+ RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(MIN_POWER, POWER_INFO_MIN_MASK, 16,
- RAPL_DOMAIN_MSR_INFO, POWER_UNIT, 0),
+ RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, POWER_INFO_MAX_TIME_WIN_MASK, 48,
- RAPL_DOMAIN_MSR_INFO, TIME_UNIT, 0),
+ RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0,
- RAPL_DOMAIN_MSR_PERF, TIME_UNIT, 0),
+ RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, PP_POLICY_MASK, 0,
- RAPL_DOMAIN_MSR_POLICY, ARBITRARY_UNIT, 0),
+ RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0),
/* non-hardware */
PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, POWER_UNIT,
RAPL_PRIMITIVE_DERIVED),
@@ -798,7 +798,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
if (!rp->name || rp->flag & RAPL_PRIMITIVE_DUMMY)
return -EINVAL;
- msr = rd->msrs[rp->id];
+ msr = rd->regs[rp->id];
if (!msr)
return -EINVAL;
@@ -874,7 +874,7 @@ static int rapl_write_data_raw(struct rapl_domain *rd,
memset(&ma, 0, sizeof(ma));
- ma.msr_no = rd->msrs[rp->id];
+ ma.msr_no = rd->regs[rp->id];
ma.clear_mask = rp->mask;
ma.set_mask = bits;
@@ -1282,8 +1282,8 @@ static int __init rapl_register_psys(void)
rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM];
rd->id = RAPL_DOMAIN_PLATFORM;
- rd->msrs[0] = MSR_PLATFORM_POWER_LIMIT;
- rd->msrs[1] = MSR_PLATFORM_ENERGY_STATUS;
+ rd->regs[0] = MSR_PLATFORM_POWER_LIMIT;
+ rd->regs[1] = MSR_PLATFORM_ENERGY_STATUS;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
--
2.7.4
^ permalink raw reply related
* Aw: Re: [PATCH v2 2/7] rtc: mt6397: move some common definitions into rtc.h
From: Frank Wunderlich @ 2019-07-04 11:49 UTC (permalink / raw)
To: Matthias Brugger
Cc: Lee Jones, Rob Herring, Mark Rutland, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney, Josef Friedl
In-Reply-To: <62a4c4ce-7ab3-2f9d-a85e-be92340724a9@gmail.com>
> Still missing commit message. Describe here why you need to do that.
ok, added note that headers are reused in power-off-driver
https://github.com/frank-w/BPI-R2-4.14/commits/5.2-poweroff-mainline
> Please check your email setting as discussed offline. Otherwise your patches
> won't get accepted.
tested with webmailer where it looks good :(
seems the problem is only shown when imported to patchwork
using only git sendemail in ubuntu 18.4 without any mta (have sendmail not installed) and no changes made to git sendemail except authentication.
i see that (except cover-letter which is quoted-printable) all is send with
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
so i have forced git sendemail now to
sendemail.composeencoding UTF-8
if this does not work i can try instead
sendemail.transferEncoding 8bit
regards Frank
^ permalink raw reply
* Re: [patch 1/5] add cpuidle-haltpoll driver
From: Marcelo Tosatti @ 2019-07-04 11:13 UTC (permalink / raw)
To: Joao Martins
Cc: kvm-devel, Paolo Bonzini, Radim Krcmar, Andrea Arcangeli,
Rafael J. Wysocki, Peter Zijlstra, Wanpeng Li,
Konrad Rzeszutek Wilk, Raslan KarimAllah, Boris Ostrovsky,
Ankur Arora, Christian Borntraeger, linux-pm
In-Reply-To: <db95f834-0307-813a-323c-c5e23c90e3f5@oracle.com>
On Thu, Jul 04, 2019 at 10:16:47AM +0100, Joao Martins wrote:
> On 7/4/19 12:51 AM, Marcelo Tosatti wrote:
> > +++ linux-2.6-newcpuidle.git/drivers/cpuidle/cpuidle-haltpoll.c
> > @@ -0,0 +1,69 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * cpuidle driver for haltpoll governor.
> > + *
> > + * Copyright 2019 Red Hat, Inc. and/or its affiliates.
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2. See
> > + * the COPYING file in the top-level directory.
> > + *
> > + * Authors: Marcelo Tosatti <mtosatti@redhat.com>
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/cpuidle.h>
> > +#include <linux/module.h>
> > +#include <linux/sched/idle.h>
> > +#include <linux/kvm_para.h>
> > +
> > +static int default_enter_idle(struct cpuidle_device *dev,
> > + struct cpuidle_driver *drv, int index)
> > +{
> > + if (current_clr_polling_and_test()) {
> > + local_irq_enable();
> > + return index;
> > + }
> > + default_idle();
> > + return index;
> > +}
> > +
> > +static struct cpuidle_driver haltpoll_driver = {
> > + .name = "haltpoll",
> > + .owner = THIS_MODULE,
> > + .states = {
> > + { /* entry 0 is for polling */ },
> > + {
> > + .enter = default_enter_idle,
> > + .exit_latency = 1,
> > + .target_residency = 1,
> > + .power_usage = -1,
> > + .name = "haltpoll idle",
> > + .desc = "default architecture idle",
> > + },
> > + },
> > + .safe_state_index = 0,
> > + .state_count = 2,
> > +};
> > +
> > +static int __init haltpoll_init(void)
> > +{
> > + struct cpuidle_driver *drv = &haltpoll_driver;
> > +
> > + cpuidle_poll_state_init(drv);
> > +
> > + if (!kvm_para_available())
> > + return 0;
> > +
>
> Isn't this meant to return -ENODEV value if the module is meant to not load?
Well, the cpuidle drivers return an error only if registration fails.
> Also this check should probably be placed before initializing the poll state,
> provided poll state isn't used anyways if you're not a kvm guest.
Poll state init is only local variable initialization, it does not
have any external effect.
^ permalink raw reply
* Aw: Re: [PATCH v2 3/7] rtc: mt6397: improvements of rtc driver
From: Frank Wunderlich @ 2019-07-04 11:08 UTC (permalink / raw)
Cc: Lee Jones, Rob Herring, Mark Rutland, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney, Josef Friedl
In-Reply-To: <24975910-cb06-7faf-998f-def23ca0891f@gmail.com>
> Gesendet: Donnerstag, 04. Juli 2019 um 11:13 Uhr
> Von: "Matthias Brugger" <matthias.bgg@gmail.com>
> It's up to the maintainer but I don't like patches doing clean-ups together with
> adding support for new HW, although it's a trivial one here.
i can split again to have clean-up and new functions separated
^ permalink raw reply
* Aw: Re: [PATCH v2 5/7] power: reset: add driver for mt6323 poweroff
From: Frank Wunderlich @ 2019-07-04 11:06 UTC (permalink / raw)
To: Ran Bi
Cc: Lee Jones, Rob Herring, Mark Rutland, Matthias Brugger, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney, Josef Friedl,
Yingjoe Chen, Ran Bi
In-Reply-To: <1562234589.19751.16.camel@mhfsdcap03>
> Gesendet: Donnerstag, 04. Juli 2019 um 12:03 Uhr
> Von: "Ran Bi" <ran.bi@mediatek.com>
> We had implement MT8173 poweroff function in arm-trusted-firmware's PSCI
> plat_system_off() function. MT8173 SoC is using PMIC MT6397. (Ref:
> https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/mediatek/mt8173/plat_pm.c and https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/mediatek/mt8173/drivers/rtc) Do you think it's better to implement poweroff function into arm-trusted-firmware compared to hijack pm_poweroff() function in Kernel? Right now, we are doing the upstream of other PMIC chip like MT6358's poweroff function in arm-trusted-firmware too.
ATF imho only used for arm64, my board is 32bit armv7 and i (currently) do not boot up with ATF
regards Frank
^ permalink raw reply
* Re: [PATCH v2] PM / wakeup: show wakeup sources stats in sysfs
From: Rafael J. Wysocki @ 2019-07-04 10:31 UTC (permalink / raw)
To: Greg KH
Cc: Tri Vo, viresh.kumar, rafael, hridya, sspatil, kaleshsingh,
linux-kernel, linux-pm, kernel-team
In-Reply-To: <20190628151040.GA14074@kroah.com>
On Friday, June 28, 2019 5:10:40 PM CEST Greg KH wrote:
> On Thu, Jun 27, 2019 at 03:53:35PM -0700, Tri Vo wrote:
> > Userspace can use wakeup_sources debugfs node to plot history of suspend
> > blocking wakeup sources over device's boot cycle. This information can
> > then be used (1) for power-specific bug reporting and (2) towards
> > attributing battery consumption to specific processes over a period of
> > time.
> >
> > However, debugfs doesn't have stable ABI. For this reason, expose wakeup
> > sources statistics in sysfs under /sys/power/wakeup_sources/<name>/
> >
> > Embedding a struct kobject into struct wakeup_source changes lifetime
> > requirements on the latter. To that end, change deallocation of struct
> > wakeup_source using kfree to kobject_put().
> >
> > Change struct wakelock's wakeup_source member to a pointer to decouple
> > lifetimes of struct wakelock and struct wakeup_source for above reason.
> >
> > Introduce CONFIG_PM_SLEEP_STATS that enables/disables showing wakeup
> > source statistics in sysfs.
> >
> > Signed-off-by: Tri Vo <trong@android.com>
>
> Ok, this looks much better, but I don't like the use of a "raw" kobject
> here. It is much simpler, and less code, to use 'struct device'
> instead.
>
> As proof, I reworked the patch to do just that, and it saves over 50
> lines of .c code, which is always nice :)
Thanks for taking the time to do that!
> Attached below is the reworked code, along with the updated
> documentation file. It creates devices in a virtual class, and you can
> easily iterate over them all by looking in /sys/class/wakeup/.
That actually is nice - no need to add anything under /sys/power/.
> Note, I'm note quite sure you need all of the changes you made in
> kernel/power/wakelock.c when you make the structure contain a pointer to
> the wakeup source and not the structure itself, but I just went with it
> and got it all to build properly.
I'm not really sure about it either.
> Also note, I've not actually tested this at all, only built it, so I
> _strongly_ suggest that you test this to make sure it really works :)
>
> What do you think?
I agree with the direction. :-)
Cheers!
^ permalink raw reply
* Re: [PATCH] cpuidle/drivers/mobile: Add new governor for mobile/embedded systems
From: Rafael J. Wysocki @ 2019-07-04 10:14 UTC (permalink / raw)
To: Daniel Lezcano
Cc: rafael, linux-kernel, Thomas Gleixner, Greg Kroah-Hartman,
open list:CPU IDLE TIME MANAGEMENT FRAMEWORK
In-Reply-To: <20190620115826.4897-1-daniel.lezcano@linaro.org>
On Thursday, June 20, 2019 1:58:08 PM CEST Daniel Lezcano wrote:
> The objective is the same for all the governors: save energy, but at
> the end the governors menu, ladder and teo aim to improve the
> performances with an acceptable energy drop for some workloads which
> are identified for servers and desktops (with the help of a firmware).
>
> The ladder governor is designed for server with a periodic tick
> configuration.
>
> The menu governor does not behave nicely with the mobile platform and
> the energy saving for the multimedia workloads is worst than picking
> up randomly an idle state.
>
> The teo governor acts efficiently, it promotes shallower state for
> performances which is perfect for the servers / desktop but inadequate
> for mobile because the energy consumed is too high.
>
> It is very difficult to do changes in these governors for embedded
> systems without impacting performances on servers/desktops or ruin the
> optimizations for the workloads on these platforms.
>
> The mobile governor is a new governor targeting embedded systems
> running on battery where the energy saving has a higher priority than
> servers or desktops. This governor aims to save energy as much as
> possible but with a performance degradation tolerance.
>
> In this way, we can optimize the governor for specific mobile workload
> and more generally embedded systems without impacting other platforms.
>
> The mobile governor is built on top of the paradigm 'separate the wake
> up sources signals and analyze them'. Three categories of wake up
> signals are identified:
> - deterministic : timers
> - predictable : most of the devices interrupt
> - unpredictable : IPI rescheduling, random signals
>
> The latter needs an iterative approach and the help of the scheduler
> to give more input to the governor.
>
> The governor uses the irq timings where we predict the next interrupt
> occurrences on the current CPU and the next timer. It is well suited
> for mobile and more generally embedded systems where the interrupts
> are usually pinned on one CPU and where the power is more important
> than the performances.
>
> The multimedia applications on the embedded system spawn multiple
> threads which are migrated across the different CPUs and waking
> between them up. In order to catch this situation we have also to
> track the idle task rescheduling duration with a relative degree of
> confidence as the scheduler is involved in the task migrations. The
> resched information is in the scope of the governor via the reflect
> callback.
>
> The governor begins with a clean foundation basing the prediction on
> the irq behavior returned by the irq timings, the timers and the idle
> task rescheduling. The advantage of the approach is we have a full
> view of the wakeup sources as we identify them separately and then we
> can control the situation without relying on biased heuristics.
>
> This first iteration provides a basic prediction but improves on some
> mobile platforms better energy for better performance for multimedia
> workloads.
>
> The scheduling aspect will be optimized iteratively with non
> regression testing for previous identified workloads on an Android
> reference platform.
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Note that there are build issues reported by 0-day that need to be fixed.
Also, IMO this really should be documented better in the tree, not just in the changelog.
At least the use case to be covered by this governor should be clearly documented and
it would be good to describe the algorithm.
> ---
> drivers/cpuidle/Kconfig | 11 ++-
> drivers/cpuidle/governors/Makefile | 1 +
> drivers/cpuidle/governors/mobile.c | 151 +++++++++++++++++++++++++++++
> 3 files changed, 162 insertions(+), 1 deletion(-)
> create mode 100644 drivers/cpuidle/governors/mobile.c
>
> diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
> index a4ac31e4a58c..e2376d85e288 100644
> --- a/drivers/cpuidle/Kconfig
> +++ b/drivers/cpuidle/Kconfig
> @@ -5,7 +5,7 @@ config CPU_IDLE
> bool "CPU idle PM support"
> default y if ACPI || PPC_PSERIES
> select CPU_IDLE_GOV_LADDER if (!NO_HZ && !NO_HZ_IDLE)
> - select CPU_IDLE_GOV_MENU if (NO_HZ || NO_HZ_IDLE) && !CPU_IDLE_GOV_TEO
> + select CPU_IDLE_GOV_MENU if (NO_HZ || NO_HZ_IDLE) && !CPU_IDLE_GOV_TEO && !CPU_IDLE_GOV_MOBILE
> help
> CPU idle is a generic framework for supporting software-controlled
> idle processor power management. It includes modular cross-platform
> @@ -33,6 +33,15 @@ config CPU_IDLE_GOV_TEO
> Some workloads benefit from using it and it generally should be safe
> to use. Say Y here if you are not happy with the alternatives.
>
> +config CPU_IDLE_GOV_MOBILE
> + bool "Mobile governor"
> + select IRQ_TIMINGS
> + help
> + The mobile governor is based on irq timings measurements and
> + pattern research combined with the next timer. This governor
> + suits very well on embedded systems where the interrupts are
> + grouped on a single core and the power is the priority.
> +
> config DT_IDLE_STATES
> bool
>
> diff --git a/drivers/cpuidle/governors/Makefile b/drivers/cpuidle/governors/Makefile
> index 42f44cc610dd..f09da7178670 100644
> --- a/drivers/cpuidle/governors/Makefile
> +++ b/drivers/cpuidle/governors/Makefile
> @@ -6,3 +6,4 @@
> obj-$(CONFIG_CPU_IDLE_GOV_LADDER) += ladder.o
> obj-$(CONFIG_CPU_IDLE_GOV_MENU) += menu.o
> obj-$(CONFIG_CPU_IDLE_GOV_TEO) += teo.o
> +obj-$(CONFIG_CPU_IDLE_GOV_MOBILE) += mobile.o
> diff --git a/drivers/cpuidle/governors/mobile.c b/drivers/cpuidle/governors/mobile.c
> new file mode 100644
> index 000000000000..8fda0f9b960b
> --- /dev/null
> +++ b/drivers/cpuidle/governors/mobile.c
> @@ -0,0 +1,151 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019, Linaro Ltd
> + * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
> + */
> +#include <linux/cpuidle.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/tick.h>
> +#include <linux/interrupt.h>
> +#include <linux/sched/clock.h>
> +
> +struct mobile_device {
> + u64 idle_ema_avg;
> + u64 idle_total;
> + unsigned long last_jiffies;
> +};
> +
> +#define EMA_ALPHA_VAL 64
> +#define EMA_ALPHA_SHIFT 7
> +#define MAX_RESCHED_INTERVAL_MS 100
> +
> +static DEFINE_PER_CPU(struct mobile_device, mobile_devices);
> +
> +static int mobile_ema_new(s64 value, s64 ema_old)
> +{
> + if (likely(ema_old))
> + return ema_old + (((value - ema_old) * EMA_ALPHA_VAL) >>
> + EMA_ALPHA_SHIFT);
> + return value;
> +}
> +
> +static void mobile_reflect(struct cpuidle_device *dev, int index)
> +{
> + struct mobile_device *mobile_dev = this_cpu_ptr(&mobile_devices);
> + struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
> + struct cpuidle_state *s = &drv->states[index];
> + int residency;
> +
> + /*
> + * The idle task was not rescheduled since
> + * MAX_RESCHED_INTERVAL_MS, let's consider the duration is
> + * long enough to clear our stats.
> + */
> + if (time_after(jiffies, mobile_dev->last_jiffies +
> + msecs_to_jiffies(MAX_RESCHED_INTERVAL_MS)))
> + mobile_dev->idle_ema_avg = 0;
Why jiffies? Any particular reason?
> +
> + /*
> + * Sum all the residencies in order to compute the total
> + * duration of the idle task.
> + */
> + residency = dev->last_residency - s->exit_latency;
> + if (residency > 0)
> + mobile_dev->idle_total += residency;
> +
> + /*
> + * We exited the idle state with the need_resched() flag, the
> + * idle task will be rescheduled, so store the duration the
> + * idle task was scheduled in an exponential moving average and
> + * reset the total of the idle duration.
> + */
> + if (need_resched()) {
> + mobile_dev->idle_ema_avg = mobile_ema_new(mobile_dev->idle_total,
> + mobile_dev->idle_ema_avg);
> + mobile_dev->idle_total = 0;
> + mobile_dev->last_jiffies = jiffies;
> + }
> +}
> +
> +static int mobile_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
> + bool *stop_tick)
> +{
> + struct mobile_device *mobile_dev = this_cpu_ptr(&mobile_devices);
> + int latency_req = cpuidle_governor_latency_req(dev->cpu);
> + int i, index = 0;
> + ktime_t delta_next;
> + u64 now, irq_length, timer_length;
> + u64 idle_duration_us;
> +
> + /*
> + * Get the present time as reference for the next steps
> + */
> + now = local_clock();
> +
> + /*
> + * Get the next interrupt event giving the 'now' as a
> + * reference, if the next event appears to have already
> + * expired then we get the 'now' returned which ends up with a
> + * zero duration.
> + */
> + irq_length = irq_timings_next_event(now) - now;
> +
> + /*
> + * Get the timer duration before expiration.
> + */
This comment is rather redundant and the one below too. :-)
> + timer_length = ktime_to_ns(tick_nohz_get_sleep_length(&delta_next));
> +
> + /*
> + * Get the smallest duration between the timer and the irq next event.
> + */
> + idle_duration_us = min_t(u64, irq_length, timer_length) / NSEC_PER_USEC;
> +
> + /*
> + * Get the idle task duration average if the information is
> + * available.
IMO it would be good to explain this step in more detail, especially the purpose of it.
> + */
> + if (mobile_dev->idle_ema_avg)
> + idle_duration_us = min_t(u64, idle_duration_us,
> + mobile_dev->idle_ema_avg);
> +
> + for (i = 0; i < drv->state_count; i++) {
> + struct cpuidle_state *s = &drv->states[i];
> + struct cpuidle_state_usage *su = &dev->states_usage[i];
> +
> + if (s->disabled || su->disable)
> + continue;
> +
> + if (s->exit_latency > latency_req)
> + break;
> +
> + if (idle_duration_us > s->exit_latency)
> + idle_duration_us = idle_duration_us - s->exit_latency;
Why do you want this?
It only causes you to miss an opportunity to select a deeper state sometimes,
so what's the reason?
Moreover, I don't think you should update idle_duration_us here, as the updated
value will go to the next step if the check below doesn't trigger.
> +
> + if (s->target_residency > idle_duration_us)
> + break;
> +
> + index = i;
> + }
> +
> + if (!index)
> + *stop_tick = false;
Well, this means that the tick is stopped for all idle states deeper than state 0.
If there are any states between state 0 and the deepest one and they are below
the tick boundary, you may very well suffer the "powernightmares" problem
because of this.
> +
> + return index;
> +}
> +
> +static struct cpuidle_governor mobile_governor = {
> + .name = "mobile",
> + .rating = 20,
> + .select = mobile_select,
> + .reflect = mobile_reflect,
> +};
> +
> +static int __init init_governor(void)
> +{
> + irq_timings_enable();
> + return cpuidle_register_governor(&mobile_governor);
> +}
> +
> +postcore_initcall(init_governor);
>
^ permalink raw reply
* Re: [PATCH v2 5/7] power: reset: add driver for mt6323 poweroff
From: Ran Bi @ 2019-07-04 10:03 UTC (permalink / raw)
To: Frank Wunderlich
Cc: Lee Jones, Rob Herring, Mark Rutland, Matthias Brugger, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney, Josef Friedl,
Yingjoe Chen, Sean Wang, Ran Bi
In-Reply-To: <20190703164822.17924-6-frank-w@public-files.de>
On Wed, 2019-07-03 at 18:48 +0200, Frank Wunderlich wrote:
> From: Josef Friedl <josef.friedl@speed.at>
>
> Suggested-by: Frank Wunderlich <frank-w@public-files.de>
> Signed-off-by: Josef Friedl <josef.friedl@speed.at>
> Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
> ---
> drivers/power/reset/Kconfig | 10 +++
> drivers/power/reset/Makefile | 1 +
> drivers/power/reset/mt6323-poweroff.c | 97 +++++++++++++++++++++++++++
> include/linux/mfd/mt6397/core.h | 2 +
> 4 files changed, 110 insertions(+)
> create mode 100644 drivers/power/reset/mt6323-poweroff.c
>
> --
> 2.17.1
>
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index 980951dff834..492678e22088 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -140,6 +140,16 @@ config POWER_RESET_LTC2952
> This driver supports an external powerdown trigger and board power
> down via the LTC2952. Bindings are made in the device tree.
>
> +config POWER_RESET_MT6323
> + bool "MediaTek MT6323 power-off driver"
> + depends on MFD_MT6397
> + help
> + The power-off driver is responsible for externally shutdown down
> + the power of a remote MediaTek SoC MT6323 is connected to through
> + controlling a tiny circuit BBPU inside MT6323 RTC.
> +
> + Say Y if you have a board where MT6323 could be found.
> +
> config POWER_RESET_QNAP
> bool "QNAP power-off driver"
> depends on OF_GPIO && PLAT_ORION
> diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
> index 0aebee954ac1..94eaceb01d66 100644
> --- a/drivers/power/reset/Makefile
> +++ b/drivers/power/reset/Makefile
> @@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
> obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
> obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
> obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
> +obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
> obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o
> obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
> obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
> diff --git a/drivers/power/reset/mt6323-poweroff.c b/drivers/power/reset/mt6323-poweroff.c
> new file mode 100644
> index 000000000000..1caf43d9e46d
> --- /dev/null
> +++ b/drivers/power/reset/mt6323-poweroff.c
> @@ -0,0 +1,97 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Power off through MediaTek PMIC
> + *
> + * Copyright (C) 2018 MediaTek Inc.
> + *
> + * Author: Sean Wang <sean.wang@mediatek.com>
> + *
> + */
> +
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/mt6397/core.h>
> +#include <linux/mfd/mt6397/rtc.h>
> +
> +struct mt6323_pwrc {
> + struct device *dev;
> + struct regmap *regmap;
> + u32 base;
> +};
> +
> +static struct mt6323_pwrc *mt_pwrc;
> +
> +static void mt6323_do_pwroff(void)
> +{
> + struct mt6323_pwrc *pwrc = mt_pwrc;
> + unsigned int val;
> + int ret;
> +
> + regmap_write(pwrc->regmap, pwrc->base + RTC_BBPU, RTC_BBPU_KEY);
> + regmap_write(pwrc->regmap, pwrc->base + RTC_WRTGR, 1);
> +
> + ret = regmap_read_poll_timeout(pwrc->regmap,
> + pwrc->base + RTC_BBPU, val,
> + !(val & RTC_BBPU_CBUSY),
> + MTK_RTC_POLL_DELAY_US,
> + MTK_RTC_POLL_TIMEOUT);
> + if (ret)
> + dev_err(pwrc->dev, "failed to write BBPU: %d\n", ret);
> +
> + /* Wait some time until system down, otherwise, notice with a warn */
> + mdelay(1000);
> +
> + WARN_ONCE(1, "Unable to power off system\n");
> +}
> +
> +static int mt6323_pwrc_probe(struct platform_device *pdev)
> +{
> + struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
> + struct mt6323_pwrc *pwrc;
> + struct resource *res;
> +
> + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
> + if (!pwrc)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + pwrc->base = res->start;
> + pwrc->regmap = mt6397_chip->regmap;
> + pwrc->dev = &pdev->dev;
> + mt_pwrc = pwrc;
> +
> + pm_power_off = &mt6323_do_pwroff;
We had implement MT8173 poweroff function in arm-trusted-firmware's PSCI
plat_system_off() function. MT8173 SoC is using PMIC MT6397. (Ref:
https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/mediatek/mt8173/plat_pm.c and https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/mediatek/mt8173/drivers/rtc) Do you think it's better to implement poweroff function into arm-trusted-firmware compared to hijack pm_poweroff() function in Kernel? Right now, we are doing the upstream of other PMIC chip like MT6358's poweroff function in arm-trusted-firmware too.
> +
> + return 0;
> +}
> +
> +static int mt6323_pwrc_remove(struct platform_device *pdev)
> +{
> + if (pm_power_off == &mt6323_do_pwroff)
> + pm_power_off = NULL;
> +
> + return 0;
> +}
> +
> +static const struct of_device_id mt6323_pwrc_dt_match[] = {
> + { .compatible = "mediatek,mt6323-pwrc" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, mt6323_pwrc_dt_match);
> +
> +static struct platform_driver mt6323_pwrc_driver = {
> + .probe = mt6323_pwrc_probe,
> + .remove = mt6323_pwrc_remove,
> + .driver = {
> + .name = "mt6323-pwrc",
> + .of_match_table = mt6323_pwrc_dt_match,
> + },
> +};
> +
> +module_platform_driver(mt6323_pwrc_driver);
> +
> +MODULE_DESCRIPTION("Poweroff driver for MT6323 PMIC");
> +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h
> index 25a95e72179b..652da61e3711 100644
> --- a/include/linux/mfd/mt6397/core.h
> +++ b/include/linux/mfd/mt6397/core.h
> @@ -7,6 +7,8 @@
> #ifndef __MFD_MT6397_CORE_H__
> #define __MFD_MT6397_CORE_H__
>
> +#include <linux/mutex.h>
> +
> enum mt6397_irq_numbers {
> MT6397_IRQ_SPKL_AB = 0,
^ permalink raw reply
* [PATCH 1/4] dt-bindings: thermal: imx8mm-thermal: Add binding doc for i.MX8MM
From: Anson.Huang @ 2019-07-04 9:13 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
shawnguo, s.hauer, kernel, festevam, catalin.marinas, will,
leonard.crestez, daniel.baluta, ping.bai, olof, maxime.ripard,
jagan, bjorn.andersson, dinguyen, enric.balletbo,
marcin.juszkiewicz, linux-pm, devicetree, linux-arm-kernel,
linux-kernel
Cc: Linux-imx
In-Reply-To: <20190704091313.9516-1-Anson.Huang@nxp.com>
From: Anson Huang <Anson.Huang@nxp.com>
Add thermal binding doc for Freescale's i.MX8MM Thermal Monitoring Unit.
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
.../devicetree/bindings/thermal/imx8mm-thermal.txt | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
diff --git a/Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt b/Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
new file mode 100644
index 0000000..d09ae82
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
@@ -0,0 +1,15 @@
+* Thermal Monitoring Unit (TMU) on Freescale i.MX8MM SoC
+
+Required properties:
+- compatible : Must be "fsl,imx8mm-tmu".
+- reg : Address range of TMU registers.
+- clocks : TMU's clock source.
+- #thermal-sensor-cells : Should be 0. See ./thermal.txt for a description.
+
+Example:
+tmu: tmu@30260000 {
+ compatible = "fsl,imx8mm-tmu";
+ reg = <0x30260000 0x10000>;
+ clocks = <&clk IMX8MM_CLK_TMU_ROOT>;
+ #thermal-sensor-cells = <0>;
+};
--
2.7.4
^ permalink raw reply related
* [PATCH 2/4] thermal: imx8mm: Add support for i.MX8MM thermal monitoring unit
From: Anson.Huang @ 2019-07-04 9:13 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
shawnguo, s.hauer, kernel, festevam, catalin.marinas, will,
leonard.crestez, daniel.baluta, ping.bai, olof, maxime.ripard,
jagan, bjorn.andersson, dinguyen, enric.balletbo,
marcin.juszkiewicz, linux-pm, devicetree, linux-arm-kernel,
linux-kernel
Cc: Linux-imx
In-Reply-To: <20190704091313.9516-1-Anson.Huang@nxp.com>
From: Anson Huang <Anson.Huang@nxp.com>
i.MX8MM has a thermal monitoring unit(TMU) inside, it ONLY has one
sensor for CPU, add support for reading immediate temperature of
this sensor.
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
drivers/thermal/Kconfig | 10 +++
drivers/thermal/Makefile | 1 +
drivers/thermal/imx8mm_thermal.c | 134 +++++++++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 drivers/thermal/imx8mm_thermal.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 454cbe5..d1663cd 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -244,6 +244,16 @@ config IMX_SC_THERMAL
sensor. It supports one critical trip point and one
passive trip point for each thermal sensor.
+config IMX8MM_THERMAL
+ tristate "Temperature sensor driver for Freescale i.MX8MM SoC"
+ depends on ARCH_MXC
+ depends on OF
+ help
+ Support for Thermal Monitoring Unit (TMU) found on Freescale i.MX8MM SoC.
+ It supports one critical trip point and one passive trip point. The
+ cpufreq is used as the cooling device to throttle CPUs when the passive
+ trip is crossed.
+
config MAX77620_THERMAL
tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
depends on MFD_MAX77620
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 717a1ba..a397d4d 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
+obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_thermal.o
obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o
diff --git a/drivers/thermal/imx8mm_thermal.c b/drivers/thermal/imx8mm_thermal.c
new file mode 100644
index 0000000..04f8a8f
--- /dev/null
+++ b/drivers/thermal/imx8mm_thermal.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define TER 0x0 /* TMU enable */
+#define TRITSR 0x20 /* TMU immediate temp */
+
+#define TER_EN BIT(31)
+#define TRITSR_VAL_MASK 0xff
+
+#define TEMP_LOW_LIMIT 10
+
+struct imx8mm_tmu {
+ struct thermal_zone_device *tzd;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static int tmu_get_temp(void *data, int *temp)
+{
+ struct imx8mm_tmu *tmu = data;
+ u32 val;
+
+ /* the temp sensor need about 1ms to finish the measurement */
+ usleep_range(1000, 2000);
+
+ val = readl_relaxed(tmu->base + TRITSR) & TRITSR_VAL_MASK;
+ if (val < TEMP_LOW_LIMIT)
+ return -EAGAIN;
+
+ *temp = val * 1000;
+
+ return 0;
+}
+
+static struct thermal_zone_of_device_ops tmu_tz_ops = {
+ .get_temp = tmu_get_temp,
+};
+
+static int imx8mm_tmu_probe(struct platform_device *pdev)
+{
+ struct imx8mm_tmu *tmu;
+ u32 val;
+ int ret;
+
+ tmu = devm_kzalloc(&pdev->dev, sizeof(struct imx8mm_tmu), GFP_KERNEL);
+ if (!tmu)
+ return -ENOMEM;
+
+ tmu->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(tmu->base))
+ return PTR_ERR(tmu->base);
+
+ tmu->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tmu->clk)) {
+ ret = PTR_ERR(tmu->clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to get tmu clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(tmu->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret);
+ return ret;
+ }
+
+ tmu->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
+ tmu, &tmu_tz_ops);
+ if (IS_ERR(tmu->tzd)) {
+ dev_err(&pdev->dev,
+ "failed to register thermal zone sensor: %d\n", ret);
+ return PTR_ERR(tmu->tzd);
+ }
+
+ platform_set_drvdata(pdev, tmu);
+
+ /* enable the monitor */
+ val = readl_relaxed(tmu->base + TER);
+ val |= TER_EN;
+ writel_relaxed(val, tmu->base + TER);
+
+ return 0;
+}
+
+static int imx8mm_tmu_remove(struct platform_device *pdev)
+{
+ struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
+ u32 val;
+
+ /* disable TMU */
+ val = readl_relaxed(tmu->base + TER);
+ val &= ~TER_EN;
+ writel_relaxed(val, tmu->base + TER);
+
+ clk_disable_unprepare(tmu->clk);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id imx8mm_tmu_table[] = {
+ { .compatible = "fsl,imx8mm-tmu", },
+ { },
+};
+
+static struct platform_driver imx8mm_tmu = {
+ .driver = {
+ .name = "i.mx8mm_thermal",
+ .of_match_table = imx8mm_tmu_table,
+ },
+ .probe = imx8mm_tmu_probe,
+ .remove = imx8mm_tmu_remove,
+};
+module_platform_driver(imx8mm_tmu);
+
+MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
+MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH 4/4] arm64: dts: imx8mm: Add thermal zone support
From: Anson.Huang @ 2019-07-04 9:13 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
shawnguo, s.hauer, kernel, festevam, catalin.marinas, will,
leonard.crestez, daniel.baluta, ping.bai, olof, maxime.ripard,
jagan, bjorn.andersson, dinguyen, enric.balletbo,
marcin.juszkiewicz, linux-pm, devicetree, linux-arm-kernel,
linux-kernel
Cc: Linux-imx
In-Reply-To: <20190704091313.9516-1-Anson.Huang@nxp.com>
From: Anson Huang <Anson.Huang@nxp.com>
Add thermal zone and tmu node to support i.MX8MM thermal
driver, ONLY cpu thermal zone is supported, and cpu cooling
is also added.
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mm.dtsi | 43 +++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
index 3a62407..1870c89 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
@@ -70,6 +70,7 @@
nvmem-cells = <&cpu_speed_grade>;
nvmem-cell-names = "speed_grade";
cpu-idle-states = <&cpu_sleep_wait>;
+ #cooling-cells = <2>;
};
A53_1: cpu@1 {
@@ -82,6 +83,7 @@
next-level-cache = <&A53_L2>;
operating-points-v2 = <&a53_opp_table>;
cpu-idle-states = <&cpu_sleep_wait>;
+ #cooling-cells = <2>;
};
A53_2: cpu@2 {
@@ -94,6 +96,7 @@
next-level-cache = <&A53_L2>;
operating-points-v2 = <&a53_opp_table>;
cpu-idle-states = <&cpu_sleep_wait>;
+ #cooling-cells = <2>;
};
A53_3: cpu@3 {
@@ -106,6 +109,7 @@
next-level-cache = <&A53_L2>;
operating-points-v2 = <&a53_opp_table>;
cpu-idle-states = <&cpu_sleep_wait>;
+ #cooling-cells = <2>;
};
A53_L2: l2-cache0 {
@@ -209,6 +213,38 @@
arm,no-tick-in-suspend;
};
+ thermal-zones {
+ cpu-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <2000>;
+ thermal-sensors = <&tmu>;
+ trips {
+ cpu_alert0: trip0 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu_crit0: trip1 {
+ temperature = <95000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert0>;
+ cooling-device =
+ <&A53_0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&A53_1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&A53_2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&A53_3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+ };
+
usbphynop1: usbphynop1 {
compatible = "usb-nop-xceiv";
clocks = <&clk IMX8MM_CLK_USB_PHY_REF>;
@@ -368,6 +404,13 @@
gpio-ranges = <&iomuxc 0 119 30>;
};
+ tmu: tmu@30260000 {
+ compatible = "fsl,imx8mm-tmu";
+ reg = <0x30260000 0x10000>;
+ clocks = <&clk IMX8MM_CLK_TMU_ROOT>;
+ #thermal-sensor-cells = <0>;
+ };
+
wdog1: watchdog@30280000 {
compatible = "fsl,imx8mm-wdt", "fsl,imx21-wdt";
reg = <0x30280000 0x10000>;
--
2.7.4
^ permalink raw reply related
* [PATCH 3/4] arm64: defconfig: Enable CONFIG_IMX8MM_THERMAL as module
From: Anson.Huang @ 2019-07-04 9:13 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
shawnguo, s.hauer, kernel, festevam, catalin.marinas, will,
leonard.crestez, daniel.baluta, ping.bai, olof, maxime.ripard,
jagan, bjorn.andersson, dinguyen, enric.balletbo,
marcin.juszkiewicz, linux-pm, devicetree, linux-arm-kernel,
linux-kernel
Cc: Linux-imx
In-Reply-To: <20190704091313.9516-1-Anson.Huang@nxp.com>
From: Anson Huang <Anson.Huang@nxp.com>
Enable CONFIG_IMX8MM_THERMAL as module to support i.MX8MM
thermal driver.
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 126665f..eeedd3f 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -434,6 +434,7 @@ CONFIG_CPU_THERMAL=y
CONFIG_THERMAL_EMULATION=y
CONFIG_QORIQ_THERMAL=m
CONFIG_IMX_SC_THERMAL=m
+CONFIG_IMX8MM_THERMAL=m
CONFIG_ROCKCHIP_THERMAL=m
CONFIG_RCAR_THERMAL=y
CONFIG_RCAR_GEN3_THERMAL=y
--
2.7.4
^ permalink raw reply related
* [PATCH 0/4] Add support for i.MX8MM thermal sensor driver
From: Anson.Huang @ 2019-07-04 9:13 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
shawnguo, s.hauer, kernel, festevam, catalin.marinas, will,
leonard.crestez, daniel.baluta, ping.bai, olof, maxime.ripard,
jagan, bjorn.andersson, dinguyen, enric.balletbo,
marcin.juszkiewicz, linux-pm, devicetree, linux-arm-kernel,
linux-kernel
Cc: Linux-imx
From: Anson Huang <Anson.Huang@nxp.com>
i.MX8MM has a thermal monitor unit (TMU) inside, it ONLY has one sensor
for CPU, add support for temperature reading for CPU, cpu cooling is
also added.
This patch series is based on below i.MX SCU thermal patch series:
https://patchwork.kernel.org/patch/11000821/
Anson Huang (4):
dt-bindings: thermal: imx8mm-thermal: Add binding doc for i.MX8MM
thermal: imx8mm: Add support for i.MX8MM thermal monitoring unit
arm64: defconfig: Enable CONFIG_IMX8MM_THERMAL as module
arm64: dts: imx8mm: Add thermal zone support
.../devicetree/bindings/thermal/imx8mm-thermal.txt | 15 +++
arch/arm64/boot/dts/freescale/imx8mm.dtsi | 43 +++++++
arch/arm64/configs/defconfig | 1 +
drivers/thermal/Kconfig | 10 ++
drivers/thermal/Makefile | 1 +
drivers/thermal/imx8mm_thermal.c | 134 +++++++++++++++++++++
6 files changed, 204 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
create mode 100644 drivers/thermal/imx8mm_thermal.c
--
2.7.4
^ permalink raw reply
* [PATCH v3 1/3] cpuidle-powernv : forced wakeup for stop states
From: Abhishek Goel @ 2019-07-04 9:18 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, linux-pm
Cc: npiggin, rjw, daniel.lezcano, mpe, ego, dja, Abhishek Goel
In-Reply-To: <20190704091827.19555-1-huntbag@linux.vnet.ibm.com>
Currently, the cpuidle governors determine what idle state a idling CPU
should enter into based on heuristics that depend on the idle history on
that CPU. Given that no predictive heuristic is perfect, there are cases
where the governor predicts a shallow idle state, hoping that the CPU will
be busy soon. However, if no new workload is scheduled on that CPU in the
near future, the CPU may end up in the shallow state.
This is problematic, when the predicted state in the aforementioned
scenario is a shallow stop state on a tickless system. As we might get
stuck into shallow states for hours, in absence of ticks or interrupts.
To address this, We forcefully wakeup the cpu by setting the
decrementer. The decrementer is set to a value that corresponds with the
residency of the next available state. Thus firing up a timer that will
forcefully wakeup the cpu. Few such iterations will essentially train the
governor to select a deeper state for that cpu, as the timer here
corresponds to the next available cpuidle state residency. Thus, cpu will
eventually end up in the deepest possible state.
Signed-off-by: Abhishek Goel <huntbag@linux.vnet.ibm.com>
---
Auto-promotion
v1 : started as auto promotion logic for cpuidle states in generic
driver
v2 : Removed timeout_needed and rebased the code to upstream kernel
Forced-wakeup
v1 : New patch with name of forced wakeup started
v2 : Extending the forced wakeup logic for all states. Setting the
decrementer instead of queuing up a hrtimer to implement the logic.
v3 : Cleanly handle setting/resetting of decrementer so as to not break
irq work
arch/powerpc/include/asm/time.h | 2 ++
arch/powerpc/kernel/time.c | 40 +++++++++++++++++++++++++++++++
drivers/cpuidle/cpuidle-powernv.c | 32 +++++++++++++++++++++++++
3 files changed, 74 insertions(+)
diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h
index 54f4ec1f9..a3bd4f3c0 100644
--- a/arch/powerpc/include/asm/time.h
+++ b/arch/powerpc/include/asm/time.h
@@ -188,6 +188,8 @@ static inline unsigned long tb_ticks_since(unsigned long tstamp)
extern u64 mulhdu(u64, u64);
#endif
+extern int set_dec_before_idle(u64 timeout);
+extern void reset_dec_after_idle(void);
extern void div128_by_32(u64 dividend_high, u64 dividend_low,
unsigned divisor, struct div_result *dr);
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 694522308..814de3469 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -576,6 +576,46 @@ void arch_irq_work_raise(void)
#endif /* CONFIG_IRQ_WORK */
+/*
+ * Returns 1 if we have reprogrammed the decrementer for idle.
+ * Returns 0 if the decrementer is unchanged.
+ */
+int set_dec_before_idle(u64 timeout)
+{
+ u64 *next_tb = this_cpu_ptr(&decrementers_next_tb);
+ u64 now = get_tb_or_rtc();
+
+ /*
+ * Ensure that the timeout is at least one microsecond
+ * before the current decrement value. Else, we will
+ * unnecesarily wakeup again within a microsecond.
+ */
+ if (now + timeout + 512 > *next_tb)
+ return 0;
+
+ set_dec(timeout);
+
+ return 1;
+}
+
+void reset_dec_after_idle(void)
+{
+ u64 now;
+ u64 *next_tb;
+
+ if (test_irq_work_pending())
+ return;
+
+ now = get_tb_or_rtc();
+ next_tb = this_cpu_ptr(&decrementers_next_tb);
+ if (now >= *next_tb)
+ return;
+
+ set_dec(*next_tb - now);
+ if (test_irq_work_pending())
+ set_dec(1);
+}
+
/*
* timer_interrupt - gets called when the decrementer overflows,
* with interrupts disabled.
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index 84b1ebe21..f51478460 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -21,6 +21,7 @@
#include <asm/opal.h>
#include <asm/runlatch.h>
#include <asm/cpuidle.h>
+#include <asm/time.h>
/*
* Expose only those Hardware idle states via the cpuidle framework
@@ -46,6 +47,26 @@ static struct stop_psscr_table stop_psscr_table[CPUIDLE_STATE_MAX] __read_mostly
static u64 default_snooze_timeout __read_mostly;
static bool snooze_timeout_en __read_mostly;
+static u64 forced_wakeup_timeout(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ int i;
+
+ for (i = index + 1; i < drv->state_count; i++) {
+ struct cpuidle_state *s = &drv->states[i];
+ struct cpuidle_state_usage *su = &dev->states_usage[i];
+
+ if (s->disabled || su->disable)
+ continue;
+
+ return (s->target_residency + 2 * s->exit_latency) *
+ tb_ticks_per_usec;
+ }
+
+ return 0;
+}
+
static u64 get_snooze_timeout(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
@@ -144,8 +165,19 @@ static int stop_loop(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
+ u64 timeout_tb;
+ int forced_wakeup = 0;
+
+ timeout_tb = forced_wakeup_timeout(dev, drv, index);
+ if (timeout_tb)
+ forced_wakeup = set_dec_before_idle(timeout_tb);
+
power9_idle_type(stop_psscr_table[index].val,
stop_psscr_table[index].mask);
+
+ if (forced_wakeup)
+ reset_dec_after_idle();
+
return index;
}
--
2.17.1
^ permalink raw reply related
* [RFC v3 3/3] cpuidle-powernv : Recompute the idle-state timeouts when state usage is enabled/disabled
From: Abhishek Goel @ 2019-07-04 9:18 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, linux-pm
Cc: npiggin, rjw, daniel.lezcano, mpe, ego, dja, Abhishek Goel
In-Reply-To: <20190704091827.19555-1-huntbag@linux.vnet.ibm.com>
The disable callback can be used to compute timeout for other states
whenever a state is enabled or disabled. We store the computed timeout
in "timeout" defined in cpuidle state strucure. So, we compute timeout
only when some state is enabled or disabled and not every time in the
fast idle path.
We also use the computed timeout to get timeout for snooze, thus getting
rid of get_snooze_timeout for snooze loop.
Signed-off-by: Abhishek Goel <huntbag@linux.vnet.ibm.com>
---
drivers/cpuidle/cpuidle-powernv.c | 35 +++++++++++--------------------
include/linux/cpuidle.h | 1 +
2 files changed, 13 insertions(+), 23 deletions(-)
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index f51478460..7350f404a 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -45,7 +45,6 @@ struct stop_psscr_table {
static struct stop_psscr_table stop_psscr_table[CPUIDLE_STATE_MAX] __read_mostly;
static u64 default_snooze_timeout __read_mostly;
-static bool snooze_timeout_en __read_mostly;
static u64 forced_wakeup_timeout(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
@@ -67,26 +66,13 @@ static u64 forced_wakeup_timeout(struct cpuidle_device *dev,
return 0;
}
-static u64 get_snooze_timeout(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
+static void pnv_disable_callback(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv)
{
int i;
- if (unlikely(!snooze_timeout_en))
- return default_snooze_timeout;
-
- for (i = index + 1; i < drv->state_count; i++) {
- struct cpuidle_state *s = &drv->states[i];
- struct cpuidle_state_usage *su = &dev->states_usage[i];
-
- if (s->disabled || su->disable)
- continue;
-
- return s->target_residency * tb_ticks_per_usec;
- }
-
- return default_snooze_timeout;
+ for (i = 0; i < drv->state_count; i++)
+ drv->states[i].timeout = forced_wakeup_timeout(dev, drv, i);
}
static int snooze_loop(struct cpuidle_device *dev,
@@ -94,16 +80,20 @@ static int snooze_loop(struct cpuidle_device *dev,
int index)
{
u64 snooze_exit_time;
+ u64 snooze_timeout = drv->states[index].timeout;
+
+ if (!snooze_timeout)
+ snooze_timeout = default_snooze_timeout;
set_thread_flag(TIF_POLLING_NRFLAG);
local_irq_enable();
- snooze_exit_time = get_tb() + get_snooze_timeout(dev, drv, index);
+ snooze_exit_time = get_tb() + snooze_timeout;
ppc64_runlatch_off();
HMT_very_low();
while (!need_resched()) {
- if (likely(snooze_timeout_en) && get_tb() > snooze_exit_time) {
+ if (get_tb() > snooze_exit_time) {
/*
* Task has not woken up but we are exiting the polling
* loop anyway. Require a barrier after polling is
@@ -168,7 +158,7 @@ static int stop_loop(struct cpuidle_device *dev,
u64 timeout_tb;
int forced_wakeup = 0;
- timeout_tb = forced_wakeup_timeout(dev, drv, index);
+ timeout_tb = drv->states[index].timeout;
if (timeout_tb)
forced_wakeup = set_dec_before_idle(timeout_tb);
@@ -255,6 +245,7 @@ static int powernv_cpuidle_driver_init(void)
*/
drv->cpumask = (struct cpumask *)cpu_present_mask;
+ drv->disable_callback = pnv_disable_callback;
return 0;
}
@@ -414,8 +405,6 @@ static int powernv_idle_probe(void)
/* Device tree can indicate more idle states */
max_idle_state = powernv_add_idle_states();
default_snooze_timeout = TICK_USEC * tb_ticks_per_usec;
- if (max_idle_state > 1)
- snooze_timeout_en = true;
} else
return -ENODEV;
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 8a0e54bd0..31662b657 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -50,6 +50,7 @@ struct cpuidle_state {
int power_usage; /* in mW */
unsigned int target_residency; /* in US */
bool disabled; /* disabled on all CPUs */
+ unsigned long long timeout; /* timeout for exiting out of a state */
int (*enter) (struct cpuidle_device *dev,
struct cpuidle_driver *drv,
--
2.17.1
^ permalink raw reply related
* [RFC v3 2/3] cpuidle : Add callback whenever a state usage is enabled/disabled
From: Abhishek Goel @ 2019-07-04 9:18 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, linux-pm
Cc: npiggin, rjw, daniel.lezcano, mpe, ego, dja, Abhishek Goel
In-Reply-To: <20190704091827.19555-1-huntbag@linux.vnet.ibm.com>
To force wakeup a cpu, we need to compute the timeout in the fast idle
path as a state may be enabled or disabled but there did not exist a
feedback to driver when a state is enabled or disabled.
This patch adds a callback whenever a state_usage records a store for
disable attribute.
Signed-off-by: Abhishek Goel <huntbag@linux.vnet.ibm.com>
---
drivers/cpuidle/sysfs.c | 15 ++++++++++++++-
include/linux/cpuidle.h | 4 ++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index eb20adb5d..141671a53 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -415,8 +415,21 @@ static ssize_t cpuidle_state_store(struct kobject *kobj, struct attribute *attr,
struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);
struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
- if (cattr->store)
+ if (cattr->store) {
ret = cattr->store(state, state_usage, buf, size);
+ if (ret == size &&
+ strncmp(cattr->attr.name, "disable",
+ strlen("disable"))) {
+ struct kobject *cpuidle_kobj = kobj->parent;
+ struct cpuidle_device *dev =
+ to_cpuidle_device(cpuidle_kobj);
+ struct cpuidle_driver *drv =
+ cpuidle_get_cpu_driver(dev);
+
+ if (drv->disable_callback)
+ drv->disable_callback(dev, drv);
+ }
+ }
return ret;
}
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index bb9a0db89..8a0e54bd0 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -119,6 +119,10 @@ struct cpuidle_driver {
/* the driver handles the cpus in cpumask */
struct cpumask *cpumask;
+
+ void (*disable_callback)(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv);
+
};
#ifdef CONFIG_CPU_IDLE
--
2.17.1
^ permalink raw reply related
* [PATCH v3 0/3] Forced-wakeup for stop states on Powernv
From: Abhishek Goel @ 2019-07-04 9:18 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev, linux-pm
Cc: npiggin, rjw, daniel.lezcano, mpe, ego, dja, Abhishek Goel
Currently, the cpuidle governors determine what idle state a idling CPU
should enter into based on heuristics that depend on the idle history on
that CPU. Given that no predictive heuristic is perfect, there are cases
where the governor predicts a shallow idle state, hoping that the CPU will
be busy soon. However, if no new workload is scheduled on that CPU in the
near future, the CPU will end up in the shallow state.
Motivation
----------
In case of POWER, this is problematic, when the predicted state in the
aforementioned scenario is a shallow stop state on a tickless system. As
we might get stuck into shallow states even for hours, in absence of ticks
or interrupts.
To address this, We forcefully wakeup the cpu by setting the decrementer.
The decrementer is set to a value that corresponds with the residency of
the next available state. Thus firing up a timer that will forcefully
wakeup the cpu. Few such iterations will essentially train the governor to
select a deeper state for that cpu, as the timer here corresponds to the
next available cpuidle state residency. Thus, cpu will eventually end up
in the deepest possible state and we won't get stuck in a shallow state
for long duration.
Experiment
----------
For earlier versions when this feature was meat to be only for shallow lite
states, I performed experiments for three scenarios to collect some data.
case 1 :
Without this patch and without tick retained, i.e. in a upstream kernel,
It would spend more than even a second to get out of stop0_lite.
case 2 : With tick retained in a upstream kernel -
Generally, we have a sched tick at 4ms(CONF_HZ = 250). Ideally I expected
it to take 8 sched tick to get out of stop0_lite. Experimentally,
observation was
=========================================================
sample min max 99percentile
20 4ms 12ms 4ms
=========================================================
It would take atleast one sched tick to get out of stop0_lite.
case 2 : With this patch (not stopping tick, but explicitly queuing a
timer)
============================================================
sample min max 99percentile
============================================================
20 144us 192us 144us
============================================================
Description of current implementation
-------------------------------------
We calculate timeout for the current idle state as the residency value
of the next available idle state. If the decrementer is set to be
greater than this timeout, we update the decrementer value with the
residency of next available idle state. Thus, essentially training the
governor to select the next available deeper state until we reach the
deepest state. Hence, we won't get stuck unnecessarily in shallow states
for longer duration.
--------------------------------
v1 of auto-promotion : https://lkml.org/lkml/2019/3/22/58 This patch was
implemented only for shallow lite state in generic cpuidle driver.
v2 : Removed timeout_needed and rebased to current
upstream kernel
Then,
v1 of forced-wakeup : Moved the code to cpuidle powernv driver and started
as forced wakeup instead of auto-promotion
v2 : Extended the forced wakeup logic for all states.
Setting the decrementer instead of queuing up a hrtimer to implement the
logic.
v3 : 1) Cleanly handle setting the decrementer after exiting out of stop
states.
2) Added a disable_callback feature to compute timeout whenever a
state is enbaled or disabled instead of computing everytime in fast
idle path.
3) Use disable callback to recompute timeout whenever state usage
is changed for a state. Also, cleaned up the get_snooze_timeout
function.
Abhishek Goel (3):
cpuidle-powernv : forced wakeup for stop states
cpuidle : Add callback whenever a state usage is enabled/disabled
cpuidle-powernv : Recompute the idle-state timeouts when state usage
is enabled/disabled
arch/powerpc/include/asm/time.h | 2 ++
arch/powerpc/kernel/time.c | 40 ++++++++++++++++++++++++++
drivers/cpuidle/cpuidle-powernv.c | 47 ++++++++++++++++++++++---------
drivers/cpuidle/sysfs.c | 15 +++++++++-
include/linux/cpuidle.h | 5 ++++
5 files changed, 95 insertions(+), 14 deletions(-)
--
2.17.1
^ permalink raw reply
* Re: [patch 1/5] add cpuidle-haltpoll driver
From: Joao Martins @ 2019-07-04 9:16 UTC (permalink / raw)
To: Marcelo Tosatti
Cc: kvm-devel, Paolo Bonzini, Radim Krcmar, Andrea Arcangeli,
Rafael J. Wysocki, Peter Zijlstra, Wanpeng Li,
Konrad Rzeszutek Wilk, Raslan KarimAllah, Boris Ostrovsky,
Ankur Arora, Christian Borntraeger, linux-pm
In-Reply-To: <20190703235828.340866829@amt.cnet>
On 7/4/19 12:51 AM, Marcelo Tosatti wrote:
> +++ linux-2.6-newcpuidle.git/drivers/cpuidle/cpuidle-haltpoll.c
> @@ -0,0 +1,69 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * cpuidle driver for haltpoll governor.
> + *
> + * Copyright 2019 Red Hat, Inc. and/or its affiliates.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + *
> + * Authors: Marcelo Tosatti <mtosatti@redhat.com>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/cpuidle.h>
> +#include <linux/module.h>
> +#include <linux/sched/idle.h>
> +#include <linux/kvm_para.h>
> +
> +static int default_enter_idle(struct cpuidle_device *dev,
> + struct cpuidle_driver *drv, int index)
> +{
> + if (current_clr_polling_and_test()) {
> + local_irq_enable();
> + return index;
> + }
> + default_idle();
> + return index;
> +}
> +
> +static struct cpuidle_driver haltpoll_driver = {
> + .name = "haltpoll",
> + .owner = THIS_MODULE,
> + .states = {
> + { /* entry 0 is for polling */ },
> + {
> + .enter = default_enter_idle,
> + .exit_latency = 1,
> + .target_residency = 1,
> + .power_usage = -1,
> + .name = "haltpoll idle",
> + .desc = "default architecture idle",
> + },
> + },
> + .safe_state_index = 0,
> + .state_count = 2,
> +};
> +
> +static int __init haltpoll_init(void)
> +{
> + struct cpuidle_driver *drv = &haltpoll_driver;
> +
> + cpuidle_poll_state_init(drv);
> +
> + if (!kvm_para_available())
> + return 0;
> +
Isn't this meant to return -ENODEV value if the module is meant to not load?
Also this check should probably be placed before initializing the poll state,
provided poll state isn't used anyways if you're not a kvm guest.
Joao
^ permalink raw reply
* Re: [PATCH v2 5/7] power: reset: add driver for mt6323 poweroff
From: Matthias Brugger @ 2019-07-04 9:15 UTC (permalink / raw)
To: Frank Wunderlich, Lee Jones, Rob Herring, Mark Rutland, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney
Cc: Josef Friedl
In-Reply-To: <20190703164822.17924-6-frank-w@public-files.de>
On 03/07/2019 18:48, Frank Wunderlich wrote:
> From: Josef Friedl <josef.friedl@speed.at>
>
> Suggested-by: Frank Wunderlich <frank-w@public-files.de>
> Signed-off-by: Josef Friedl <josef.friedl@speed.at>
Why is there a Signed-off-by from Josef for this patch but not for the others?
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox