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
Родитель 42b4ca04cb d3569c149d f8edfa6d1e
Коммит b3fac81783
24 изменённых файлов: 775 добавлений и 598 удалений

Просмотреть файл

@ -37,20 +37,6 @@ Description:
The /sys/class/devfreq/.../target_freq shows the next governor The /sys/class/devfreq/.../target_freq shows the next governor
predicted target frequency of the corresponding devfreq object. 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 What: /sys/class/devfreq/.../trans_stat
Date: October 2012 Date: October 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com> Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
@ -66,14 +52,6 @@ Description:
echo 0 > /sys/class/devfreq/.../trans_stat 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 What: /sys/class/devfreq/.../available_frequencies
Date: October 2012 Date: October 2012
Contact: Nishanth Menon <nm@ti.com> Contact: Nishanth Menon <nm@ti.com>
@ -110,6 +88,35 @@ Description:
The max_freq overrides min_freq because max_freq may be The max_freq overrides min_freq because max_freq may be
used to throttle devices to avoid overheating. 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 What: /sys/class/devfreq/.../timer
Date: July 2020 Date: July 2020
Contact: Chanwoo Choi <cw00.choi@samsung.com> Contact: Chanwoo Choi <cw00.choi@samsung.com>
@ -122,3 +129,6 @@ Description:
echo deferrable > /sys/class/devfreq/.../timer echo deferrable > /sys/class/devfreq/.../timer
echo delayed > /sys/class/devfreq/.../timer echo delayed > /sys/class/devfreq/.../timer
A list of governors that support the node:
- simple_ondemand

Просмотреть файл

@ -51,6 +51,19 @@ Optional properties only for parent bus device:
- exynos,saturation-ratio: the percentage value which is used to calibrate - exynos,saturation-ratio: the percentage value which is used to calibrate
the performance count against total cycle count. 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: Detailed correlation between sub-blocks and power line according to Exynos SoC:
- In case of Exynos3250, there are two power line as following: - In case of Exynos3250, there are two power line as following:
VDD_MIF |--- DMC VDD_MIF |--- DMC
@ -135,7 +148,7 @@ Detailed correlation between sub-blocks and power line according to Exynos SoC:
|--- PERIC (Fixed clock rate) |--- PERIC (Fixed clock rate)
|--- FSYS (Fixed clock rate) |--- FSYS (Fixed clock rate)
Example1: Example 1:
Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to
power line (regulator). The MIF (Memory Interface) AXI bus is used 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. transfer data between DRAM and CPU and uses the VDD_MIF regulator.
@ -184,7 +197,7 @@ Example1:
|L5 |200000 |200000 |400000 |300000 | ||1000000 | |L5 |200000 |200000 |400000 |300000 | ||1000000 |
---------------------------------------------------------- ----------------------------------------------------------
Example2 : Example 2:
The bus of DMC (Dynamic Memory Controller) block in exynos3250.dtsi The bus of DMC (Dynamic Memory Controller) block in exynos3250.dtsi
is listed below: is listed below:
@ -419,3 +432,57 @@ Example2 :
devfreq = <&bus_leftbus>; devfreq = <&bus_leftbus>;
status = "okay"; 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>;
/* ... */
};
};

Просмотреть файл

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

Просмотреть файл

@ -13,6 +13,7 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/clk/tegra.h> #include <linux/clk/tegra.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/export.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.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; 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) 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; return 0;
} }
EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);

Просмотреть файл

@ -121,16 +121,6 @@ config ARM_TEGRA_DEVFREQ
It reads ACTMON counters of memory controllers and adjusts the It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support. 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 config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver" tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \ depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \

Просмотреть файл

@ -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_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
# DEVFREQ Event Drivers # DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/

Просмотреть файл

@ -31,6 +31,8 @@
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h> #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 #define HZ_PER_KHZ 1000
static struct class *devfreq_class; static struct class *devfreq_class;
@ -367,6 +369,14 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
return err; 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; freqs.new = new_freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); 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; 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. * @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 * Note: Lock devfreq->lock before calling devfreq_update_target. This function
* This function is exported for governors. * 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; int err = 0;
u32 flags = 0; u32 flags = 0;
@ -418,7 +429,21 @@ int update_devfreq(struct devfreq *devfreq)
} }
return devfreq_set_target(devfreq, freq, flags); 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); EXPORT_SYMBOL(update_devfreq);
@ -456,7 +481,7 @@ static void devfreq_monitor(struct work_struct *work)
*/ */
void devfreq_monitor_start(struct devfreq *devfreq) void devfreq_monitor_start(struct devfreq *devfreq)
{ {
if (devfreq->governor->interrupt_driven) if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return; return;
switch (devfreq->profile->timer) { switch (devfreq->profile->timer) {
@ -486,7 +511,7 @@ EXPORT_SYMBOL(devfreq_monitor_start);
*/ */
void devfreq_monitor_stop(struct devfreq *devfreq) void devfreq_monitor_stop(struct devfreq *devfreq)
{ {
if (devfreq->governor->interrupt_driven) if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return; return;
cancel_delayed_work_sync(&devfreq->work); cancel_delayed_work_sync(&devfreq->work);
@ -517,7 +542,7 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)
devfreq->stop_polling = true; devfreq->stop_polling = true;
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
if (devfreq->governor->interrupt_driven) if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return; return;
cancel_delayed_work_sync(&devfreq->work); cancel_delayed_work_sync(&devfreq->work);
@ -537,12 +562,13 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
unsigned long freq; unsigned long freq;
mutex_lock(&devfreq->lock); mutex_lock(&devfreq->lock);
if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
goto out_update;
if (!devfreq->stop_polling) if (!devfreq->stop_polling)
goto out; goto out;
if (devfreq->governor->interrupt_driven)
goto out_update;
if (!delayed_work_pending(&devfreq->work) && if (!delayed_work_pending(&devfreq->work) &&
devfreq->profile->polling_ms) devfreq->profile->polling_ms)
queue_delayed_work(devfreq_wq, &devfreq->work, 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); mutex_lock(&devfreq->lock);
devfreq->profile->polling_ms = new_delay; devfreq->profile->polling_ms = new_delay;
if (devfreq->stop_polling) if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
goto out; goto out;
if (devfreq->governor->interrupt_driven) if (devfreq->stop_polling)
goto out; goto out;
/* if new delay is zero, stop polling */ /* if new delay is zero, stop polling */
@ -735,6 +761,11 @@ static void devfreq_dev_release(struct device *dev)
kfree(devfreq); 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 * devfreq_add_device() - Add devfreq feature to the device
* @dev: the device to add devfreq feature. * @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; devfreq->dev.release = devfreq_dev_release;
INIT_LIST_HEAD(&devfreq->node); INIT_LIST_HEAD(&devfreq->node);
devfreq->profile = profile; devfreq->profile = profile;
strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
devfreq->previous_freq = profile->initial_freq; devfreq->previous_freq = profile->initial_freq;
devfreq->last_status.current_frequency = profile->initial_freq; devfreq->last_status.current_frequency = profile->initial_freq;
devfreq->data = data; devfreq->data = data;
@ -876,7 +906,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_lock(&devfreq_list_lock); mutex_lock(&devfreq_list_lock);
governor = try_then_request_governor(devfreq->governor_name); governor = try_then_request_governor(governor_name);
if (IS_ERR(governor)) { if (IS_ERR(governor)) {
dev_err(dev, "%s: Unable to find governor for the device\n", dev_err(dev, "%s: Unable to find governor for the device\n",
__func__); __func__);
@ -892,6 +922,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
__func__); __func__);
goto err_init; goto err_init;
} }
create_sysfs_files(devfreq, devfreq->governor);
list_add(&devfreq->node, &devfreq_list); list_add(&devfreq->node, &devfreq_list);
@ -922,9 +953,12 @@ int devfreq_remove_device(struct devfreq *devfreq)
if (!devfreq) if (!devfreq)
return -EINVAL; return -EINVAL;
if (devfreq->governor) if (devfreq->governor) {
devfreq->governor->event_handler(devfreq, devfreq->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL); DEVFREQ_GOV_STOP, NULL);
remove_sysfs_files(devfreq, devfreq->governor);
}
device_unregister(&devfreq->dev); device_unregister(&devfreq->dev);
return 0; return 0;
@ -1214,7 +1248,7 @@ int devfreq_add_governor(struct devfreq_governor *governor)
int ret = 0; int ret = 0;
struct device *dev = devfreq->dev.parent; struct device *dev = devfreq->dev.parent;
if (!strncmp(devfreq->governor_name, governor->name, if (!strncmp(devfreq->governor->name, governor->name,
DEVFREQ_NAME_LEN)) { DEVFREQ_NAME_LEN)) {
/* The following should never occur */ /* The following should never occur */
if (devfreq->governor) { if (devfreq->governor) {
@ -1276,7 +1310,7 @@ int devfreq_remove_governor(struct devfreq_governor *governor)
int ret; int ret;
struct device *dev = devfreq->dev.parent; struct device *dev = devfreq->dev.parent;
if (!strncmp(devfreq->governor_name, governor->name, if (!strncmp(devfreq->governor->name, governor->name,
DEVFREQ_NAME_LEN)) { DEVFREQ_NAME_LEN)) {
/* we should have a devfreq governor! */ /* we should have a devfreq governor! */
if (!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) { if (df->governor == governor) {
ret = 0; ret = 0;
goto out; 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; ret = -EINVAL;
goto out; 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); ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
if (ret) { if (ret) {
dev_warn(dev, "%s: Governor %s not stopped(%d)\n", dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
__func__, df->governor->name, ret); __func__, df->governor->name, ret);
goto out; 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; prev_governor = df->governor;
df->governor = governor; df->governor = governor;
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) { if (ret) {
dev_warn(dev, "%s: Governor %s not started(%d)\n", dev_warn(dev, "%s: Governor %s not started(%d)\n",
__func__, df->governor->name, ret); __func__, df->governor->name, ret);
/* Restore previous governor */
df->governor = prev_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); ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) { if (ret) {
dev_err(dev, dev_err(dev,
"%s: reverting to Governor %s failed (%d)\n", "%s: reverting to Governor %s failed (%d)\n",
__func__, df->governor_name, ret); __func__, prev_governor->name, ret);
df->governor = NULL; 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: out:
mutex_unlock(&devfreq_list_lock); 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 * The devfreq with immutable governor (e.g., passive) shows
* only own governor. * only own governor.
*/ */
if (df->governor->immutable) { if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
"%s ", df->governor_name); "%s ", df->governor->name);
/* /*
* The devfreq device shows the registered governor except for * The devfreq device shows the registered governor except for
* immutable governors such as passive governor . * immutable governors such as passive governor .
@ -1413,7 +1464,7 @@ static ssize_t available_governors_show(struct device *d,
struct devfreq_governor *governor; struct devfreq_governor *governor;
list_for_each_entry(governor, &devfreq_governor_list, node) { list_for_each_entry(governor, &devfreq_governor_list, node) {
if (governor->immutable) if (IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE))
continue; continue;
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
"%s ", governor->name); "%s ", governor->name);
@ -1458,39 +1509,6 @@ static ssize_t target_freq_show(struct device *dev,
} }
static DEVICE_ATTR_RO(target_freq); 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, static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) 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 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, static ssize_t timer_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@ -1761,21 +1826,36 @@ out:
} }
static DEVICE_ATTR_RW(timer); static DEVICE_ATTR_RW(timer);
static struct attribute *devfreq_attrs[] = { #define CREATE_SYSFS_FILE(df, name) \
&dev_attr_name.attr, { \
&dev_attr_governor.attr, int ret; \
&dev_attr_available_governors.attr, ret = sysfs_create_file(&df->dev.kobj, &dev_attr_##name.attr); \
&dev_attr_cur_freq.attr, if (ret < 0) { \
&dev_attr_available_frequencies.attr, dev_warn(&df->dev, \
&dev_attr_target_freq.attr, "Unable to create attr(%s)\n", "##name"); \
&dev_attr_polling_interval.attr, } \
&dev_attr_min_freq.attr, } \
&dev_attr_max_freq.attr,
&dev_attr_trans_stat.attr, /* Create the specific sysfs files which depend on each governor. */
&dev_attr_timer.attr, static void create_sysfs_files(struct devfreq *devfreq,
NULL, const struct devfreq_governor *gov)
}; {
ATTRIBUTE_GROUPS(devfreq); 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 * 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) { list_for_each_entry_reverse(devfreq, &devfreq_list, node) {
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) #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)) { DEVFREQ_NAME_LEN)) {
struct devfreq_passive_data *data = devfreq->data; 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); mutex_lock(&devfreq->lock);
cur_freq = devfreq->previous_freq; cur_freq = devfreq->previous_freq;
get_freq_range(devfreq, &min_freq, &max_freq); get_freq_range(devfreq, &min_freq, &max_freq);
polling_ms = devfreq->profile->polling_ms;
timer = devfreq->profile->timer; 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); mutex_unlock(&devfreq->lock);
seq_printf(s, seq_printf(s,
"%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n", "%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n",
dev_name(&devfreq->dev), dev_name(&devfreq->dev),
p_devfreq ? dev_name(&p_devfreq->dev) : "null", p_devfreq ? dev_name(&p_devfreq->dev) : "null",
devfreq->governor_name, devfreq->governor->name,
polling_ms ? timer_name[timer] : "null", polling_ms ? timer_name[timer] : "null",
polling_ms, polling_ms,
cur_freq, cur_freq,

Просмотреть файл

@ -24,6 +24,7 @@
struct exynos_bus { struct exynos_bus {
struct device *dev; struct device *dev;
struct platform_device *icc_pdev;
struct devfreq *devfreq; struct devfreq *devfreq;
struct devfreq_event_dev **edev; struct devfreq_event_dev **edev;
@ -156,6 +157,8 @@ static void exynos_bus_exit(struct device *dev)
if (ret < 0) if (ret < 0)
dev_warn(dev, "failed to disable the devfreq-event devices\n"); dev_warn(dev, "failed to disable the devfreq-event devices\n");
platform_device_unregister(bus->icc_pdev);
dev_pm_opp_of_remove_table(dev); dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk); clk_disable_unprepare(bus->clk);
dev_pm_opp_put_regulators(bus->opp_table); 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); struct exynos_bus *bus = dev_get_drvdata(dev);
platform_device_unregister(bus->icc_pdev);
dev_pm_opp_of_remove_table(dev); dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk); clk_disable_unprepare(bus->clk);
} }
@ -430,6 +435,18 @@ static int exynos_bus_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
goto err; 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; max_state = bus->devfreq->profile->max_state;
min_freq = (bus->devfreq->profile->freq_table[0] / 1000); min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000); max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);

Просмотреть файл

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

Просмотреть файл

@ -92,36 +92,6 @@ out:
return ret; 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, static int devfreq_passive_notifier_call(struct notifier_block *nb,
unsigned long event, void *ptr) 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 *parent = (struct devfreq *)data->parent;
struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr; struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr;
unsigned long freq = freqs->new; unsigned long freq = freqs->new;
int ret = 0;
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
switch (event) { switch (event) {
case DEVFREQ_PRECHANGE: case DEVFREQ_PRECHANGE:
if (parent->previous_freq > freq) if (parent->previous_freq > freq)
update_devfreq_passive(devfreq, freq); ret = devfreq_update_target(devfreq, freq);
break; break;
case DEVFREQ_POSTCHANGE: case DEVFREQ_POSTCHANGE:
if (parent->previous_freq < freq) if (parent->previous_freq < freq)
update_devfreq_passive(devfreq, freq); ret = devfreq_update_target(devfreq, freq);
break; break;
} }
mutex_unlock(&devfreq->lock);
if (ret < 0)
dev_warn(&devfreq->dev,
"failed to update devfreq using passive governor\n");
return NOTIFY_DONE; return NOTIFY_DONE;
} }
@ -180,7 +158,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
static struct devfreq_governor devfreq_passive = { static struct devfreq_governor devfreq_passive = {
.name = DEVFREQ_GOV_PASSIVE, .name = DEVFREQ_GOV_PASSIVE,
.immutable = 1, .flags = DEVFREQ_GOV_FLAG_IMMUTABLE,
.get_target_freq = devfreq_passive_get_target_freq, .get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler, .event_handler = devfreq_passive_event_handler,
}; };

Просмотреть файл

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

Просмотреть файл

@ -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");

Просмотреть файл

@ -19,6 +19,8 @@
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <soc/tegra/fuse.h>
#include "governor.h" #include "governor.h"
#define ACTMON_GLB_STATUS 0x0 #define ACTMON_GLB_STATUS 0x0
@ -55,13 +57,6 @@
#define ACTMON_BELOW_WMARK_WINDOW 3 #define ACTMON_BELOW_WMARK_WINDOW 3
#define ACTMON_BOOST_FREQ_STEP 16000 #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 * ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which
* translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128 * translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128
@ -109,7 +104,7 @@ enum tegra_actmon_device {
MCCPU, 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) */ /* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0, .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 * struct tegra_devfreq_device - state specific to an ACTMON device
* *
@ -153,8 +170,15 @@ struct tegra_devfreq_device {
unsigned long target_freq; 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 tegra_devfreq {
struct devfreq *devfreq; struct devfreq *devfreq;
struct opp_table *opp_table;
struct reset_control *reset; struct reset_control *reset;
struct clk *clock; struct clk *clock;
@ -168,11 +192,13 @@ struct tegra_devfreq {
struct delayed_work cpufreq_update_work; struct delayed_work cpufreq_update_work;
struct notifier_block cpu_rate_change_nb; 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; unsigned int irq;
bool started; bool started;
const struct tegra_devfreq_soc_data *soc;
}; };
struct tegra_actmon_emc_ratio { 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_avg_wmark(tegra, dev);
tegra_devfreq_update_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); device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
val |= ACTMON_DEV_CTRL_ENB_PERIODIC; 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, static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags) u32 flags)
{ {
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
unsigned long rate; int ret;
int err;
opp = devfreq_recommended_opp(dev, freq, flags); opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp)) { if (IS_ERR(opp)) {
dev_err(dev, "Failed to find opp for %lu Hz\n", *freq); dev_err(dev, "Failed to find opp for %lu Hz\n", *freq);
return PTR_ERR(opp); return PTR_ERR(opp);
} }
rate = dev_pm_opp_get_freq(opp);
ret = dev_pm_opp_set_bw(dev, opp);
dev_pm_opp_put(opp); dev_pm_opp_put(opp);
err = clk_set_min_rate(tegra->emc_clock, rate * KHZ); return ret;
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, 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; stat->private_data = tegra;
/* The below are to be used by the other governors */ /* 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]; 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); 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; return 0;
} }
@ -765,14 +781,16 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
static struct devfreq_governor tegra_devfreq_governor = { static struct devfreq_governor tegra_devfreq_governor = {
.name = "tegra_actmon", .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, .get_target_freq = tegra_governor_get_target,
.event_handler = tegra_governor_event_handler, .event_handler = tegra_governor_event_handler,
.immutable = true,
.interrupt_driven = true,
}; };
static int tegra_devfreq_probe(struct platform_device *pdev) 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_device *dev;
struct tegra_devfreq *tegra; struct tegra_devfreq *tegra;
struct devfreq *devfreq; struct devfreq *devfreq;
@ -784,6 +802,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
if (!tegra) if (!tegra)
return -ENOMEM; return -ENOMEM;
tegra->soc = of_device_get_match_data(&pdev->dev);
tegra->regs = devm_platform_ioremap_resource(pdev, 0); tegra->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tegra->regs)) if (IS_ERR(tegra->regs))
return PTR_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"); tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clock)) { if (IS_ERR(tegra->emc_clock))
dev_err(&pdev->dev, "Failed to get emc clock\n"); return dev_err_probe(&pdev->dev, PTR_ERR(tegra->emc_clock),
return PTR_ERR(tegra->emc_clock); "Failed to get emc clock\n");
}
err = platform_get_irq(pdev, 0); err = platform_get_irq(pdev, 0);
if (err < 0) if (err < 0)
@ -822,11 +841,25 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err; 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); err = clk_prepare_enable(tegra->clock);
if (err) { if (err) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Failed to prepare and enable ACTMON clock\n"); "Failed to prepare and enable ACTMON clock\n");
return err; goto remove_table;
} }
err = reset_control_reset(tegra->reset); err = reset_control_reset(tegra->reset);
@ -844,29 +877,12 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
tegra->max_freq = rate / KHZ; 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 = tegra->devices + i;
dev->config = actmon_device_configs + i; dev->config = tegra->soc->configs + i;
dev->regs = tegra->regs + dev->config->offset; 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); platform_set_drvdata(pdev, tegra);
tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb; 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 = clk_get_rate(tegra->emc_clock);
tegra_devfreq_profile.initial_freq /= KHZ;
devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile, devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL); "tegra_actmon", NULL);
@ -902,6 +917,10 @@ remove_opps:
reset_control_reset(tegra->reset); reset_control_reset(tegra->reset);
disable_clk: disable_clk:
clk_disable_unprepare(tegra->clock); 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; return err;
} }
@ -913,17 +932,33 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
devfreq_remove_device(tegra->devfreq); devfreq_remove_device(tegra->devfreq);
devfreq_remove_governor(&tegra_devfreq_governor); devfreq_remove_governor(&tegra_devfreq_governor);
dev_pm_opp_remove_all_dynamic(&pdev->dev);
reset_control_reset(tegra->reset); reset_control_reset(tegra->reset);
clk_disable_unprepare(tegra->clock); clk_disable_unprepare(tegra->clock);
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_supported_hw(tegra->opp_table);
return 0; 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[] = { static const struct of_device_id tegra_devfreq_of_match[] = {
{ .compatible = "nvidia,tegra30-actmon" }, { .compatible = "nvidia,tegra30-actmon", .data = &tegra30_soc, },
{ .compatible = "nvidia,tegra124-actmon" }, { .compatible = "nvidia,tegra124-actmon", .data = &tegra124_soc, },
{ }, { },
}; };

Просмотреть файл

@ -3,6 +3,7 @@
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*/ */
#include <linux/export.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.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; 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 = { static const struct of_device_id apbmisc_match[] __initconst = {
{ .compatible = "nvidia,tegra20-apbmisc", }, { .compatible = "nvidia,tegra20-apbmisc", },

Просмотреть файл

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

Просмотреть файл

@ -56,7 +56,11 @@ u32 tegra_read_straps(void);
u32 tegra_read_ram_code(void); u32 tegra_read_ram_code(void);
int tegra_fuse_readl(unsigned long offset, u32 *value); int tegra_fuse_readl(unsigned long offset, u32 *value);
#ifdef CONFIG_ARCH_TEGRA
extern struct tegra_sku_info tegra_sku_info; 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); struct device *tegra_soc_device_register(void);

Просмотреть файл

@ -8,6 +8,34 @@
#include <linux/devfreq.h> #include <linux/devfreq.h>
#include <linux/tracepoint.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, TRACE_EVENT(devfreq_monitor,
TP_PROTO(struct devfreq *devfreq), TP_PROTO(struct devfreq *devfreq),
@ -29,7 +57,7 @@ TRACE_EVENT(devfreq_monitor,
__assign_str(dev_name, dev_name(&devfreq->dev)); __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, __get_str(dev_name), __entry->freq, __entry->polling_ms,
__entry->total_time == 0 ? 0 : __entry->total_time == 0 ? 0 :
(100 * __entry->busy_time) / __entry->total_time) (100 * __entry->busy_time) / __entry->total_time)

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -94,6 +94,8 @@ struct cpupower_cpu_info {
*/ */
extern int get_cpu_info(struct cpupower_cpu_info *cpu_info); extern int get_cpu_info(struct cpupower_cpu_info *cpu_info);
extern struct cpupower_cpu_info cpupower_cpu_info; extern struct cpupower_cpu_info cpupower_cpu_info;
/* cpuid and cpuinfo helpers **************************/ /* cpuid and cpuinfo helpers **************************/
/* X86 ONLY ****************************************/ /* 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; }; static inline unsigned int cpuid_edx(unsigned int op) { return 0; };
#endif /* defined(__i386__) || defined(__x86_64__) */ #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__ */ #endif /* __CPUPOWERUTILS_HELPERS__ */

Просмотреть файл

@ -1,8 +1,12 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#if defined(__i386__) || defined(__x86_64__)
#include <stdio.h>
#include <stdlib.h>
#include "helpers/helpers.h" #include "helpers/helpers.h"
#if defined(__i386__) || defined(__x86_64__)
#define MSR_AMD_HWCR 0xc0010015 #define MSR_AMD_HWCR 0xc0010015
int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, 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; return 0;
} }
#endif /* #if defined(__i386__) || defined(__x86_64__) */ #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"));
}
}

Просмотреть файл

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

Просмотреть файл

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