Merge branches 'pm-devfreq' and 'pm-tools'

* pm-devfreq:
  PM / devfreq: tegra30: Separate configurations per-SoC generation
  PM / devfreq: tegra30: Support interconnect and OPPs from device-tree
  PM / devfreq: tegra20: Deprecate in a favor of emc-stat based driver
  PM / devfreq: exynos-bus: Add registration of interconnect child device
  dt-bindings: devfreq: Add documentation for the interconnect properties
  soc/tegra: fuse: Add stub for tegra_sku_info
  soc/tegra: fuse: Export tegra_read_ram_code()
  clk: tegra: Export Tegra20 EMC kernel symbols
  PM / devfreq: tegra30: Silence deferred probe error
  PM / devfreq: tegra20: Relax Kconfig dependency
  PM / devfreq: tegra20: Silence deferred probe error
  PM / devfreq: Remove redundant governor_name from struct devfreq
  PM / devfreq: Add governor attribute flag for specifc sysfs nodes
  PM / devfreq: Add governor feature flag
  PM / devfreq: Add tracepoint for frequency changes
  PM / devfreq: Unify frequency change to devfreq_update_target func
  trace: events: devfreq: Use fixed indentation size to improve readability

* pm-tools:
  pm-graph v5.8
  cpupower: Provide online and offline CPU information
This commit is contained in:
Rafael J. Wysocki 2020-12-15 15:27:16 +01:00
commit b3fac81783
24 changed files with 775 additions and 598 deletions

View File

@ -37,20 +37,6 @@ Description:
The /sys/class/devfreq/.../target_freq shows the next governor
predicted target frequency of the corresponding devfreq object.
What: /sys/class/devfreq/.../polling_interval
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../polling_interval shows and sets
the requested polling interval of the corresponding devfreq
object. The values are represented in ms. If the value is
less than 1 jiffy, it is considered to be 0, which means
no polling. This value is meaningless if the governor is
not polling; thus. If the governor is not using
devfreq-provided central polling
(/sys/class/devfreq/.../central_polling is 0), this value
may be useless.
What: /sys/class/devfreq/.../trans_stat
Date: October 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
@ -66,14 +52,6 @@ Description:
echo 0 > /sys/class/devfreq/.../trans_stat
What: /sys/class/devfreq/.../userspace/set_freq
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../userspace/set_freq shows and
sets the requested frequency for the devfreq object if
userspace governor is in effect.
What: /sys/class/devfreq/.../available_frequencies
Date: October 2012
Contact: Nishanth Menon <nm@ti.com>
@ -110,6 +88,35 @@ Description:
The max_freq overrides min_freq because max_freq may be
used to throttle devices to avoid overheating.
What: /sys/class/devfreq/.../polling_interval
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../polling_interval shows and sets
the requested polling interval of the corresponding devfreq
object. The values are represented in ms. If the value is
less than 1 jiffy, it is considered to be 0, which means
no polling. This value is meaningless if the governor is
not polling; thus. If the governor is not using
devfreq-provided central polling
(/sys/class/devfreq/.../central_polling is 0), this value
may be useless.
A list of governors that support the node:
- simple_ondmenad
- tegra_actmon
What: /sys/class/devfreq/.../userspace/set_freq
Date: September 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/devfreq/.../userspace/set_freq shows and
sets the requested frequency for the devfreq object if
userspace governor is in effect.
A list of governors that support the node:
- userspace
What: /sys/class/devfreq/.../timer
Date: July 2020
Contact: Chanwoo Choi <cw00.choi@samsung.com>
@ -122,3 +129,6 @@ Description:
echo deferrable > /sys/class/devfreq/.../timer
echo delayed > /sys/class/devfreq/.../timer
A list of governors that support the node:
- simple_ondemand

View File

@ -51,6 +51,19 @@ Optional properties only for parent bus device:
- exynos,saturation-ratio: the percentage value which is used to calibrate
the performance count against total cycle count.
Optional properties for the interconnect functionality (QoS frequency
constraints):
- #interconnect-cells: should be 0.
- interconnects: as documented in ../interconnect.txt, describes a path at the
higher level interconnects used by this interconnect provider.
If this interconnect provider is directly linked to a top level interconnect
provider the property contains only one phandle. The provider extends
the interconnect graph by linking its node to a node registered by provider
pointed to by first phandle in the 'interconnects' property.
- samsung,data-clock-ratio: ratio of the data throughput in B/s to minimum data
clock frequency in Hz, default value is 8 when this property is missing.
Detailed correlation between sub-blocks and power line according to Exynos SoC:
- In case of Exynos3250, there are two power line as following:
VDD_MIF |--- DMC
@ -135,7 +148,7 @@ Detailed correlation between sub-blocks and power line according to Exynos SoC:
|--- PERIC (Fixed clock rate)
|--- FSYS (Fixed clock rate)
Example1:
Example 1:
Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to
power line (regulator). The MIF (Memory Interface) AXI bus is used to
transfer data between DRAM and CPU and uses the VDD_MIF regulator.
@ -184,7 +197,7 @@ Example1:
|L5 |200000 |200000 |400000 |300000 | ||1000000 |
----------------------------------------------------------
Example2 :
Example 2:
The bus of DMC (Dynamic Memory Controller) block in exynos3250.dtsi
is listed below:
@ -419,3 +432,57 @@ Example2 :
devfreq = <&bus_leftbus>;
status = "okay";
};
Example 3:
An interconnect path "bus_display -- bus_leftbus -- bus_dmc" on
Exynos4412 SoC with video mixer as an interconnect consumer device.
soc {
bus_dmc: bus_dmc {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_DMC>;
clock-names = "bus";
operating-points-v2 = <&bus_dmc_opp_table>;
samsung,data-clock-ratio = <4>;
#interconnect-cells = <0>;
};
bus_leftbus: bus_leftbus {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_GDL>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
#interconnect-cells = <0>;
interconnects = <&bus_dmc>;
};
bus_display: bus_display {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_ACLK160>;
clock-names = "bus";
operating-points-v2 = <&bus_display_opp_table>;
#interconnect-cells = <0>;
interconnects = <&bus_leftbus &bus_dmc>;
};
bus_dmc_opp_table: opp_table1 {
compatible = "operating-points-v2";
/* ... */
}
bus_leftbus_opp_table: opp_table3 {
compatible = "operating-points-v2";
/* ... */
};
bus_display_opp_table: opp_table4 {
compatible = "operating-points-v2";
/* .. */
};
&mixer {
compatible = "samsung,exynos4212-mixer";
interconnects = <&bus_display &bus_dmc>;
/* ... */
};
};

View File

@ -11313,7 +11313,6 @@ L: linux-pm@vger.kernel.org
L: linux-tegra@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
S: Maintained
F: drivers/devfreq/tegra20-devfreq.c
F: drivers/devfreq/tegra30-devfreq.c
MEMORY MANAGEMENT

View File

@ -13,6 +13,7 @@
#include <linux/clk-provider.h>
#include <linux/clk/tegra.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@ -235,6 +236,7 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
emc->cb_arg = cb_arg;
}
}
EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
{
@ -291,3 +293,4 @@ int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
return 0;
}
EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);

View File

@ -121,16 +121,6 @@ config ARM_TEGRA_DEVFREQ
It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support.
config ARM_TEGRA20_DEVFREQ
tristate "NVIDIA Tegra20 DEVFREQ Driver"
depends on (TEGRA_MC && TEGRA20_EMC) || COMPILE_TEST
depends on COMMON_CLK
select DEVFREQ_GOV_SIMPLE_ONDEMAND
help
This adds the DEVFREQ driver for the Tegra20 family of SoCs.
It reads Memory Controller counters and adjusts the operating
frequencies and voltages with OPP support.
config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \

View File

@ -13,7 +13,6 @@ obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
# DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/

View File

@ -31,6 +31,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h>
#define IS_SUPPORTED_FLAG(f, name) ((f & DEVFREQ_GOV_FLAG_##name) ? true : false)
#define IS_SUPPORTED_ATTR(f, name) ((f & DEVFREQ_GOV_ATTR_##name) ? true : false)
#define HZ_PER_KHZ 1000
static struct class *devfreq_class;
@ -367,6 +369,14 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
return err;
}
/*
* Print devfreq_frequency trace information between DEVFREQ_PRECHANGE
* and DEVFREQ_POSTCHANGE because for showing the correct frequency
* change order of between devfreq device and passive devfreq device.
*/
if (trace_devfreq_frequency_enabled() && new_freq != cur_freq)
trace_devfreq_frequency(devfreq, new_freq, cur_freq);
freqs.new = new_freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
@ -382,18 +392,19 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
return err;
}
/* Load monitoring helper functions for governors use */
/**
* update_devfreq() - Reevaluate the device and configure frequency.
* devfreq_update_target() - Reevaluate the device and configure frequency
* on the final stage.
* @devfreq: the devfreq instance.
* @freq: the new frequency of parent device. This argument
* is only used for devfreq device using passive governor.
*
* Note: Lock devfreq->lock before calling update_devfreq
* This function is exported for governors.
* Note: Lock devfreq->lock before calling devfreq_update_target. This function
* should be only used by both update_devfreq() and devfreq governors.
*/
int update_devfreq(struct devfreq *devfreq)
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq)
{
unsigned long freq, min_freq, max_freq;
unsigned long min_freq, max_freq;
int err = 0;
u32 flags = 0;
@ -418,7 +429,21 @@ int update_devfreq(struct devfreq *devfreq)
}
return devfreq_set_target(devfreq, freq, flags);
}
EXPORT_SYMBOL(devfreq_update_target);
/* Load monitoring helper functions for governors use */
/**
* update_devfreq() - Reevaluate the device and configure frequency.
* @devfreq: the devfreq instance.
*
* Note: Lock devfreq->lock before calling update_devfreq
* This function is exported for governors.
*/
int update_devfreq(struct devfreq *devfreq)
{
return devfreq_update_target(devfreq, 0L);
}
EXPORT_SYMBOL(update_devfreq);
@ -456,7 +481,7 @@ static void devfreq_monitor(struct work_struct *work)
*/
void devfreq_monitor_start(struct devfreq *devfreq)
{
if (devfreq->governor->interrupt_driven)
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return;
switch (devfreq->profile->timer) {
@ -486,7 +511,7 @@ EXPORT_SYMBOL(devfreq_monitor_start);
*/
void devfreq_monitor_stop(struct devfreq *devfreq)
{
if (devfreq->governor->interrupt_driven)
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return;
cancel_delayed_work_sync(&devfreq->work);
@ -517,7 +542,7 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)
devfreq->stop_polling = true;
mutex_unlock(&devfreq->lock);
if (devfreq->governor->interrupt_driven)
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return;
cancel_delayed_work_sync(&devfreq->work);
@ -537,12 +562,13 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
unsigned long freq;
mutex_lock(&devfreq->lock);
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
goto out_update;
if (!devfreq->stop_polling)
goto out;
if (devfreq->governor->interrupt_driven)
goto out_update;
if (!delayed_work_pending(&devfreq->work) &&
devfreq->profile->polling_ms)
queue_delayed_work(devfreq_wq, &devfreq->work,
@ -577,10 +603,10 @@ void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay)
mutex_lock(&devfreq->lock);
devfreq->profile->polling_ms = new_delay;
if (devfreq->stop_polling)
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
goto out;
if (devfreq->governor->interrupt_driven)
if (devfreq->stop_polling)
goto out;
/* if new delay is zero, stop polling */
@ -735,6 +761,11 @@ static void devfreq_dev_release(struct device *dev)
kfree(devfreq);
}
static void create_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov);
static void remove_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov);
/**
* devfreq_add_device() - Add devfreq feature to the device
* @dev: the device to add devfreq feature.
@ -780,7 +811,6 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->dev.release = devfreq_dev_release;
INIT_LIST_HEAD(&devfreq->node);
devfreq->profile = profile;
strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
devfreq->previous_freq = profile->initial_freq;
devfreq->last_status.current_frequency = profile->initial_freq;
devfreq->data = data;
@ -876,7 +906,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_lock(&devfreq_list_lock);
governor = try_then_request_governor(devfreq->governor_name);
governor = try_then_request_governor(governor_name);
if (IS_ERR(governor)) {
dev_err(dev, "%s: Unable to find governor for the device\n",
__func__);
@ -892,6 +922,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
__func__);
goto err_init;
}
create_sysfs_files(devfreq, devfreq->governor);
list_add(&devfreq->node, &devfreq_list);
@ -922,9 +953,12 @@ int devfreq_remove_device(struct devfreq *devfreq)
if (!devfreq)
return -EINVAL;
if (devfreq->governor)
if (devfreq->governor) {
devfreq->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL);
remove_sysfs_files(devfreq, devfreq->governor);
}
device_unregister(&devfreq->dev);
return 0;
@ -1214,7 +1248,7 @@ int devfreq_add_governor(struct devfreq_governor *governor)
int ret = 0;
struct device *dev = devfreq->dev.parent;
if (!strncmp(devfreq->governor_name, governor->name,
if (!strncmp(devfreq->governor->name, governor->name,
DEVFREQ_NAME_LEN)) {
/* The following should never occur */
if (devfreq->governor) {
@ -1276,7 +1310,7 @@ int devfreq_remove_governor(struct devfreq_governor *governor)
int ret;
struct device *dev = devfreq->dev.parent;
if (!strncmp(devfreq->governor_name, governor->name,
if (!strncmp(devfreq->governor->name, governor->name,
DEVFREQ_NAME_LEN)) {
/* we should have a devfreq governor! */
if (!devfreq->governor) {
@ -1347,36 +1381,53 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
if (df->governor == governor) {
ret = 0;
goto out;
} else if (df->governor->immutable || governor->immutable) {
} else if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)
|| IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE)) {
ret = -EINVAL;
goto out;
}
/*
* Stop the current governor and remove the specific sysfs files
* which depend on current governor.
*/
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
__func__, df->governor->name, ret);
goto out;
}
remove_sysfs_files(df, df->governor);
/*
* Start the new governor and create the specific sysfs files
* which depend on the new governor.
*/
prev_governor = df->governor;
df->governor = governor;
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s not started(%d)\n",
__func__, df->governor->name, ret);
/* Restore previous governor */
df->governor = prev_governor;
strncpy(df->governor_name, prev_governor->name,
DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) {
dev_err(dev,
"%s: reverting to Governor %s failed (%d)\n",
__func__, df->governor_name, ret);
__func__, prev_governor->name, ret);
df->governor = NULL;
goto out;
}
}
/*
* Create the sysfs files for the new governor. But if failed to start
* the new governor, restore the sysfs files of previous governor.
*/
create_sysfs_files(df, df->governor);
out:
mutex_unlock(&devfreq_list_lock);
@ -1402,9 +1453,9 @@ static ssize_t available_governors_show(struct device *d,
* The devfreq with immutable governor (e.g., passive) shows
* only own governor.
*/
if (df->governor->immutable) {
if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
"%s ", df->governor_name);
"%s ", df->governor->name);
/*
* The devfreq device shows the registered governor except for
* immutable governors such as passive governor .
@ -1413,7 +1464,7 @@ static ssize_t available_governors_show(struct device *d,
struct devfreq_governor *governor;
list_for_each_entry(governor, &devfreq_governor_list, node) {
if (governor->immutable)
if (IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE))
continue;
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
"%s ", governor->name);
@ -1458,39 +1509,6 @@ static ssize_t target_freq_show(struct device *dev,
}
static DEVICE_ATTR_RO(target_freq);
static ssize_t polling_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct devfreq *df = to_devfreq(dev);
if (!df->profile)
return -EINVAL;
return sprintf(buf, "%d\n", df->profile->polling_ms);
}
static ssize_t polling_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct devfreq *df = to_devfreq(dev);
unsigned int value;
int ret;
if (!df->governor)
return -EINVAL;
ret = sscanf(buf, "%u", &value);
if (ret != 1)
return -EINVAL;
df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
ret = count;
return ret;
}
static DEVICE_ATTR_RW(polling_interval);
static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@ -1698,6 +1716,53 @@ static ssize_t trans_stat_store(struct device *dev,
}
static DEVICE_ATTR_RW(trans_stat);
static struct attribute *devfreq_attrs[] = {
&dev_attr_name.attr,
&dev_attr_governor.attr,
&dev_attr_available_governors.attr,
&dev_attr_cur_freq.attr,
&dev_attr_available_frequencies.attr,
&dev_attr_target_freq.attr,
&dev_attr_min_freq.attr,
&dev_attr_max_freq.attr,
&dev_attr_trans_stat.attr,
NULL,
};
ATTRIBUTE_GROUPS(devfreq);
static ssize_t polling_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct devfreq *df = to_devfreq(dev);
if (!df->profile)
return -EINVAL;
return sprintf(buf, "%d\n", df->profile->polling_ms);
}
static ssize_t polling_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct devfreq *df = to_devfreq(dev);
unsigned int value;
int ret;
if (!df->governor)
return -EINVAL;
ret = sscanf(buf, "%u", &value);
if (ret != 1)
return -EINVAL;
df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
ret = count;
return ret;
}
static DEVICE_ATTR_RW(polling_interval);
static ssize_t timer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -1761,21 +1826,36 @@ out:
}
static DEVICE_ATTR_RW(timer);
static struct attribute *devfreq_attrs[] = {
&dev_attr_name.attr,
&dev_attr_governor.attr,
&dev_attr_available_governors.attr,
&dev_attr_cur_freq.attr,
&dev_attr_available_frequencies.attr,
&dev_attr_target_freq.attr,
&dev_attr_polling_interval.attr,
&dev_attr_min_freq.attr,
&dev_attr_max_freq.attr,
&dev_attr_trans_stat.attr,
&dev_attr_timer.attr,
NULL,
};
ATTRIBUTE_GROUPS(devfreq);
#define CREATE_SYSFS_FILE(df, name) \
{ \
int ret; \
ret = sysfs_create_file(&df->dev.kobj, &dev_attr_##name.attr); \
if (ret < 0) { \
dev_warn(&df->dev, \
"Unable to create attr(%s)\n", "##name"); \
} \
} \
/* Create the specific sysfs files which depend on each governor. */
static void create_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov)
{
if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
CREATE_SYSFS_FILE(devfreq, polling_interval);
if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
CREATE_SYSFS_FILE(devfreq, timer);
}
/* Remove the specific sysfs files which depend on each governor. */
static void remove_sysfs_files(struct devfreq *devfreq,
const struct devfreq_governor *gov)
{
if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
sysfs_remove_file(&devfreq->dev.kobj,
&dev_attr_polling_interval.attr);
if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
sysfs_remove_file(&devfreq->dev.kobj, &dev_attr_timer.attr);
}
/**
* devfreq_summary_show() - Show the summary of the devfreq devices
@ -1818,7 +1898,7 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
list_for_each_entry_reverse(devfreq, &devfreq_list, node) {
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE)
if (!strncmp(devfreq->governor_name, DEVFREQ_GOV_PASSIVE,
if (!strncmp(devfreq->governor->name, DEVFREQ_GOV_PASSIVE,
DEVFREQ_NAME_LEN)) {
struct devfreq_passive_data *data = devfreq->data;
@ -1832,15 +1912,19 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
mutex_lock(&devfreq->lock);
cur_freq = devfreq->previous_freq;
get_freq_range(devfreq, &min_freq, &max_freq);
polling_ms = devfreq->profile->polling_ms;
timer = devfreq->profile->timer;
if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL))
polling_ms = devfreq->profile->polling_ms;
else
polling_ms = 0;
mutex_unlock(&devfreq->lock);
seq_printf(s,
"%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n",
dev_name(&devfreq->dev),
p_devfreq ? dev_name(&p_devfreq->dev) : "null",
devfreq->governor_name,
devfreq->governor->name,
polling_ms ? timer_name[timer] : "null",
polling_ms,
cur_freq,

View File

@ -24,6 +24,7 @@
struct exynos_bus {
struct device *dev;
struct platform_device *icc_pdev;
struct devfreq *devfreq;
struct devfreq_event_dev **edev;
@ -156,6 +157,8 @@ static void exynos_bus_exit(struct device *dev)
if (ret < 0)
dev_warn(dev, "failed to disable the devfreq-event devices\n");
platform_device_unregister(bus->icc_pdev);
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
dev_pm_opp_put_regulators(bus->opp_table);
@ -166,6 +169,8 @@ static void exynos_bus_passive_exit(struct device *dev)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
platform_device_unregister(bus->icc_pdev);
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
}
@ -430,6 +435,18 @@ static int exynos_bus_probe(struct platform_device *pdev)
if (ret < 0)
goto err;
/* Create child platform device for the interconnect provider */
if (of_get_property(dev->of_node, "#interconnect-cells", NULL)) {
bus->icc_pdev = platform_device_register_data(
dev, "exynos-generic-icc",
PLATFORM_DEVID_AUTO, NULL, 0);
if (IS_ERR(bus->icc_pdev)) {
ret = PTR_ERR(bus->icc_pdev);
goto err;
}
}
max_state = bus->devfreq->profile->max_state;
min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);

View File

@ -13,6 +13,8 @@
#include <linux/devfreq.h>
#define DEVFREQ_NAME_LEN 16
#define to_devfreq(DEV) container_of((DEV), struct devfreq, dev)
/* Devfreq events */
@ -25,14 +27,32 @@
#define DEVFREQ_MIN_FREQ 0
#define DEVFREQ_MAX_FREQ ULONG_MAX
/*
* Definition of the governor feature flags
* - DEVFREQ_GOV_FLAG_IMMUTABLE
* : This governor is never changeable to other governors.
* - DEVFREQ_GOV_FLAG_IRQ_DRIVEN
* : The devfreq won't schedule the work for this governor.
*/
#define DEVFREQ_GOV_FLAG_IMMUTABLE BIT(0)
#define DEVFREQ_GOV_FLAG_IRQ_DRIVEN BIT(1)
/*
* Definition of governor attribute flags except for common sysfs attributes
* - DEVFREQ_GOV_ATTR_POLLING_INTERVAL
* : Indicate polling_interal sysfs attribute
* - DEVFREQ_GOV_ATTR_TIMER
* : Indicate timer sysfs attribute
*/
#define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0)
#define DEVFREQ_GOV_ATTR_TIMER BIT(1)
/**
* struct devfreq_governor - Devfreq policy governor
* @node: list node - contains registered devfreq governors
* @name: Governor's name
* @immutable: Immutable flag for governor. If the value is 1,
* this governor is never changeable to other governor.
* @interrupt_driven: Devfreq core won't schedule polling work for this
* governor if value is set to 1.
* @attrs: Governor's sysfs attribute flags
* @flags: Governor's feature flags
* @get_target_freq: Returns desired operating frequency for the device.
* Basically, get_target_freq will run
* devfreq_dev_profile.get_dev_status() to get the
@ -50,8 +70,8 @@ struct devfreq_governor {
struct list_head node;
const char name[DEVFREQ_NAME_LEN];
const unsigned int immutable;
const unsigned int interrupt_driven;
const u64 attrs;
const u64 flags;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data);
@ -67,6 +87,7 @@ int devfreq_add_governor(struct devfreq_governor *governor);
int devfreq_remove_governor(struct devfreq_governor *governor);
int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);
static inline int devfreq_update_stats(struct devfreq *df)
{

View File

@ -92,36 +92,6 @@ out:
return ret;
}
static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
{
int ret;
if (!devfreq->governor)
return -EINVAL;
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
ret = devfreq->governor->get_target_freq(devfreq, &freq);
if (ret < 0)
goto out;
ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0);
if (ret < 0)
goto out;
if (devfreq->profile->freq_table
&& (devfreq_update_status(devfreq, freq)))
dev_err(&devfreq->dev,
"Couldn't update frequency transition information.\n");
devfreq->previous_freq = freq;
out:
mutex_unlock(&devfreq->lock);
return 0;
}
static int devfreq_passive_notifier_call(struct notifier_block *nb,
unsigned long event, void *ptr)
{
@ -131,17 +101,25 @@ static int devfreq_passive_notifier_call(struct notifier_block *nb,
struct devfreq *parent = (struct devfreq *)data->parent;
struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr;
unsigned long freq = freqs->new;
int ret = 0;
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
switch (event) {
case DEVFREQ_PRECHANGE:
if (parent->previous_freq > freq)
update_devfreq_passive(devfreq, freq);
ret = devfreq_update_target(devfreq, freq);
break;
case DEVFREQ_POSTCHANGE:
if (parent->previous_freq < freq)
update_devfreq_passive(devfreq, freq);
ret = devfreq_update_target(devfreq, freq);
break;
}
mutex_unlock(&devfreq->lock);
if (ret < 0)
dev_warn(&devfreq->dev,
"failed to update devfreq using passive governor\n");
return NOTIFY_DONE;
}
@ -180,7 +158,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
static struct devfreq_governor devfreq_passive = {
.name = DEVFREQ_GOV_PASSIVE,
.immutable = 1,
.flags = DEVFREQ_GOV_FLAG_IMMUTABLE,
.get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler,
};

View File

@ -117,6 +117,8 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
static struct devfreq_governor devfreq_simple_ondemand = {
.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL
| DEVFREQ_GOV_ATTR_TIMER,
.get_target_freq = devfreq_simple_ondemand_func,
.event_handler = devfreq_simple_ondemand_handler,
};

View File

@ -1,212 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* NVIDIA Tegra20 devfreq driver
*
* Copyright (C) 2019 GRATE-DRIVER project
*/
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <soc/tegra/mc.h>
#include "governor.h"
#define MC_STAT_CONTROL 0x90
#define MC_STAT_EMC_CLOCK_LIMIT 0xa0
#define MC_STAT_EMC_CLOCKS 0xa4
#define MC_STAT_EMC_CONTROL 0xa8
#define MC_STAT_EMC_COUNT 0xb8
#define EMC_GATHER_CLEAR (1 << 8)
#define EMC_GATHER_ENABLE (3 << 8)
struct tegra_devfreq {
struct devfreq *devfreq;
struct clk *emc_clock;
void __iomem *regs;
};
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp;
unsigned long rate;
int err;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp))
return PTR_ERR(opp);
rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
err = clk_set_min_rate(tegra->emc_clock, rate);
if (err)
return err;
err = clk_set_rate(tegra->emc_clock, 0);
if (err)
goto restore_min_rate;
return 0;
restore_min_rate:
clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
return err;
}
static int tegra_devfreq_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
/*
* EMC_COUNT returns number of memory events, that number is lower
* than the number of clocks. Conversion ratio of 1/8 results in a
* bit higher bandwidth than actually needed, it is good enough for
* the time being because drivers don't support requesting minimum
* needed memory bandwidth yet.
*
* TODO: adjust the ratio value once relevant drivers will support
* memory bandwidth management.
*/
stat->busy_time = readl_relaxed(tegra->regs + MC_STAT_EMC_COUNT);
stat->total_time = readl_relaxed(tegra->regs + MC_STAT_EMC_CLOCKS) / 8;
stat->current_frequency = clk_get_rate(tegra->emc_clock);
writel_relaxed(EMC_GATHER_CLEAR, tegra->regs + MC_STAT_CONTROL);
writel_relaxed(EMC_GATHER_ENABLE, tegra->regs + MC_STAT_CONTROL);
return 0;
}
static struct devfreq_dev_profile tegra_devfreq_profile = {
.polling_ms = 500,
.target = tegra_devfreq_target,
.get_dev_status = tegra_devfreq_get_dev_status,
};
static struct tegra_mc *tegra_get_memory_controller(void)
{
struct platform_device *pdev;
struct device_node *np;
struct tegra_mc *mc;
np = of_find_compatible_node(NULL, NULL, "nvidia,tegra20-mc-gart");
if (!np)
return ERR_PTR(-ENOENT);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-ENODEV);
mc = platform_get_drvdata(pdev);
if (!mc)
return ERR_PTR(-EPROBE_DEFER);
return mc;
}
static int tegra_devfreq_probe(struct platform_device *pdev)
{
struct tegra_devfreq *tegra;
struct tegra_mc *mc;
unsigned long max_rate;
unsigned long rate;
int err;
mc = tegra_get_memory_controller();
if (IS_ERR(mc)) {
err = PTR_ERR(mc);
dev_err(&pdev->dev, "failed to get memory controller: %d\n",
err);
return err;
}
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
/* EMC is a system-critical clock that is always enabled */
tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clock)) {
err = PTR_ERR(tegra->emc_clock);
dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
return err;
}
tegra->regs = mc->regs;
max_rate = clk_round_rate(tegra->emc_clock, ULONG_MAX);
for (rate = 0; rate <= max_rate; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);
err = dev_pm_opp_add(&pdev->dev, rate, 0);
if (err) {
dev_err(&pdev->dev, "failed to add opp: %d\n", err);
goto remove_opps;
}
}
/*
* Reset statistic gathers state, select global bandwidth for the
* statistics collection mode and set clocks counter saturation
* limit to maximum.
*/
writel_relaxed(0x00000000, tegra->regs + MC_STAT_CONTROL);
writel_relaxed(0x00000000, tegra->regs + MC_STAT_EMC_CONTROL);
writel_relaxed(0xffffffff, tegra->regs + MC_STAT_EMC_CLOCK_LIMIT);
platform_set_drvdata(pdev, tegra);
tegra->devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
if (IS_ERR(tegra->devfreq)) {
err = PTR_ERR(tegra->devfreq);
goto remove_opps;
}
return 0;
remove_opps:
dev_pm_opp_remove_all_dynamic(&pdev->dev);
return err;
}
static int tegra_devfreq_remove(struct platform_device *pdev)
{
struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
devfreq_remove_device(tegra->devfreq);
dev_pm_opp_remove_all_dynamic(&pdev->dev);
return 0;
}
static struct platform_driver tegra_devfreq_driver = {
.probe = tegra_devfreq_probe,
.remove = tegra_devfreq_remove,
.driver = {
.name = "tegra20-devfreq",
},
};
module_platform_driver(tegra_devfreq_driver);
MODULE_ALIAS("platform:tegra20-devfreq");
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver");
MODULE_LICENSE("GPL v2");

View File

@ -19,6 +19,8 @@
#include <linux/reset.h>
#include <linux/workqueue.h>
#include <soc/tegra/fuse.h>
#include "governor.h"
#define ACTMON_GLB_STATUS 0x0
@ -55,13 +57,6 @@
#define ACTMON_BELOW_WMARK_WINDOW 3
#define ACTMON_BOOST_FREQ_STEP 16000
/*
* Activity counter is incremented every 256 memory transactions, and each
* transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is
* 4 * 256 = 1024.
*/
#define ACTMON_COUNT_WEIGHT 0x400
/*
* ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which
* translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128
@ -109,7 +104,7 @@ enum tegra_actmon_device {
MCCPU,
};
static const struct tegra_devfreq_device_config actmon_device_configs[] = {
static const struct tegra_devfreq_device_config tegra124_device_configs[] = {
{
/* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0,
@ -131,6 +126,28 @@ static const struct tegra_devfreq_device_config actmon_device_configs[] = {
},
};
static const struct tegra_devfreq_device_config tegra30_device_configs[] = {
{
/* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0,
.irq_mask = 1 << 26,
.boost_up_coeff = 200,
.boost_down_coeff = 50,
.boost_up_threshold = 20,
.boost_down_threshold = 10,
},
{
/* MCCPU: memory accesses from the CPUs */
.offset = 0x200,
.irq_mask = 1 << 25,
.boost_up_coeff = 800,
.boost_down_coeff = 40,
.boost_up_threshold = 27,
.boost_down_threshold = 10,
.avg_dependency_threshold = 16000, /* 16MHz in kHz units */
},
};
/**
* struct tegra_devfreq_device - state specific to an ACTMON device
*
@ -153,8 +170,15 @@ struct tegra_devfreq_device {
unsigned long target_freq;
};
struct tegra_devfreq_soc_data {
const struct tegra_devfreq_device_config *configs;
/* Weight value for count measurements */
unsigned int count_weight;
};
struct tegra_devfreq {
struct devfreq *devfreq;
struct opp_table *opp_table;
struct reset_control *reset;
struct clk *clock;
@ -168,11 +192,13 @@ struct tegra_devfreq {
struct delayed_work cpufreq_update_work;
struct notifier_block cpu_rate_change_nb;
struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
struct tegra_devfreq_device devices[2];
unsigned int irq;
bool started;
const struct tegra_devfreq_soc_data *soc;
};
struct tegra_actmon_emc_ratio {
@ -485,7 +511,7 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
tegra_devfreq_update_avg_wmark(tegra, dev);
tegra_devfreq_update_wmark(tegra, dev);
device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT);
device_writel(dev, tegra->soc->count_weight, ACTMON_DEV_COUNT_WEIGHT);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
val |= ACTMON_DEV_CTRL_ENB_PERIODIC;
@ -612,34 +638,19 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp;
unsigned long rate;
int err;
int ret;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp)) {
dev_err(dev, "Failed to find opp for %lu Hz\n", *freq);
return PTR_ERR(opp);
}
rate = dev_pm_opp_get_freq(opp);
ret = dev_pm_opp_set_bw(dev, opp);
dev_pm_opp_put(opp);
err = clk_set_min_rate(tegra->emc_clock, rate * KHZ);
if (err)
return err;
err = clk_set_rate(tegra->emc_clock, 0);
if (err)
goto restore_min_rate;
return 0;
restore_min_rate:
clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
return err;
return ret;
}
static int tegra_devfreq_get_dev_status(struct device *dev,
@ -655,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
stat->private_data = tegra;
/* The below are to be used by the other governors */
stat->current_frequency = cur_freq;
stat->current_frequency = cur_freq * KHZ;
actmon_dev = &tegra->devices[MCALL];
@ -705,7 +716,12 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
target_freq = max(target_freq, dev->target_freq);
}
*freq = target_freq;
/*
* tegra-devfreq driver operates with KHz units, while OPP table
* entries use Hz units. Hence we need to convert the units for the
* devfreq core.
*/
*freq = target_freq * KHZ;
return 0;
}
@ -765,14 +781,16 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
static struct devfreq_governor tegra_devfreq_governor = {
.name = "tegra_actmon",
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL,
.flags = DEVFREQ_GOV_FLAG_IMMUTABLE
| DEVFREQ_GOV_FLAG_IRQ_DRIVEN,
.get_target_freq = tegra_governor_get_target,
.event_handler = tegra_governor_event_handler,
.immutable = true,
.interrupt_driven = true,
};
static int tegra_devfreq_probe(struct platform_device *pdev)
{
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
struct tegra_devfreq_device *dev;
struct tegra_devfreq *tegra;
struct devfreq *devfreq;
@ -784,6 +802,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
if (!tegra)
return -ENOMEM;
tegra->soc = of_device_get_match_data(&pdev->dev);
tegra->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
@ -801,10 +821,9 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}
tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clock)) {
dev_err(&pdev->dev, "Failed to get emc clock\n");
return PTR_ERR(tegra->emc_clock);
}
if (IS_ERR(tegra->emc_clock))
return dev_err_probe(&pdev->dev, PTR_ERR(tegra->emc_clock),
"Failed to get emc clock\n");
err = platform_get_irq(pdev, 0);
if (err < 0)
@ -822,11 +841,25 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err;
}
tegra->opp_table = dev_pm_opp_set_supported_hw(&pdev->dev,
&hw_version, 1);
err = PTR_ERR_OR_ZERO(tegra->opp_table);
if (err) {
dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err);
return err;
}
err = dev_pm_opp_of_add_table(&pdev->dev);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err);
goto put_hw;
}
err = clk_prepare_enable(tegra->clock);
if (err) {
dev_err(&pdev->dev,
"Failed to prepare and enable ACTMON clock\n");
return err;
goto remove_table;
}
err = reset_control_reset(tegra->reset);
@ -844,29 +877,12 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
tegra->max_freq = rate / KHZ;
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = tegra->devices + i;
dev->config = actmon_device_configs + i;
dev->config = tegra->soc->configs + i;
dev->regs = tegra->regs + dev->config->offset;
}
for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);
if (rate < 0) {
dev_err(&pdev->dev,
"Failed to round clock rate: %ld\n", rate);
err = rate;
goto remove_opps;
}
err = dev_pm_opp_add(&pdev->dev, rate / KHZ, 0);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP: %d\n", err);
goto remove_opps;
}
}
platform_set_drvdata(pdev, tegra);
tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb;
@ -882,7 +898,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
tegra_devfreq_profile.initial_freq /= KHZ;
devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL);
@ -902,6 +917,10 @@ remove_opps:
reset_control_reset(tegra->reset);
disable_clk:
clk_disable_unprepare(tegra->clock);
remove_table:
dev_pm_opp_of_remove_table(&pdev->dev);
put_hw:
dev_pm_opp_put_supported_hw(tegra->opp_table);
return err;
}
@ -913,17 +932,33 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
devfreq_remove_device(tegra->devfreq);
devfreq_remove_governor(&tegra_devfreq_governor);
dev_pm_opp_remove_all_dynamic(&pdev->dev);
reset_control_reset(tegra->reset);
clk_disable_unprepare(tegra->clock);
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_supported_hw(tegra->opp_table);
return 0;
}
static const struct tegra_devfreq_soc_data tegra124_soc = {
.configs = tegra124_device_configs,
/*
* Activity counter is incremented every 256 memory transactions,
* and each transaction takes 4 EMC clocks.
*/
.count_weight = 4 * 256,
};
static const struct tegra_devfreq_soc_data tegra30_soc = {
.configs = tegra30_device_configs,
.count_weight = 2 * 256,
};
static const struct of_device_id tegra_devfreq_of_match[] = {
{ .compatible = "nvidia,tegra30-actmon" },
{ .compatible = "nvidia,tegra124-actmon" },
{ .compatible = "nvidia,tegra30-actmon", .data = &tegra30_soc, },
{ .compatible = "nvidia,tegra124-actmon", .data = &tegra124_soc, },
{ },
};

View File

@ -3,6 +3,7 @@
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
@ -90,6 +91,7 @@ u32 tegra_read_ram_code(void)
return straps >> PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT;
}
EXPORT_SYMBOL_GPL(tegra_read_ram_code);
static const struct of_device_id apbmisc_match[] __initconst = {
{ .compatible = "nvidia,tegra20-apbmisc", },

View File

@ -15,8 +15,6 @@
#include <linux/pm_opp.h>
#include <linux/pm_qos.h>
#define DEVFREQ_NAME_LEN 16
/* DEVFREQ governor name */
#define DEVFREQ_GOV_SIMPLE_ONDEMAND "simple_ondemand"
#define DEVFREQ_GOV_PERFORMANCE "performance"
@ -139,7 +137,6 @@ struct devfreq_stats {
* using devfreq.
* @profile: device-specific devfreq profile
* @governor: method how to choose frequency based on the usage.
* @governor_name: devfreq governor name for use with this devfreq
* @nb: notifier block used to notify devfreq object that it should
* reevaluate operable frequencies. Devfreq users may use
* devfreq.nb to the corresponding register notifier call chain.
@ -176,7 +173,6 @@ struct devfreq {
struct device dev;
struct devfreq_dev_profile *profile;
const struct devfreq_governor *governor;
char governor_name[DEVFREQ_NAME_LEN];
struct notifier_block nb;
struct delayed_work work;

View File

@ -56,7 +56,11 @@ u32 tegra_read_straps(void);
u32 tegra_read_ram_code(void);
int tegra_fuse_readl(unsigned long offset, u32 *value);
#ifdef CONFIG_ARCH_TEGRA
extern struct tegra_sku_info tegra_sku_info;
#else
static struct tegra_sku_info tegra_sku_info __maybe_unused;
#endif
struct device *tegra_soc_device_register(void);

View File

@ -8,6 +8,34 @@
#include <linux/devfreq.h>
#include <linux/tracepoint.h>
TRACE_EVENT(devfreq_frequency,
TP_PROTO(struct devfreq *devfreq, unsigned long freq,
unsigned long prev_freq),
TP_ARGS(devfreq, freq, prev_freq),
TP_STRUCT__entry(
__string(dev_name, dev_name(&devfreq->dev))
__field(unsigned long, freq)
__field(unsigned long, prev_freq)
__field(unsigned long, busy_time)
__field(unsigned long, total_time)
),
TP_fast_assign(
__assign_str(dev_name, dev_name(&devfreq->dev));
__entry->freq = freq;
__entry->prev_freq = prev_freq;
__entry->busy_time = devfreq->last_status.busy_time;
__entry->total_time = devfreq->last_status.total_time;
),
TP_printk("dev_name=%-30s freq=%-12lu prev_freq=%-12lu load=%-2lu",
__get_str(dev_name), __entry->freq, __entry->prev_freq,
__entry->total_time == 0 ? 0 :
(100 * __entry->busy_time) / __entry->total_time)
);
TRACE_EVENT(devfreq_monitor,
TP_PROTO(struct devfreq *devfreq),
@ -29,7 +57,7 @@ TRACE_EVENT(devfreq_monitor,
__assign_str(dev_name, dev_name(&devfreq->dev));
),
TP_printk("dev_name=%s freq=%lu polling_ms=%u load=%lu",
TP_printk("dev_name=%-30s freq=%-12lu polling_ms=%-3u load=%-2lu",
__get_str(dev_name), __entry->freq, __entry->polling_ms,
__entry->total_time == 0 ? 0 :
(100 * __entry->busy_time) / __entry->total_time)

View File

@ -315,6 +315,7 @@ int cmd_freq_set(int argc, char **argv)
}
}
get_cpustate();
/* loop over CPUs */
for (cpu = bitmask_first(cpus_chosen);
@ -332,5 +333,7 @@ int cmd_freq_set(int argc, char **argv)
}
}
print_offline_cpus();
return 0;
}

View File

@ -95,6 +95,8 @@ int cmd_idle_set(int argc, char **argv)
exit(EXIT_FAILURE);
}
get_cpustate();
/* Default is: set all CPUs */
if (bitmask_isallclear(cpus_chosen))
bitmask_setall(cpus_chosen);
@ -181,5 +183,7 @@ int cmd_idle_set(int argc, char **argv)
break;
}
}
print_offline_cpus();
return EXIT_SUCCESS;
}

View File

@ -34,6 +34,8 @@ int run_as_root;
int base_cpu;
/* Affected cpus chosen by -c/--cpu param */
struct bitmask *cpus_chosen;
struct bitmask *online_cpus;
struct bitmask *offline_cpus;
#ifdef DEBUG
int be_verbose;
@ -178,6 +180,8 @@ int main(int argc, const char *argv[])
char pathname[32];
cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
online_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
offline_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
argc--;
argv += 1;
@ -230,6 +234,10 @@ int main(int argc, const char *argv[])
ret = p->main(argc, argv);
if (cpus_chosen)
bitmask_free(cpus_chosen);
if (online_cpus)
bitmask_free(online_cpus);
if (offline_cpus)
bitmask_free(offline_cpus);
return ret;
}
print_help();

View File

@ -94,6 +94,8 @@ struct cpupower_cpu_info {
*/
extern int get_cpu_info(struct cpupower_cpu_info *cpu_info);
extern struct cpupower_cpu_info cpupower_cpu_info;
/* cpuid and cpuinfo helpers **************************/
/* X86 ONLY ****************************************/
@ -171,4 +173,14 @@ static inline unsigned int cpuid_ecx(unsigned int op) { return 0; };
static inline unsigned int cpuid_edx(unsigned int op) { return 0; };
#endif /* defined(__i386__) || defined(__x86_64__) */
/*
* CPU State related functions
*/
extern struct bitmask *online_cpus;
extern struct bitmask *offline_cpus;
void get_cpustate(void);
void print_online_cpus(void);
void print_offline_cpus(void);
#endif /* __CPUPOWERUTILS_HELPERS__ */

View File

@ -1,8 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#if defined(__i386__) || defined(__x86_64__)
#include <stdio.h>
#include <stdlib.h>
#include "helpers/helpers.h"
#if defined(__i386__) || defined(__x86_64__)
#define MSR_AMD_HWCR 0xc0010015
int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active,
@ -41,3 +45,63 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active,
return 0;
}
#endif /* #if defined(__i386__) || defined(__x86_64__) */
/* get_cpustate
*
* Gather the information of all online CPUs into bitmask struct
*/
void get_cpustate(void)
{
unsigned int cpu = 0;
bitmask_clearall(online_cpus);
bitmask_clearall(offline_cpus);
for (cpu = bitmask_first(cpus_chosen);
cpu <= bitmask_last(cpus_chosen); cpu++) {
if (cpupower_is_cpu_online(cpu) == 1)
bitmask_setbit(online_cpus, cpu);
else
bitmask_setbit(offline_cpus, cpu);
continue;
}
}
/* print_online_cpus
*
* Print the CPU numbers of all CPUs that are online currently
*/
void print_online_cpus(void)
{
int str_len = 0;
char *online_cpus_str = NULL;
str_len = online_cpus->size * 5;
online_cpus_str = (void *)malloc(sizeof(char) * str_len);
if (!bitmask_isallclear(online_cpus)) {
bitmask_displaylist(online_cpus_str, str_len, online_cpus);
printf(_("Following CPUs are online:\n%s\n"), online_cpus_str);
}
}
/* print_offline_cpus
*
* Print the CPU numbers of all CPUs that are offline currently
*/
void print_offline_cpus(void)
{
int str_len = 0;
char *offline_cpus_str = NULL;
str_len = offline_cpus->size * 5;
offline_cpus_str = (void *)malloc(sizeof(char) * str_len);
if (!bitmask_isallclear(offline_cpus)) {
bitmask_displaylist(offline_cpus_str, str_len, offline_cpus);
printf(_("Following CPUs are offline:\n%s\n"), offline_cpus_str);
printf(_("cpupower set operation was not performed on them\n"));
}
}

View File

@ -6,7 +6,7 @@
|_| |___/ |_|
pm-graph: suspend/resume/boot timing analysis tools
Version: 5.7
Version: 5.8
Author: Todd Brandt <todd.e.brandt@intel.com>
Home Page: https://01.org/pm-graph
@ -61,7 +61,7 @@
- runs with python2 or python3, choice is made by /usr/bin/python link
- python
- python-configparser (for python2 sleepgraph)
- python-requests (for googlesheet.py)
- python-requests (for stresstester.py)
- linux-tools-common (for turbostat usage in sleepgraph)
Ubuntu:

View File

@ -81,7 +81,7 @@ def ascii(text):
# store system values and test parameters
class SystemValues:
title = 'SleepGraph'
version = '5.7'
version = '5.8'
ansi = False
rs = 0
display = ''
@ -92,8 +92,9 @@ class SystemValues:
testlog = True
dmesglog = True
ftracelog = False
acpidebug = True
tstat = True
mindevlen = 0.0
mindevlen = 0.0001
mincglen = 0.0
cgphase = ''
cgtest = -1
@ -115,6 +116,7 @@ class SystemValues:
fpdtpath = '/sys/firmware/acpi/tables/FPDT'
epath = '/sys/kernel/debug/tracing/events/power/'
pmdpath = '/sys/power/pm_debug_messages'
acpipath='/sys/module/acpi/parameters/debug_level'
traceevents = [
'suspend_resume',
'wakeup_source_activate',
@ -162,16 +164,16 @@ class SystemValues:
devdump = False
mixedphaseheight = True
devprops = dict()
cfgdef = dict()
platinfo = []
predelay = 0
postdelay = 0
pmdebug = ''
tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
tracefuncs = {
'sys_sync': {},
'ksys_sync': {},
'pm_notifier_call_chain_robust': {},
'__pm_notifier_call_chain': {},
'pm_prepare_console': {},
'pm_notifier_call_chain': {},
'freeze_processes': {},
@ -490,9 +492,9 @@ class SystemValues:
call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
def initdmesg(self):
# get the latest time stamp from the dmesg log
fp = Popen('dmesg', stdout=PIPE).stdout
lines = Popen('dmesg', stdout=PIPE).stdout.readlines()
ktime = '0'
for line in fp:
for line in reversed(lines):
line = ascii(line).replace('\r\n', '')
idx = line.find('[')
if idx > 1:
@ -500,7 +502,7 @@ class SystemValues:
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
if(m):
ktime = m.group('ktime')
fp.close()
break
self.dmesgstart = float(ktime)
def getdmesg(self, testdata):
op = self.writeDatafileHeader(self.dmesgfile, testdata)
@ -715,8 +717,6 @@ class SystemValues:
self.fsetVal('0', 'events/kprobes/enable')
self.fsetVal('', 'kprobe_events')
self.fsetVal('1024', 'buffer_size_kb')
if self.pmdebug:
self.setVal(self.pmdebug, self.pmdpath)
def setupAllKprobes(self):
for name in self.tracefuncs:
self.defaultKprobe(name, self.tracefuncs[name])
@ -740,11 +740,7 @@ class SystemValues:
# turn trace off
self.fsetVal('0', 'tracing_on')
self.cleanupFtrace()
# pm debug messages
pv = self.getVal(self.pmdpath)
if pv != '1':
self.setVal('1', self.pmdpath)
self.pmdebug = pv
self.testVal(self.pmdpath, 'basic', '1')
# set the trace clock to global
self.fsetVal('global', 'trace_clock')
self.fsetVal('nop', 'current_tracer')
@ -900,6 +896,14 @@ class SystemValues:
if isgz:
return gzip.open(filename, mode+'t')
return open(filename, mode)
def putlog(self, filename, text):
with self.openlog(filename, 'a') as fp:
fp.write(text)
fp.close()
def dlog(self, text):
self.putlog(self.dmesgfile, '# %s\n' % text)
def flog(self, text):
self.putlog(self.ftracefile, text)
def b64unzip(self, data):
try:
out = codecs.decode(base64.b64decode(data), 'zlib').decode()
@ -992,9 +996,7 @@ class SystemValues:
# add a line for each of these commands with their outputs
for name, cmdline, info in cmdafter:
footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
with self.openlog(self.ftracefile, 'a') as fp:
fp.write(footer)
self.flog(footer)
return True
def commonPrefix(self, list):
if len(list) < 2:
@ -1034,6 +1036,7 @@ class SystemValues:
cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
if not cmdpath or (begin and not delta):
continue
self.dlog('[%s]' % cmdline)
try:
fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
info = ascii(fp.read()).strip()
@ -1060,6 +1063,29 @@ class SystemValues:
else:
out.append((name, cmdline, '\tnothing' if not info else info))
return out
def testVal(self, file, fmt='basic', value=''):
if file == 'restoreall':
for f in self.cfgdef:
if os.path.exists(f):
fp = open(f, 'w')
fp.write(self.cfgdef[f])
fp.close()
self.cfgdef = dict()
elif value and os.path.exists(file):
fp = open(file, 'r+')
if fmt == 'radio':
m = re.match('.*\[(?P<v>.*)\].*', fp.read())
if m:
self.cfgdef[file] = m.group('v')
elif fmt == 'acpi':
line = fp.read().strip().split('\n')[-1]
m = re.match('.* (?P<v>[0-9A-Fx]*) .*', line)
if m:
self.cfgdef[file] = m.group('v')
else:
self.cfgdef[file] = fp.read().strip()
fp.write(value)
fp.close()
def haveTurbostat(self):
if not self.tstat:
return False
@ -1201,6 +1227,57 @@ class SystemValues:
self.multitest[sz] *= 1440
elif unit == 'h':
self.multitest[sz] *= 60
def displayControl(self, cmd):
xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
if self.sudouser:
xset = 'sudo -u %s %s' % (self.sudouser, xset)
if cmd == 'init':
ret = call(xset.format('dpms 0 0 0'), shell=True)
if not ret:
ret = call(xset.format('s off'), shell=True)
elif cmd == 'reset':
ret = call(xset.format('s reset'), shell=True)
elif cmd in ['on', 'off', 'standby', 'suspend']:
b4 = self.displayControl('stat')
ret = call(xset.format('dpms force %s' % cmd), shell=True)
if not ret:
curr = self.displayControl('stat')
self.vprint('Display Switched: %s -> %s' % (b4, curr))
if curr != cmd:
self.vprint('WARNING: Display failed to change to %s' % cmd)
if ret:
self.vprint('WARNING: Display failed to change to %s with xset' % cmd)
return ret
elif cmd == 'stat':
fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
ret = 'unknown'
for line in fp:
m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
if(m and len(m.group('m')) >= 2):
out = m.group('m').lower()
ret = out[3:] if out[0:2] == 'in' else out
break
fp.close()
return ret
def setRuntimeSuspend(self, before=True):
if before:
# runtime suspend disable or enable
if self.rs > 0:
self.rstgt, self.rsval, self.rsdir = 'on', 'auto', 'enabled'
else:
self.rstgt, self.rsval, self.rsdir = 'auto', 'on', 'disabled'
pprint('CONFIGURING RUNTIME SUSPEND...')
self.rslist = deviceInfo(self.rstgt)
for i in self.rslist:
self.setVal(self.rsval, i)
pprint('runtime suspend %s on all devices (%d changed)' % (self.rsdir, len(self.rslist)))
pprint('waiting 5 seconds...')
time.sleep(5)
else:
# runtime suspend re-enable or re-disable
for i in self.rslist:
self.setVal(self.rstgt, i)
pprint('runtime suspend settings restored on %d devices' % len(self.rslist))
sysvals = SystemValues()
switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
@ -1640,15 +1717,20 @@ class Data:
if 'resume_machine' in phase and 'suspend_machine' in lp:
tS, tR = self.dmesg[lp]['end'], self.dmesg[phase]['start']
tL = tR - tS
if tL > 0:
left = True if tR > tZero else False
self.trimTime(tS, tL, left)
if 'trying' in self.dmesg[lp] and self.dmesg[lp]['trying'] >= 0.001:
tTry = round(self.dmesg[lp]['trying'] * 1000)
text = '%.0f (-%.0f waking)' % (tL * 1000, tTry)
if tL <= 0:
continue
left = True if tR > tZero else False
self.trimTime(tS, tL, left)
if 'waking' in self.dmesg[lp]:
tCnt = self.dmesg[lp]['waking'][0]
if self.dmesg[lp]['waking'][1] >= 0.001:
tTry = '-%.0f' % (round(self.dmesg[lp]['waking'][1] * 1000))
else:
text = '%.0f' % (tL * 1000)
self.tLow.append(text)
tTry = '-%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
text = '%.0f (%s ms waking %d times)' % (tL * 1000, tTry, tCnt)
else:
text = '%.0f' % (tL * 1000)
self.tLow.append(text)
lp = phase
def getMemTime(self):
if not self.hwstart or not self.hwend:
@ -1921,7 +2003,7 @@ class Data:
for dev in list:
length = (list[dev]['end'] - list[dev]['start']) * 1000
width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
if width != '0.000000' and length >= mindevlen:
if length >= mindevlen:
devlist.append(dev)
self.tdevlist[phase] = devlist
def addHorizontalDivider(self, devname, devend):
@ -3316,9 +3398,10 @@ def parseTraceLog(live=False):
# trim out s2idle loops, track time trying to freeze
llp = data.lastPhase(2)
if llp.startswith('suspend_machine'):
if 'trying' not in data.dmesg[llp]:
data.dmesg[llp]['trying'] = 0
data.dmesg[llp]['trying'] += \
if 'waking' not in data.dmesg[llp]:
data.dmesg[llp]['waking'] = [0, 0.0]
data.dmesg[llp]['waking'][0] += 1
data.dmesg[llp]['waking'][1] += \
t.time - data.dmesg[lp]['start']
data.currphase = ''
del data.dmesg[lp]
@ -4555,7 +4638,7 @@ def createHTML(testruns, testfail):
# draw the devices for this phase
phaselist = data.dmesg[b]['list']
for d in sorted(data.tdevlist[b]):
dname = d if '[' not in d else d.split('[')[0]
dname = d if ('[' not in d or 'CPU' in d) else d.split('[')[0]
name, dev = dname, phaselist[d]
drv = xtraclass = xtrainfo = xtrastyle = ''
if 'htmlclass' in dev:
@ -5194,156 +5277,146 @@ def addScriptCode(hf, testruns):
'</script>\n'
hf.write(script_code);
def setRuntimeSuspend(before=True):
global sysvals
sv = sysvals
if sv.rs == 0:
return
if before:
# runtime suspend disable or enable
if sv.rs > 0:
sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled'
else:
sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled'
pprint('CONFIGURING RUNTIME SUSPEND...')
sv.rslist = deviceInfo(sv.rstgt)
for i in sv.rslist:
sv.setVal(sv.rsval, i)
pprint('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist)))
pprint('waiting 5 seconds...')
time.sleep(5)
else:
# runtime suspend re-enable or re-disable
for i in sv.rslist:
sv.setVal(sv.rstgt, i)
pprint('runtime suspend settings restored on %d devices' % len(sv.rslist))
# Function: executeSuspend
# Description:
# Execute system suspend through the sysfs interface, then copy the output
# dmesg and ftrace files to the test output directory.
def executeSuspend(quiet=False):
pm = ProcessMonitor()
tp = sysvals.tpath
if sysvals.wifi:
wifi = sysvals.checkWifi()
sv, tp, pm = sysvals, sysvals.tpath, ProcessMonitor()
if sv.wifi:
wifi = sv.checkWifi()
sv.dlog('wifi check, connected device is "%s"' % wifi)
testdata = []
# run these commands to prepare the system for suspend
if sysvals.display:
if sv.display:
if not quiet:
pprint('SET DISPLAY TO %s' % sysvals.display.upper())
displayControl(sysvals.display)
pprint('SET DISPLAY TO %s' % sv.display.upper())
ret = sv.displayControl(sv.display)
sv.dlog('xset display %s, ret = %d' % (sv.display, ret))
time.sleep(1)
if sysvals.sync:
if sv.sync:
if not quiet:
pprint('SYNCING FILESYSTEMS')
sv.dlog('syncing filesystems')
call('sync', shell=True)
# mark the start point in the kernel ring buffer just as we start
sysvals.initdmesg()
sv.dlog('read dmesg')
sv.initdmesg()
# start ftrace
if(sysvals.usecallgraph or sysvals.usetraceevents):
if(sv.usecallgraph or sv.usetraceevents):
if not quiet:
pprint('START TRACING')
sysvals.fsetVal('1', 'tracing_on')
if sysvals.useprocmon:
sv.dlog('start ftrace tracing')
sv.fsetVal('1', 'tracing_on')
if sv.useprocmon:
sv.dlog('start the process monitor')
pm.start()
sysvals.cmdinfo(True)
sv.dlog('run the cmdinfo list before')
sv.cmdinfo(True)
# execute however many s/r runs requested
for count in range(1,sysvals.execcount+1):
for count in range(1,sv.execcount+1):
# x2delay in between test runs
if(count > 1 and sysvals.x2delay > 0):
sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
time.sleep(sysvals.x2delay/1000.0)
sysvals.fsetVal('WAIT END', 'trace_marker')
if(count > 1 and sv.x2delay > 0):
sv.fsetVal('WAIT %d' % sv.x2delay, 'trace_marker')
time.sleep(sv.x2delay/1000.0)
sv.fsetVal('WAIT END', 'trace_marker')
# start message
if sysvals.testcommand != '':
if sv.testcommand != '':
pprint('COMMAND START')
else:
if(sysvals.rtcwake):
if(sv.rtcwake):
pprint('SUSPEND START')
else:
pprint('SUSPEND START (press a key to resume)')
# set rtcwake
if(sysvals.rtcwake):
if(sv.rtcwake):
if not quiet:
pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
sysvals.rtcWakeAlarmOn()
pprint('will issue an rtcwake in %d seconds' % sv.rtcwaketime)
sv.dlog('enable RTC wake alarm')
sv.rtcWakeAlarmOn()
# start of suspend trace marker
if(sysvals.usecallgraph or sysvals.usetraceevents):
sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
if(sv.usecallgraph or sv.usetraceevents):
sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
# predelay delay
if(count == 1 and sysvals.predelay > 0):
sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
time.sleep(sysvals.predelay/1000.0)
sysvals.fsetVal('WAIT END', 'trace_marker')
if(count == 1 and sv.predelay > 0):
sv.fsetVal('WAIT %d' % sv.predelay, 'trace_marker')
time.sleep(sv.predelay/1000.0)
sv.fsetVal('WAIT END', 'trace_marker')
# initiate suspend or command
sv.dlog('system executing a suspend')
tdata = {'error': ''}
if sysvals.testcommand != '':
res = call(sysvals.testcommand+' 2>&1', shell=True);
if sv.testcommand != '':
res = call(sv.testcommand+' 2>&1', shell=True);
if res != 0:
tdata['error'] = 'cmd returned %d' % res
else:
mode = sysvals.suspendmode
if sysvals.memmode and os.path.exists(sysvals.mempowerfile):
mode = sv.suspendmode
if sv.memmode and os.path.exists(sv.mempowerfile):
mode = 'mem'
pf = open(sysvals.mempowerfile, 'w')
pf.write(sysvals.memmode)
pf.close()
if sysvals.diskmode and os.path.exists(sysvals.diskpowerfile):
sv.testVal(sv.mempowerfile, 'radio', sv.memmode)
if sv.diskmode and os.path.exists(sv.diskpowerfile):
mode = 'disk'
pf = open(sysvals.diskpowerfile, 'w')
pf.write(sysvals.diskmode)
pf.close()
if mode == 'freeze' and sysvals.haveTurbostat():
sv.testVal(sv.diskpowerfile, 'radio', sv.diskmode)
if sv.acpidebug:
sv.testVal(sv.acpipath, 'acpi', '0xe')
if mode == 'freeze' and sv.haveTurbostat():
# execution will pause here
turbo = sysvals.turbostat()
turbo = sv.turbostat()
if turbo:
tdata['turbo'] = turbo
else:
pf = open(sysvals.powerfile, 'w')
pf = open(sv.powerfile, 'w')
pf.write(mode)
# execution will pause here
try:
pf.close()
except Exception as e:
tdata['error'] = str(e)
if(sysvals.rtcwake):
sysvals.rtcWakeAlarmOff()
sv.dlog('system returned from resume')
# reset everything
sv.testVal('restoreall')
if(sv.rtcwake):
sv.dlog('disable RTC wake alarm')
sv.rtcWakeAlarmOff()
# postdelay delay
if(count == sysvals.execcount and sysvals.postdelay > 0):
sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
time.sleep(sysvals.postdelay/1000.0)
sysvals.fsetVal('WAIT END', 'trace_marker')
if(count == sv.execcount and sv.postdelay > 0):
sv.fsetVal('WAIT %d' % sv.postdelay, 'trace_marker')
time.sleep(sv.postdelay/1000.0)
sv.fsetVal('WAIT END', 'trace_marker')
# return from suspend
pprint('RESUME COMPLETE')
if(sysvals.usecallgraph or sysvals.usetraceevents):
sysvals.fsetVal(datetime.now().strftime(sysvals.tmend), 'trace_marker')
if sysvals.wifi and wifi:
tdata['wifi'] = sysvals.pollWifi(wifi)
if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
if(sv.usecallgraph or sv.usetraceevents):
sv.fsetVal(datetime.now().strftime(sv.tmend), 'trace_marker')
if sv.wifi and wifi:
tdata['wifi'] = sv.pollWifi(wifi)
sv.dlog('wifi check, %s' % tdata['wifi'])
if(sv.suspendmode == 'mem' or sv.suspendmode == 'command'):
sv.dlog('read the ACPI FPDT')
tdata['fw'] = getFPDT(False)
testdata.append(tdata)
cmdafter = sysvals.cmdinfo(False)
sv.dlog('run the cmdinfo list after')
cmdafter = sv.cmdinfo(False)
# stop ftrace
if(sysvals.usecallgraph or sysvals.usetraceevents):
if sysvals.useprocmon:
if(sv.usecallgraph or sv.usetraceevents):
if sv.useprocmon:
sv.dlog('stop the process monitor')
pm.stop()
sysvals.fsetVal('0', 'tracing_on')
sv.fsetVal('0', 'tracing_on')
# grab a copy of the dmesg output
if not quiet:
pprint('CAPTURING DMESG')
sysvals.getdmesg(testdata)
sysvals.dlog('EXECUTION TRACE END')
sv.getdmesg(testdata)
# grab a copy of the ftrace output
if(sysvals.usecallgraph or sysvals.usetraceevents):
if(sv.usecallgraph or sv.usetraceevents):
if not quiet:
pprint('CAPTURING TRACE')
op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
op = sv.writeDatafileHeader(sv.ftracefile, testdata)
fp = open(tp+'trace', 'r')
for line in fp:
op.write(line)
op.close()
sysvals.fsetVal('', 'trace')
sysvals.platforminfo(cmdafter)
sv.fsetVal('', 'trace')
sv.platforminfo(cmdafter)
def readFile(file):
if os.path.islink(file):
@ -5586,39 +5659,6 @@ def dmidecode(mempath, fatal=False):
count += 1
return out
def displayControl(cmd):
xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
if sysvals.sudouser:
xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
if cmd == 'init':
ret = call(xset.format('dpms 0 0 0'), shell=True)
if not ret:
ret = call(xset.format('s off'), shell=True)
elif cmd == 'reset':
ret = call(xset.format('s reset'), shell=True)
elif cmd in ['on', 'off', 'standby', 'suspend']:
b4 = displayControl('stat')
ret = call(xset.format('dpms force %s' % cmd), shell=True)
if not ret:
curr = displayControl('stat')
sysvals.vprint('Display Switched: %s -> %s' % (b4, curr))
if curr != cmd:
sysvals.vprint('WARNING: Display failed to change to %s' % cmd)
if ret:
sysvals.vprint('WARNING: Display failed to change to %s with xset' % cmd)
return ret
elif cmd == 'stat':
fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
ret = 'unknown'
for line in fp:
m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
if(m and len(m.group('m')) >= 2):
out = m.group('m').lower()
ret = out[3:] if out[0:2] == 'in' else out
break
fp.close()
return ret
# Function: getFPDT
# Description:
# Read the acpi bios tables and pull out FPDT, the firmware data
@ -6001,8 +6041,19 @@ def rerunTest(htmlfile=''):
# execute a suspend/resume, gather the logs, and generate the output
def runTest(n=0, quiet=False):
# prepare for the test
sysvals.initFtrace(quiet)
sysvals.initTestOutput('suspend')
op = sysvals.writeDatafileHeader(sysvals.dmesgfile, [])
op.write('# EXECUTION TRACE START\n')
op.close()
if n <= 1:
if sysvals.rs != 0:
sysvals.dlog('%sabling runtime suspend' % ('en' if sysvals.rs > 0 else 'dis'))
sysvals.setRuntimeSuspend(True)
if sysvals.display:
ret = sysvals.displayControl('init')
sysvals.dlog('xset display init, ret = %d' % ret)
sysvals.dlog('initialize ftrace')
sysvals.initFtrace(quiet)
# execute the test
executeSuspend(quiet)
@ -6098,8 +6149,16 @@ def data_from_html(file, outpath, issues, fulldetail=False):
if wifi:
extra['wifi'] = wifi
low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
if low and 'waking' in low:
issue = 'FREEZEWAKE'
for lowstr in ['waking', '+']:
if not low:
break
if lowstr not in low:
continue
if lowstr == '+':
issue = 'S2LOOPx%d' % len(low.split('+'))
else:
m = re.match('.*waking *(?P<n>[0-9]*) *times.*', low)
issue = 'S2WAKEx%s' % m.group('n') if m else 'S2WAKExNaN'
match = [i for i in issues if i['match'] == issue]
if len(match) > 0:
match[0]['count'] += 1
@ -6605,6 +6664,11 @@ if __name__ == '__main__':
val = next(args)
except:
doError('-info requires one string argument', True)
elif(arg == '-desc'):
try:
val = next(args)
except:
doError('-desc requires one string argument', True)
elif(arg == '-rs'):
try:
val = next(args)
@ -6814,9 +6878,9 @@ if __name__ == '__main__':
runSummary(sysvals.outdir, True, genhtml)
elif(cmd in ['xon', 'xoff', 'xstandby', 'xsuspend', 'xinit', 'xreset']):
sysvals.verbose = True
ret = displayControl(cmd[1:])
ret = sysvals.displayControl(cmd[1:])
elif(cmd == 'xstat'):
pprint('Display Status: %s' % displayControl('stat').upper())
pprint('Display Status: %s' % sysvals.displayControl('stat').upper())
elif(cmd == 'wificheck'):
dev = sysvals.checkWifi()
if dev:
@ -6854,12 +6918,8 @@ if __name__ == '__main__':
if mode.startswith('disk-'):
sysvals.diskmode = mode.split('-', 1)[-1]
sysvals.suspendmode = 'disk'
sysvals.systemInfo(dmidecode(sysvals.mempath))
setRuntimeSuspend(True)
if sysvals.display:
displayControl('init')
failcnt, ret = 0, 0
if sysvals.multitest['run']:
# run multiple tests in a separate subdirectory
@ -6900,7 +6960,10 @@ if __name__ == '__main__':
sysvals.testdir = sysvals.outdir
# run the test in the current directory
ret = runTest()
# reset to default values after testing
if sysvals.display:
displayControl('reset')
setRuntimeSuspend(False)
sysvals.displayControl('reset')
if sysvals.rs != 0:
sysvals.setRuntimeSuspend(False)
sys.exit(ret)