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:
commit
b3fac81783
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
/* ... */
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) || \
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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/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, },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
@ -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", },
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue