Power management updates for 5.20-rc1

- Make cpufreq_show_cpus() more straightforward (Viresh Kumar).
 
  - Drop unnecessary CPU hotplug locking from store() used by cpufreq
    sysfs attributes (Viresh Kumar).
 
  - Make the ACPI cpufreq driver support the boost control interface on
    Zhaoxin/Centaur processors (Tony W Wang-oc).
 
  - Print a warning message on attempts to free an active cpufreq policy
    which should never happen (Viresh Kumar).
 
  - Fix grammar in the Kconfig help text for the loongson2 cpufreq
    driver (Randy Dunlap).
 
  - Use cpumask_var_t for an on-stack CPU mask in the ondemand cpufreq
    governor (Zhao Liu).
 
  - Add trace points for guest_halt_poll_ns grow/shrink to the haltpoll
    cpuidle driver (Eiichi Tsukata).
 
  - Modify intel_idle to treat C1 and C1E as independent idle states on
    Sapphire Rapids (Artem Bityutskiy).
 
  - Extend support for wakeirq to callback wrappers used during system
    suspend and resume (Ulf Hansson).
 
  - Defer waiting for device probe before loading a hibernation image
    till the first actual device access to avoid possible deadlocks
    reported by syzbot (Tetsuo Handa).
 
  - Unify device_init_wakeup() for PM_SLEEP and !PM_SLEEP (Bjorn
    Helgaas).
 
  - Add Raptor Lake-P to the list of processors supported by the Intel
    RAPL driver (George D Sworo).
 
  - Add Alder Lake-N and Raptor Lake-P to the list of processors for
    which Power Limit4 is supported in the Intel RAPL driver (Sumeet
    Pawnikar).
 
  - Make pm_genpd_remove() check genpd_debugfs_dir against NULL before
    attempting to remove it (Hsin-Yi Wang).
 
  - Change the Energy Model code to represent power in micro-Watts and
    adjust its users accordingly (Lukasz Luba).
 
  - Add new devfreq driver for Mediatek CCI (Cache Coherent
    Interconnect) (Johnson Wang).
 
  - Convert the Samsung Exynos SoC Bus bindings to DT schema of
    exynos-bus.c (Krzysztof Kozlowski).
 
  - Address kernel-doc warnings by adding the description for unused
    fucntion parameters in devfreq core (Mauro Carvalho Chehab).
 
  - Use NULL to pass a null pointer rather than zero according to the
    function propotype in imx-bus.c (Colin Ian King).
 
  - Print error message instead of error interger value in
    tegra30-devfreq.c (Dmitry Osipenko).
 
  - Add checks to prevent setting negative frequency QoS limits for
    CPUs (Shivnandan Kumar).
 
  - Update the pm-graph suite of utilities to the latest revision 5.9
    including multiple improvements (Todd Brandt).
 
  - Drop pme_interrupt reference from the PCI power management
    documentation (Mario Limonciello).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmLoKy8SHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRx3+oQAJNVU+W14EaRPWXQRMuwBC5zk3hb6T9q
 JqmMd8coEd+9/4ABAeRAWso1B26rUzB6JyBvw3lGH9OXInpYmvnJEhEPrTpK2h0D
 U9HxEARuGJolrDm0X9NAkn7tKKMC9GnvPS9z2s7s+N97VFFWC/QiU+PHB0SypGNb
 JxRfbVJZQCuxmNG9UeK+xeHFQ9lM2Z9ZdTxR71G0n7nQPPR+sUvnFufFby3Aogf3
 XnBYfia+YNqkUlefxxwB5a0cFwPXOUGsQkIf4d64gZnq1TgZ+71kht1GEF08PDFl
 wV8v1rOWuXEae8dozuf5xszp/eVyAqzgB+IShT9APREOO3Wg6I16XdBm8R1TGwCK
 JTdZqnm6HVKBNqchEwYViJILX69rrNUT+AwHBWhtKKDNh3qeTuwi/JGTeDVN++en
 xf3TNKx3LV31Nq6nWJFzDGLehfZMnAPkhfYohUBI7FNyblpk4mJRVcZ0bYI7UNnS
 als77uoipvb5KdFCtdhxYBHd/y867NvXKa1qsAuDxusAsfJHf4SnlMdbgOepBH2y
 jJg06CGrMDU3TZ8BL+WpqUYk4irQnAMs/159Txh7A6/dOnTjE7S9NHrENCwmt2og
 FrHSLH1eLX6Sa4RSibiGHPC7mNULP2/TOtryf3zFdlIVcjm3NEU3bnfzx7nlJn05
 8t6ObMxgMhWT
 =XeLV
 -----END PGP SIGNATURE-----

Merge tag 'pm-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management updates from Rafael Wysocki:
 "These are mostly minor improvements all over including new CPU IDs for
  the Intel RAPL driver, an Energy Model rework to use micro-Watt as the
  power unit, cpufreq fixes and cleanus, cpuidle updates, devfreq
  updates, documentation cleanups and a new version of the pm-graph
  suite of utilities.

  Specifics:

   - Make cpufreq_show_cpus() more straightforward (Viresh Kumar).

   - Drop unnecessary CPU hotplug locking from store() used by cpufreq
     sysfs attributes (Viresh Kumar).

   - Make the ACPI cpufreq driver support the boost control interface on
     Zhaoxin/Centaur processors (Tony W Wang-oc).

   - Print a warning message on attempts to free an active cpufreq
     policy which should never happen (Viresh Kumar).

   - Fix grammar in the Kconfig help text for the loongson2 cpufreq
     driver (Randy Dunlap).

   - Use cpumask_var_t for an on-stack CPU mask in the ondemand cpufreq
     governor (Zhao Liu).

   - Add trace points for guest_halt_poll_ns grow/shrink to the haltpoll
     cpuidle driver (Eiichi Tsukata).

   - Modify intel_idle to treat C1 and C1E as independent idle states on
     Sapphire Rapids (Artem Bityutskiy).

   - Extend support for wakeirq to callback wrappers used during system
     suspend and resume (Ulf Hansson).

   - Defer waiting for device probe before loading a hibernation image
     till the first actual device access to avoid possible deadlocks
     reported by syzbot (Tetsuo Handa).

   - Unify device_init_wakeup() for PM_SLEEP and !PM_SLEEP (Bjorn
     Helgaas).

   - Add Raptor Lake-P to the list of processors supported by the Intel
     RAPL driver (George D Sworo).

   - Add Alder Lake-N and Raptor Lake-P to the list of processors for
     which Power Limit4 is supported in the Intel RAPL driver (Sumeet
     Pawnikar).

   - Make pm_genpd_remove() check genpd_debugfs_dir against NULL before
     attempting to remove it (Hsin-Yi Wang).

   - Change the Energy Model code to represent power in micro-Watts and
     adjust its users accordingly (Lukasz Luba).

   - Add new devfreq driver for Mediatek CCI (Cache Coherent
     Interconnect) (Johnson Wang).

   - Convert the Samsung Exynos SoC Bus bindings to DT schema of
     exynos-bus.c (Krzysztof Kozlowski).

   - Address kernel-doc warnings by adding the description for unused
     function parameters in devfreq core (Mauro Carvalho Chehab).

   - Use NULL to pass a null pointer rather than zero according to the
     function propotype in imx-bus.c (Colin Ian King).

   - Print error message instead of error interger value in
     tegra30-devfreq.c (Dmitry Osipenko).

   - Add checks to prevent setting negative frequency QoS limits for
     CPUs (Shivnandan Kumar).

   - Update the pm-graph suite of utilities to the latest revision 5.9
     including multiple improvements (Todd Brandt).

   - Drop pme_interrupt reference from the PCI power management
     documentation (Mario Limonciello)"

* tag 'pm-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (27 commits)
  powercap: RAPL: Add Power Limit4 support for Alder Lake-N and Raptor Lake-P
  PM: QoS: Add check to make sure CPU freq is non-negative
  PM: hibernate: defer device probing when resuming from hibernation
  intel_idle: make SPR C1 and C1E be independent
  cpufreq: ondemand: Use cpumask_var_t for on-stack cpu mask
  cpufreq: loongson2: fix Kconfig "its" grammar
  pm-graph v5.9
  cpufreq: Warn users while freeing active policy
  cpufreq: scmi: Support the power scale in micro-Watts in SCMI v3.1
  firmware: arm_scmi: Get detailed power scale from perf
  Documentation: EM: Switch to micro-Watts scale
  PM: EM: convert power field to micro-Watts precision and align drivers
  PM / devfreq: tegra30: Add error message for devm_devfreq_add_device()
  PM / devfreq: imx-bus: use NULL to pass a null pointer rather than zero
  PM / devfreq: shut up kernel-doc warnings
  dt-bindings: interconnect: samsung,exynos-bus: convert to dtschema
  PM / devfreq: mediatek: Introduce MediaTek CCI devfreq driver
  dt-bindings: interconnect: Add MediaTek CCI dt-bindings
  PM: domains: Ensure genpd_debugfs_dir exists before remove
  PM: runtime: Extend support for wakeirq for force_suspend|resume
  ...
This commit is contained in:
Linus Torvalds 2022-08-02 11:17:00 -07:00
commit a771ea6413
41 changed files with 1499 additions and 828 deletions

View File

@ -1,488 +0,0 @@
* Generic Exynos Bus frequency device
The Samsung Exynos SoC has many buses for data transfer between DRAM
and sub-blocks in SoC. Most Exynos SoCs share the common architecture
for buses. Generally, each bus of Exynos SoC includes a source clock
and a power line, which are able to change the clock frequency
of the bus in runtime. To monitor the usage of each bus in runtime,
the driver uses the PPMU (Platform Performance Monitoring Unit), which
is able to measure the current load of sub-blocks.
The Exynos SoC includes the various sub-blocks which have the each AXI bus.
The each AXI bus has the owned source clock but, has not the only owned
power line. The power line might be shared among one more sub-blocks.
So, we can divide into two type of device as the role of each sub-block.
There are two type of bus devices as following:
- parent bus device
- passive bus device
Basically, parent and passive bus device share the same power line.
The parent bus device can only change the voltage of shared power line
and the rest bus devices (passive bus device) depend on the decision of
the parent bus device. If there are three blocks which share the VDD_xxx
power line, Only one block should be parent device and then the rest blocks
should depend on the parent device as passive device.
VDD_xxx |--- A block (parent)
|--- B block (passive)
|--- C block (passive)
There are a little different composition among Exynos SoC because each Exynos
SoC has different sub-blocks. Therefore, such difference should be specified
in devicetree file instead of each device driver. In result, this driver
is able to support the bus frequency for all Exynos SoCs.
Required properties for all bus devices:
- compatible: Should be "samsung,exynos-bus".
- clock-names : the name of clock used by the bus, "bus".
- clocks : phandles for clock specified in "clock-names" property.
- operating-points-v2: the OPP table including frequency/voltage information
to support DVFS (Dynamic Voltage/Frequency Scaling) feature.
Required properties only for parent bus device:
- vdd-supply: the regulator to provide the buses with the voltage.
- devfreq-events: the devfreq-event device to monitor the current utilization
of buses.
Required properties only for passive bus device:
- devfreq: the parent bus device.
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
VDD_INT |--- LEFTBUS (parent device)
|--- PERIL
|--- MFC
|--- G3D
|--- RIGHTBUS
|--- PERIR
|--- FSYS
|--- LCD0
|--- PERIR
|--- ISP
|--- CAM
- In case of Exynos4210, there is one power line as following:
VDD_INT |--- DMC (parent device)
|--- LEFTBUS
|--- PERIL
|--- MFC(L)
|--- G3D
|--- TV
|--- LCD0
|--- RIGHTBUS
|--- PERIR
|--- MFC(R)
|--- CAM
|--- FSYS
|--- GPS
|--- LCD0
|--- LCD1
- In case of Exynos4x12, there are two power line as following:
VDD_MIF |--- DMC
VDD_INT |--- LEFTBUS (parent device)
|--- PERIL
|--- MFC(L)
|--- G3D
|--- TV
|--- IMAGE
|--- RIGHTBUS
|--- PERIR
|--- MFC(R)
|--- CAM
|--- FSYS
|--- GPS
|--- LCD0
|--- ISP
- In case of Exynos5422, there are two power line as following:
VDD_MIF |--- DREX 0 (parent device, DRAM EXpress controller)
|--- DREX 1
VDD_INT |--- NoC_Core (parent device)
|--- G2D
|--- G3D
|--- DISP1
|--- NoC_WCORE
|--- GSCL
|--- MSCL
|--- ISP
|--- MFC
|--- GEN
|--- PERIS
|--- PERIC
|--- FSYS
|--- FSYS2
- In case of Exynos5433, there is VDD_INT power line as following:
VDD_INT |--- G2D (parent device)
|--- MSCL
|--- GSCL
|--- JPEG
|--- MFC
|--- HEVC
|--- BUS0
|--- BUS1
|--- BUS2
|--- PERIS (Fixed clock rate)
|--- PERIC (Fixed clock rate)
|--- FSYS (Fixed clock rate)
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.
- MIF (Memory Interface) block
: VDD_MIF |--- DMC (Dynamic Memory Controller)
- INT (Internal) block
: VDD_INT |--- LEFTBUS (parent device)
|--- PERIL
|--- MFC
|--- G3D
|--- RIGHTBUS
|--- FSYS
|--- LCD0
|--- PERIR
|--- ISP
|--- CAM
- MIF bus's frequency/voltage table
-----------------------
|Lv| Freq | Voltage |
-----------------------
|L1| 50000 |800000 |
|L2| 100000 |800000 |
|L3| 134000 |800000 |
|L4| 200000 |825000 |
|L5| 400000 |875000 |
-----------------------
- INT bus's frequency/voltage table
----------------------------------------------------------
|Block|LEFTBUS|RIGHTBUS|MCUISP |ISP |PERIL ||VDD_INT |
| name| |LCD0 | | | || |
| | |FSYS | | | || |
| | |MFC | | | || |
----------------------------------------------------------
|Mode |*parent|passive |passive|passive|passive|| |
----------------------------------------------------------
|Lv |Frequency ||Voltage |
----------------------------------------------------------
|L1 |50000 |50000 |50000 |50000 |50000 ||900000 |
|L2 |80000 |80000 |80000 |80000 |80000 ||900000 |
|L3 |100000 |100000 |100000 |100000 |100000 ||1000000 |
|L4 |134000 |134000 |200000 |200000 | ||1000000 |
|L5 |200000 |200000 |400000 |300000 | ||1000000 |
----------------------------------------------------------
Example 2:
The bus of DMC (Dynamic Memory Controller) block in exynos3250.dtsi
is listed below:
bus_dmc: bus_dmc {
compatible = "samsung,exynos-bus";
clocks = <&cmu_dmc CLK_DIV_DMC>;
clock-names = "bus";
operating-points-v2 = <&bus_dmc_opp_table>;
status = "disabled";
};
bus_dmc_opp_table: opp_table1 {
compatible = "operating-points-v2";
opp-shared;
opp-50000000 {
opp-hz = /bits/ 64 <50000000>;
opp-microvolt = <800000>;
};
opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <800000>;
};
opp-134000000 {
opp-hz = /bits/ 64 <134000000>;
opp-microvolt = <800000>;
};
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
opp-microvolt = <825000>;
};
opp-400000000 {
opp-hz = /bits/ 64 <400000000>;
opp-microvolt = <875000>;
};
};
bus_leftbus: bus_leftbus {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_GDL>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
status = "disabled";
};
bus_rightbus: bus_rightbus {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_GDR>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
status = "disabled";
};
bus_lcd0: bus_lcd0 {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_ACLK_160>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
status = "disabled";
};
bus_fsys: bus_fsys {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_ACLK_200>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
status = "disabled";
};
bus_mcuisp: bus_mcuisp {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_ACLK_400_MCUISP>;
clock-names = "bus";
operating-points-v2 = <&bus_mcuisp_opp_table>;
status = "disabled";
};
bus_isp: bus_isp {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_ACLK_266>;
clock-names = "bus";
operating-points-v2 = <&bus_isp_opp_table>;
status = "disabled";
};
bus_peril: bus_peril {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_ACLK_100>;
clock-names = "bus";
operating-points-v2 = <&bus_peril_opp_table>;
status = "disabled";
};
bus_mfc: bus_mfc {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_SCLK_MFC>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
status = "disabled";
};
bus_leftbus_opp_table: opp_table1 {
compatible = "operating-points-v2";
opp-shared;
opp-50000000 {
opp-hz = /bits/ 64 <50000000>;
opp-microvolt = <900000>;
};
opp-80000000 {
opp-hz = /bits/ 64 <80000000>;
opp-microvolt = <900000>;
};
opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <1000000>;
};
opp-134000000 {
opp-hz = /bits/ 64 <134000000>;
opp-microvolt = <1000000>;
};
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
opp-microvolt = <1000000>;
};
};
bus_mcuisp_opp_table: opp_table2 {
compatible = "operating-points-v2";
opp-shared;
opp-50000000 {
opp-hz = /bits/ 64 <50000000>;
};
opp-80000000 {
opp-hz = /bits/ 64 <80000000>;
};
opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
};
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
};
opp-400000000 {
opp-hz = /bits/ 64 <400000000>;
};
};
bus_isp_opp_table: opp_table3 {
compatible = "operating-points-v2";
opp-shared;
opp-50000000 {
opp-hz = /bits/ 64 <50000000>;
};
opp-80000000 {
opp-hz = /bits/ 64 <80000000>;
};
opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
};
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
};
opp-300000000 {
opp-hz = /bits/ 64 <300000000>;
};
};
bus_peril_opp_table: opp_table4 {
compatible = "operating-points-v2";
opp-shared;
opp-50000000 {
opp-hz = /bits/ 64 <50000000>;
};
opp-80000000 {
opp-hz = /bits/ 64 <80000000>;
};
opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
};
};
Usage case to handle the frequency and voltage of bus on runtime
in exynos3250-rinato.dts is listed below:
&bus_dmc {
devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>;
vdd-supply = <&buck1_reg>; /* VDD_MIF */
status = "okay";
};
&bus_leftbus {
devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>;
vdd-supply = <&buck3_reg>;
status = "okay";
};
&bus_rightbus {
devfreq = <&bus_leftbus>;
status = "okay";
};
&bus_lcd0 {
devfreq = <&bus_leftbus>;
status = "okay";
};
&bus_fsys {
devfreq = <&bus_leftbus>;
status = "okay";
};
&bus_mcuisp {
devfreq = <&bus_leftbus>;
status = "okay";
};
&bus_isp {
devfreq = <&bus_leftbus>;
status = "okay";
};
&bus_peril {
devfreq = <&bus_leftbus>;
status = "okay";
};
&bus_mfc {
devfreq = <&bus_leftbus>;
status = "okay";
};
Example 3:
An interconnect path "bus_display -- bus_leftbus -- bus_dmc" on
Exynos4412 SoC with video mixer as an interconnect consumer device.
soc {
bus_dmc: bus_dmc {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_DMC>;
clock-names = "bus";
operating-points-v2 = <&bus_dmc_opp_table>;
samsung,data-clock-ratio = <4>;
#interconnect-cells = <0>;
};
bus_leftbus: bus_leftbus {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_GDL>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
#interconnect-cells = <0>;
interconnects = <&bus_dmc>;
};
bus_display: bus_display {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_ACLK160>;
clock-names = "bus";
operating-points-v2 = <&bus_display_opp_table>;
#interconnect-cells = <0>;
interconnects = <&bus_leftbus &bus_dmc>;
};
bus_dmc_opp_table: opp_table1 {
compatible = "operating-points-v2";
/* ... */
}
bus_leftbus_opp_table: opp_table3 {
compatible = "operating-points-v2";
/* ... */
};
bus_display_opp_table: opp_table4 {
compatible = "operating-points-v2";
/* .. */
};
&mixer {
compatible = "samsung,exynos4212-mixer";
interconnects = <&bus_display &bus_dmc>;
/* ... */
};
};

View File

@ -0,0 +1,141 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/mediatek,cci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek Cache Coherent Interconnect (CCI) frequency and voltage scaling
maintainers:
- Jia-Wei Chang <jia-wei.chang@mediatek.com>
- Johnson Wang <johnson.wang@mediatek.com>
description: |
MediaTek Cache Coherent Interconnect (CCI) is a hardware engine used by
MT8183 and MT8186 SoCs to scale the frequency and adjust the voltage in
hardware. It can also optimize the voltage to reduce the power consumption.
properties:
compatible:
enum:
- mediatek,mt8183-cci
- mediatek,mt8186-cci
clocks:
items:
- description:
The multiplexer for clock input of the bus.
- description:
A parent of "bus" clock which is used as an intermediate clock source
when the original clock source (PLL) is under transition and not
stable yet.
clock-names:
items:
- const: cci
- const: intermediate
operating-points-v2: true
opp-table: true
proc-supply:
description:
Phandle of the regulator for CCI that provides the supply voltage.
sram-supply:
description:
Phandle of the regulator for sram of CCI that provides the supply
voltage. When it is present, the implementation needs to do
"voltage tracking" to step by step scale up/down Vproc and Vsram to fit
SoC specific needs. When absent, the voltage scaling flow is handled by
hardware, hence no software "voltage tracking" is needed.
required:
- compatible
- clocks
- clock-names
- operating-points-v2
- proc-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mt8183-clk.h>
cci: cci {
compatible = "mediatek,mt8183-cci";
clocks = <&mcucfg CLK_MCU_BUS_SEL>,
<&topckgen CLK_TOP_ARMPLL_DIV_PLL1>;
clock-names = "cci", "intermediate";
operating-points-v2 = <&cci_opp>;
proc-supply = <&mt6358_vproc12_reg>;
};
cci_opp: opp-table-cci {
compatible = "operating-points-v2";
opp-shared;
opp2_00: opp-273000000 {
opp-hz = /bits/ 64 <273000000>;
opp-microvolt = <650000>;
};
opp2_01: opp-338000000 {
opp-hz = /bits/ 64 <338000000>;
opp-microvolt = <687500>;
};
opp2_02: opp-403000000 {
opp-hz = /bits/ 64 <403000000>;
opp-microvolt = <718750>;
};
opp2_03: opp-463000000 {
opp-hz = /bits/ 64 <463000000>;
opp-microvolt = <756250>;
};
opp2_04: opp-546000000 {
opp-hz = /bits/ 64 <546000000>;
opp-microvolt = <800000>;
};
opp2_05: opp-624000000 {
opp-hz = /bits/ 64 <624000000>;
opp-microvolt = <818750>;
};
opp2_06: opp-689000000 {
opp-hz = /bits/ 64 <689000000>;
opp-microvolt = <850000>;
};
opp2_07: opp-767000000 {
opp-hz = /bits/ 64 <767000000>;
opp-microvolt = <868750>;
};
opp2_08: opp-845000000 {
opp-hz = /bits/ 64 <845000000>;
opp-microvolt = <893750>;
};
opp2_09: opp-871000000 {
opp-hz = /bits/ 64 <871000000>;
opp-microvolt = <906250>;
};
opp2_10: opp-923000000 {
opp-hz = /bits/ 64 <923000000>;
opp-microvolt = <931250>;
};
opp2_11: opp-962000000 {
opp-hz = /bits/ 64 <962000000>;
opp-microvolt = <943750>;
};
opp2_12: opp-1027000000 {
opp-hz = /bits/ 64 <1027000000>;
opp-microvolt = <975000>;
};
opp2_13: opp-1092000000 {
opp-hz = /bits/ 64 <1092000000>;
opp-microvolt = <1000000>;
};
opp2_14: opp-1144000000 {
opp-hz = /bits/ 64 <1144000000>;
opp-microvolt = <1025000>;
};
opp2_15: opp-1196000000 {
opp-hz = /bits/ 64 <1196000000>;
opp-microvolt = <1050000>;
};
};

View File

@ -0,0 +1,290 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/samsung,exynos-bus.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC Bus and Interconnect
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- Krzysztof Kozlowski <krzk@kernel.org>
description: |
The Samsung Exynos SoC has many buses for data transfer between DRAM and
sub-blocks in SoC. Most Exynos SoCs share the common architecture for buses.
Generally, each bus of Exynos SoC includes a source clock and a power line,
which are able to change the clock frequency of the bus in runtime. To
monitor the usage of each bus in runtime, the driver uses the PPMU (Platform
Performance Monitoring Unit), which is able to measure the current load of
sub-blocks.
The Exynos SoC includes the various sub-blocks which have the each AXI bus.
The each AXI bus has the owned source clock but, has not the only owned power
line. The power line might be shared among one more sub-blocks. So, we can
divide into two type of device as the role of each sub-block. There are two
type of bus devices as following::
- parent bus device
- passive bus device
Basically, parent and passive bus device share the same power line. The
parent bus device can only change the voltage of shared power line and the
rest bus devices (passive bus device) depend on the decision of the parent
bus device. If there are three blocks which share the VDD_xxx power line,
Only one block should be parent device and then the rest blocks should depend
on the parent device as passive device.
VDD_xxx |--- A block (parent)
|--- B block (passive)
|--- C block (passive)
There are a little different composition among Exynos SoC because each Exynos
SoC has different sub-blocks. Therefore, such difference should be specified
in devicetree file instead of each device driver. In result, this driver is
able to support the bus frequency for all Exynos SoCs.
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 (Dynamic Memory Controller)
VDD_INT |--- LEFTBUS (parent device)
|--- PERIL
|--- MFC
|--- G3D
|--- RIGHTBUS
|--- PERIR
|--- FSYS
|--- LCD0
|--- PERIR
|--- ISP
|--- CAM
- MIF bus's frequency/voltage table
-----------------------
|Lv| Freq | Voltage |
-----------------------
|L1| 50000 |800000 |
|L2| 100000 |800000 |
|L3| 134000 |800000 |
|L4| 200000 |825000 |
|L5| 400000 |875000 |
-----------------------
- INT bus's frequency/voltage table
----------------------------------------------------------
|Block|LEFTBUS|RIGHTBUS|MCUISP |ISP |PERIL ||VDD_INT |
| name| |LCD0 | | | || |
| | |FSYS | | | || |
| | |MFC | | | || |
----------------------------------------------------------
|Mode |*parent|passive |passive|passive|passive|| |
----------------------------------------------------------
|Lv |Frequency ||Voltage |
----------------------------------------------------------
|L1 |50000 |50000 |50000 |50000 |50000 ||900000 |
|L2 |80000 |80000 |80000 |80000 |80000 ||900000 |
|L3 |100000 |100000 |100000 |100000 |100000 ||1000000 |
|L4 |134000 |134000 |200000 |200000 | ||1000000 |
|L5 |200000 |200000 |400000 |300000 | ||1000000 |
----------------------------------------------------------
- In case of Exynos4210, there is one power line as following::
VDD_INT |--- DMC (parent device, Dynamic Memory Controller)
|--- LEFTBUS
|--- PERIL
|--- MFC(L)
|--- G3D
|--- TV
|--- LCD0
|--- RIGHTBUS
|--- PERIR
|--- MFC(R)
|--- CAM
|--- FSYS
|--- GPS
|--- LCD0
|--- LCD1
- In case of Exynos4x12, there are two power line as following::
VDD_MIF |--- DMC (Dynamic Memory Controller)
VDD_INT |--- LEFTBUS (parent device)
|--- PERIL
|--- MFC(L)
|--- G3D
|--- TV
|--- IMAGE
|--- RIGHTBUS
|--- PERIR
|--- MFC(R)
|--- CAM
|--- FSYS
|--- GPS
|--- LCD0
|--- ISP
- In case of Exynos5422, there are two power line as following::
VDD_MIF |--- DREX 0 (parent device, DRAM EXpress controller)
|--- DREX 1
VDD_INT |--- NoC_Core (parent device)
|--- G2D
|--- G3D
|--- DISP1
|--- NoC_WCORE
|--- GSCL
|--- MSCL
|--- ISP
|--- MFC
|--- GEN
|--- PERIS
|--- PERIC
|--- FSYS
|--- FSYS2
- In case of Exynos5433, there is VDD_INT power line as following::
VDD_INT |--- G2D (parent device)
|--- MSCL
|--- GSCL
|--- JPEG
|--- MFC
|--- HEVC
|--- BUS0
|--- BUS1
|--- BUS2
|--- PERIS (Fixed clock rate)
|--- PERIC (Fixed clock rate)
|--- FSYS (Fixed clock rate)
properties:
compatible:
enum:
- samsung,exynos-bus
clocks:
maxItems: 1
clock-names:
items:
- const: bus
devfreq:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Parent bus device. Valid and required only for the passive bus devices.
devfreq-events:
$ref: /schemas/types.yaml#/definitions/phandle-array
minItems: 1
maxItems: 4
description:
Devfreq-event device to monitor the current utilization of buses. Valid
and required only for the parent bus devices.
exynos,saturation-ratio:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Percentage value which is used to calibrate the performance count against
total cycle count. Valid only for the parent bus devices.
'#interconnect-cells':
const: 0
interconnects:
minItems: 1
maxItems: 2
operating-points-v2: true
samsung,data-clock-ratio:
$ref: /schemas/types.yaml#/definitions/uint32
default: 8
description:
Ratio of the data throughput in B/s to minimum data clock frequency in
Hz.
vdd-supply:
description:
Main bus power rail. Valid and required only for the parent bus devices.
required:
- compatible
- clocks
- clock-names
- operating-points-v2
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/exynos3250.h>
bus-dmc {
compatible = "samsung,exynos-bus";
clocks = <&cmu_dmc CLK_DIV_DMC>;
clock-names = "bus";
operating-points-v2 = <&bus_dmc_opp_table>;
devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>;
vdd-supply = <&buck1_reg>;
};
ppmu_dmc0: ppmu@106a0000 {
compatible = "samsung,exynos-ppmu";
reg = <0x106a0000 0x2000>;
events {
ppmu_dmc0_3: ppmu-event3-dmc0 {
event-name = "ppmu-event3-dmc0";
};
};
};
bus_leftbus: bus-leftbus {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_GDL>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>;
vdd-supply = <&buck3_reg>;
};
bus-rightbus {
compatible = "samsung,exynos-bus";
clocks = <&cmu CLK_DIV_GDR>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
devfreq = <&bus_leftbus>;
};
- |
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>;
devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>;
vdd-supply = <&buck1_reg>;
};
leftbus: bus-leftbus {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_GDL>;
clock-names = "bus";
operating-points-v2 = <&bus_leftbus_opp_table>;
interconnects = <&dmc>;
#interconnect-cells = <0>;
devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>;
vdd-supply = <&buck3_reg>;
};
display: bus-display {
compatible = "samsung,exynos-bus";
clocks = <&clock CLK_DIV_ACLK_266>;
clock-names = "bus";
operating-points-v2 = <&bus_display_opp_table>;
interconnects = <&leftbus &dmc>;
#interconnect-cells = <0>;
devfreq = <&leftbus>;
};

View File

@ -20,20 +20,20 @@ possible source of information on its own, the EM framework intervenes as an
abstraction layer which standardizes the format of power cost tables in the
kernel, hence enabling to avoid redundant work.
The power values might be expressed in milli-Watts or in an 'abstract scale'.
The power values might be expressed in micro-Watts or in an 'abstract scale'.
Multiple subsystems might use the EM and it is up to the system integrator to
check that the requirements for the power value scale types are met. An example
can be found in the Energy-Aware Scheduler documentation
Documentation/scheduler/sched-energy.rst. For some subsystems like thermal or
powercap power values expressed in an 'abstract scale' might cause issues.
These subsystems are more interested in estimation of power used in the past,
thus the real milli-Watts might be needed. An example of these requirements can
thus the real micro-Watts might be needed. An example of these requirements can
be found in the Intelligent Power Allocation in
Documentation/driver-api/thermal/power_allocator.rst.
Kernel subsystems might implement automatic detection to check whether EM
registered devices have inconsistent scale (based on EM internal flag).
Important thing to keep in mind is that when the power values are expressed in
an 'abstract scale' deriving real energy in milli-Joules would not be possible.
an 'abstract scale' deriving real energy in micro-Joules would not be possible.
The figure below depicts an example of drivers (Arm-specific here, but the
approach is applicable to any architecture) providing power costs to the EM
@ -98,7 +98,7 @@ Drivers are expected to register performance domains into the EM framework by
calling the following API::
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *cpus, bool milliwatts);
struct em_data_callback *cb, cpumask_t *cpus, bool microwatts);
Drivers must provide a callback function returning <frequency, power> tuples
for each performance state. The callback function provided by the driver is free
@ -106,10 +106,10 @@ to fetch data from any relevant location (DT, firmware, ...), and by any mean
deemed necessary. Only for CPU devices, drivers must specify the CPUs of the
performance domains using cpumask. For other devices than CPUs the last
argument must be set to NULL.
The last argument 'milliwatts' is important to set with correct value. Kernel
The last argument 'microwatts' is important to set with correct value. Kernel
subsystems which use EM might rely on this flag to check if all EM devices use
the same scale. If there are different scales, these subsystems might decide
to: return warning/error, stop working or panic.
to return warning/error, stop working or panic.
See Section 3. for an example of driver implementing this
callback, or Section 2.4 for further documentation on this API
@ -137,7 +137,7 @@ The .get_cost() allows to provide the 'cost' values which reflect the
efficiency of the CPUs. This would allow to provide EAS information which
has different relation than what would be forced by the EM internal
formulas calculating 'cost' values. To register an EM for such platform, the
driver must set the flag 'milliwatts' to 0, provide .get_power() callback
driver must set the flag 'microwatts' to 0, provide .get_power() callback
and provide .get_cost() callback. The EM framework would handle such platform
properly during registration. A flag EM_PERF_DOMAIN_ARTIFICIAL is set for such
platform. Special care should be taken by other frameworks which are using EM

View File

@ -315,7 +315,7 @@ that these callbacks operate on::
configuration space */
unsigned int pme_support:5; /* Bitmask of states from which PME#
can be generated */
unsigned int pme_interrupt:1;/* Is native PCIe PME signaling used? */
unsigned int pme_poll:1; /* Poll device's PME status bit */
unsigned int d1_support:1; /* Low power state D1 is supported */
unsigned int d2_support:1; /* Low power state D2 is supported */
unsigned int no_d1d2:1; /* D1 and D2 are forbidden */

View File

@ -4408,7 +4408,7 @@ L: linux-pm@vger.kernel.org
L: linux-samsung-soc@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
F: Documentation/devicetree/bindings/devfreq/exynos-bus.txt
F: Documentation/devicetree/bindings/interconnect/samsung,exynos-bus.yaml
F: drivers/devfreq/exynos-bus.c
BUSLOGIC SCSI DRIVER
@ -5890,6 +5890,7 @@ L: linux-pm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
F: Documentation/devicetree/bindings/devfreq/
F: Documentation/devicetree/bindings/interconnect/mediatek,cci.yaml
F: drivers/devfreq/
F: include/linux/devfreq.h
F: include/trace/events/devfreq.h

View File

@ -222,6 +222,9 @@ static void genpd_debug_remove(struct generic_pm_domain *genpd)
{
struct dentry *d;
if (!genpd_debugfs_dir)
return;
d = debugfs_lookup(genpd->name, genpd_debugfs_dir);
debugfs_remove(d);
}

View File

@ -1862,10 +1862,13 @@ int pm_runtime_force_suspend(struct device *dev)
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
dev_pm_enable_wake_irq_check(dev, true);
ret = callback ? callback(dev) : 0;
if (ret)
goto err;
dev_pm_enable_wake_irq_complete(dev);
/*
* If the device can stay in suspend after the system-wide transition
* to the working state that will follow, drop the children counter of
@ -1882,6 +1885,7 @@ int pm_runtime_force_suspend(struct device *dev)
return 0;
err:
dev_pm_disable_wake_irq_check(dev, true);
pm_runtime_enable(dev);
return ret;
}
@ -1915,9 +1919,11 @@ int pm_runtime_force_resume(struct device *dev)
callback = RPM_GET_CALLBACK(dev, runtime_resume);
dev_pm_disable_wake_irq_check(dev, false);
ret = callback ? callback(dev) : 0;
if (ret) {
pm_runtime_set_suspended(dev);
dev_pm_enable_wake_irq_check(dev, false);
goto out;
}

View File

@ -500,36 +500,6 @@ void device_set_wakeup_capable(struct device *dev, bool capable)
}
EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
/**
* device_init_wakeup - Device wakeup initialization.
* @dev: Device to handle.
* @enable: Whether or not to enable @dev as a wakeup device.
*
* By default, most devices should leave wakeup disabled. The exceptions are
* devices that everyone expects to be wakeup sources: keyboards, power buttons,
* possibly network interfaces, etc. Also, devices that don't generate their
* own wakeup requests but merely forward requests from one bus to another
* (like PCI bridges) should have wakeup enabled by default.
*/
int device_init_wakeup(struct device *dev, bool enable)
{
int ret = 0;
if (!dev)
return -EINVAL;
if (enable) {
device_set_wakeup_capable(dev, true);
ret = device_wakeup_enable(dev);
} else {
device_wakeup_disable(dev);
device_set_wakeup_capable(dev, false);
}
return ret;
}
EXPORT_SYMBOL_GPL(device_init_wakeup);
/**
* device_set_wakeup_enable - Enable or disable a device to wake up the system.
* @dev: Device to handle.

View File

@ -268,7 +268,7 @@ config LOONGSON2_CPUFREQ
This option adds a CPUFreq driver for loongson processors which
support software configurable cpu frequency.
Loongson2F and it's successors support this feature.
Loongson2F and its successors support this feature.
If in doubt, say N.

View File

@ -78,6 +78,8 @@ static bool boost_state(unsigned int cpu)
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_INTEL:
case X86_VENDOR_CENTAUR:
case X86_VENDOR_ZHAOXIN:
rdmsr_on_cpu(cpu, MSR_IA32_MISC_ENABLE, &lo, &hi);
msr = lo | ((u64)hi << 32);
return !(msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE);
@ -97,6 +99,8 @@ static int boost_set_msr(bool enable)
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_INTEL:
case X86_VENDOR_CENTAUR:
case X86_VENDOR_ZHAOXIN:
msr_addr = MSR_IA32_MISC_ENABLE;
msr_mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE;
break;

View File

@ -843,12 +843,14 @@ ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf)
unsigned int cpu;
for_each_cpu(cpu, mask) {
if (i)
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), " ");
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u ", cpu);
if (i >= (PAGE_SIZE - 5))
break;
}
/* Remove the extra space at the end */
i--;
i += sprintf(&buf[i], "\n");
return i;
}
@ -971,21 +973,10 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
if (!fattr->store)
return -EIO;
/*
* cpus_read_trylock() is used here to work around a circular lock
* dependency problem with respect to the cpufreq_register_driver().
*/
if (!cpus_read_trylock())
return -EBUSY;
if (cpu_online(policy->cpu)) {
down_write(&policy->rwsem);
if (likely(!policy_is_inactive(policy)))
ret = fattr->store(policy, buf, count);
up_write(&policy->rwsem);
}
cpus_read_unlock();
return ret;
}
@ -1282,6 +1273,13 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
unsigned long flags;
int cpu;
/*
* The callers must ensure the policy is inactive by now, to avoid any
* races with show()/store() callbacks.
*/
if (unlikely(!policy_is_inactive(policy)))
pr_warn("%s: Freeing active policy\n", __func__);
/* Remove policy from list */
write_lock_irqsave(&cpufreq_driver_lock, flags);
list_del(&policy->policy_list);
@ -1536,8 +1534,6 @@ out_destroy_policy:
for_each_cpu(j, policy->real_cpus)
remove_cpu_dev_symlink(policy, j, get_cpu_device(j));
cpumask_clear(policy->cpus);
out_offline_policy:
if (cpufreq_driver->offline)
cpufreq_driver->offline(policy);
@ -1547,6 +1543,7 @@ out_exit_policy:
cpufreq_driver->exit(policy);
out_free_policy:
cpumask_clear(policy->cpus);
up_write(&policy->rwsem);
cpufreq_policy_free(policy);

View File

@ -416,10 +416,13 @@ static struct dbs_governor od_dbs_gov = {
static void od_set_powersave_bias(unsigned int powersave_bias)
{
unsigned int cpu;
cpumask_t done;
cpumask_var_t done;
if (!alloc_cpumask_var(&done, GFP_KERNEL))
return;
default_powersave_bias = powersave_bias;
cpumask_clear(&done);
cpumask_clear(done);
cpus_read_lock();
for_each_online_cpu(cpu) {
@ -428,7 +431,7 @@ static void od_set_powersave_bias(unsigned int powersave_bias)
struct dbs_data *dbs_data;
struct od_dbs_tuners *od_tuners;
if (cpumask_test_cpu(cpu, &done))
if (cpumask_test_cpu(cpu, done))
continue;
policy = cpufreq_cpu_get_raw(cpu);
@ -439,13 +442,15 @@ static void od_set_powersave_bias(unsigned int powersave_bias)
if (!policy_dbs)
continue;
cpumask_or(&done, &done, policy->cpus);
cpumask_or(done, done, policy->cpus);
dbs_data = policy_dbs->dbs_data;
od_tuners = dbs_data->tuners;
od_tuners->powersave_bias = default_powersave_bias;
}
cpus_read_unlock();
free_cpumask_var(done);
}
void od_register_powersave_bias_handler(unsigned int (*f)

View File

@ -51,7 +51,7 @@ static const u16 cpufreq_mtk_offsets[REG_ARRAY_SIZE] = {
};
static int __maybe_unused
mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *mW,
mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *uW,
unsigned long *KHz)
{
struct mtk_cpufreq_data *data;
@ -71,8 +71,9 @@ mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *mW,
i--;
*KHz = data->table[i].frequency;
*mW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] +
i * LUT_ROW_SIZE) / 1000;
/* Provide micro-Watts value to the Energy Model */
*uW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] +
i * LUT_ROW_SIZE);
return 0;
}

View File

@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/scmi_protocol.h>
#include <linux/types.h>
#include <linux/units.h>
struct scmi_data {
int domain_id;
@ -99,6 +100,7 @@ static int __maybe_unused
scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
unsigned long *KHz)
{
enum scmi_power_scale power_scale = perf_ops->power_scale_get(ph);
unsigned long Hz;
int ret, domain;
@ -112,6 +114,10 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
if (ret)
return ret;
/* Convert the power to uW if it is mW (ignore bogoW) */
if (power_scale == SCMI_POWER_MILLIWATTS)
*power *= MICROWATT_PER_MILLIWATT;
/* The EM framework specifies the frequency in KHz. */
*KHz = Hz / 1000;
@ -249,8 +255,9 @@ static int scmi_cpufreq_exit(struct cpufreq_policy *policy)
static void scmi_cpufreq_register_em(struct cpufreq_policy *policy)
{
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
bool power_scale_mw = perf_ops->power_scale_mw_get(ph);
enum scmi_power_scale power_scale = perf_ops->power_scale_get(ph);
struct scmi_data *priv = policy->driver_data;
bool em_power_scale = false;
/*
* This callback will be called for each policy, but we don't need to
@ -262,9 +269,13 @@ static void scmi_cpufreq_register_em(struct cpufreq_policy *policy)
if (!priv->nr_opp)
return;
if (power_scale == SCMI_POWER_MILLIWATTS
|| power_scale == SCMI_POWER_MICROWATTS)
em_power_scale = true;
em_dev_register_perf_domain(get_cpu_device(policy->cpu), priv->nr_opp,
&em_cb, priv->opp_shared_cpus,
power_scale_mw);
em_power_scale);
}
static struct cpufreq_driver scmi_cpufreq_driver = {

View File

@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kvm_para.h>
#include <trace/events/power.h>
static unsigned int guest_halt_poll_ns __read_mostly = 200000;
module_param(guest_halt_poll_ns, uint, 0644);
@ -90,6 +91,7 @@ static void adjust_poll_limit(struct cpuidle_device *dev, u64 block_ns)
if (val > guest_halt_poll_ns)
val = guest_halt_poll_ns;
trace_guest_halt_poll_ns_grow(val, dev->poll_limit_ns);
dev->poll_limit_ns = val;
} else if (block_ns > guest_halt_poll_ns &&
guest_halt_poll_allow_shrink) {
@ -100,6 +102,7 @@ static void adjust_poll_limit(struct cpuidle_device *dev, u64 block_ns)
val = 0;
else
val /= shrink;
trace_guest_halt_poll_ns_shrink(val, dev->poll_limit_ns);
dev->poll_limit_ns = val;
}
}

View File

@ -120,6 +120,16 @@ config ARM_TEGRA_DEVFREQ
It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support.
config ARM_MEDIATEK_CCI_DEVFREQ
tristate "MEDIATEK CCI DEVFREQ Driver"
depends on ARM_MEDIATEK_CPUFREQ || COMPILE_TEST
select DEVFREQ_GOV_PASSIVE
help
This adds a devfreq driver for MediaTek Cache Coherent Interconnect
which is shared the same regulators with the cpu cluster. It can track
buck voltages and update a proper CCI frequency. Use the notification
to get the regulator status.
config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \

View File

@ -11,6 +11,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o

View File

@ -696,6 +696,8 @@ static int qos_notifier_call(struct devfreq *devfreq)
/**
* qos_min_notifier_call() - Callback for QoS min_freq changes.
* @nb: Should be devfreq->nb_min
* @val: not used
* @ptr: not used
*/
static int qos_min_notifier_call(struct notifier_block *nb,
unsigned long val, void *ptr)
@ -706,6 +708,8 @@ static int qos_min_notifier_call(struct notifier_block *nb,
/**
* qos_max_notifier_call() - Callback for QoS max_freq changes.
* @nb: Should be devfreq->nb_max
* @val: not used
* @ptr: not used
*/
static int qos_max_notifier_call(struct notifier_block *nb,
unsigned long val, void *ptr)

View File

@ -59,7 +59,7 @@ static int imx_bus_init_icc(struct device *dev)
struct imx_bus *priv = dev_get_drvdata(dev);
const char *icc_driver_name;
if (!of_get_property(dev->of_node, "#interconnect-cells", 0))
if (!of_get_property(dev->of_node, "#interconnect-cells", NULL))
return 0;
if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) {
dev_warn(dev, "imx interconnect drivers disabled\n");

View File

@ -0,0 +1,440 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 MediaTek Inc.
*/
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/regulator/consumer.h>
struct mtk_ccifreq_platform_data {
int min_volt_shift;
int max_volt_shift;
int proc_max_volt;
int sram_min_volt;
int sram_max_volt;
};
struct mtk_ccifreq_drv {
struct device *dev;
struct devfreq *devfreq;
struct regulator *proc_reg;
struct regulator *sram_reg;
struct clk *cci_clk;
struct clk *inter_clk;
int inter_voltage;
unsigned long pre_freq;
/* Avoid race condition for regulators between notify and policy */
struct mutex reg_lock;
struct notifier_block opp_nb;
const struct mtk_ccifreq_platform_data *soc_data;
int vtrack_max;
};
static int mtk_ccifreq_set_voltage(struct mtk_ccifreq_drv *drv, int new_voltage)
{
const struct mtk_ccifreq_platform_data *soc_data = drv->soc_data;
struct device *dev = drv->dev;
int pre_voltage, pre_vsram, new_vsram, vsram, voltage, ret;
int retry_max = drv->vtrack_max;
if (!drv->sram_reg) {
ret = regulator_set_voltage(drv->proc_reg, new_voltage,
drv->soc_data->proc_max_volt);
return ret;
}
pre_voltage = regulator_get_voltage(drv->proc_reg);
if (pre_voltage < 0) {
dev_err(dev, "invalid vproc value: %d\n", pre_voltage);
return pre_voltage;
}
pre_vsram = regulator_get_voltage(drv->sram_reg);
if (pre_vsram < 0) {
dev_err(dev, "invalid vsram value: %d\n", pre_vsram);
return pre_vsram;
}
new_vsram = clamp(new_voltage + soc_data->min_volt_shift,
soc_data->sram_min_volt, soc_data->sram_max_volt);
do {
if (pre_voltage <= new_voltage) {
vsram = clamp(pre_voltage + soc_data->max_volt_shift,
soc_data->sram_min_volt, new_vsram);
ret = regulator_set_voltage(drv->sram_reg, vsram,
soc_data->sram_max_volt);
if (ret)
return ret;
if (vsram == soc_data->sram_max_volt ||
new_vsram == soc_data->sram_min_volt)
voltage = new_voltage;
else
voltage = vsram - soc_data->min_volt_shift;
ret = regulator_set_voltage(drv->proc_reg, voltage,
soc_data->proc_max_volt);
if (ret) {
regulator_set_voltage(drv->sram_reg, pre_vsram,
soc_data->sram_max_volt);
return ret;
}
} else if (pre_voltage > new_voltage) {
voltage = max(new_voltage,
pre_vsram - soc_data->max_volt_shift);
ret = regulator_set_voltage(drv->proc_reg, voltage,
soc_data->proc_max_volt);
if (ret)
return ret;
if (voltage == new_voltage)
vsram = new_vsram;
else
vsram = max(new_vsram,
voltage + soc_data->min_volt_shift);
ret = regulator_set_voltage(drv->sram_reg, vsram,
soc_data->sram_max_volt);
if (ret) {
regulator_set_voltage(drv->proc_reg, pre_voltage,
soc_data->proc_max_volt);
return ret;
}
}
pre_voltage = voltage;
pre_vsram = vsram;
if (--retry_max < 0) {
dev_err(dev,
"over loop count, failed to set voltage\n");
return -EINVAL;
}
} while (voltage != new_voltage || vsram != new_vsram);
return 0;
}
static int mtk_ccifreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct mtk_ccifreq_drv *drv = dev_get_drvdata(dev);
struct clk *cci_pll = clk_get_parent(drv->cci_clk);
struct dev_pm_opp *opp;
unsigned long opp_rate;
int voltage, pre_voltage, inter_voltage, target_voltage, ret;
if (!drv)
return -EINVAL;
if (drv->pre_freq == *freq)
return 0;
inter_voltage = drv->inter_voltage;
opp_rate = *freq;
opp = devfreq_recommended_opp(dev, &opp_rate, 1);
if (IS_ERR(opp)) {
dev_err(dev, "failed to find opp for freq: %ld\n", opp_rate);
return PTR_ERR(opp);
}
mutex_lock(&drv->reg_lock);
voltage = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
pre_voltage = regulator_get_voltage(drv->proc_reg);
if (pre_voltage < 0) {
dev_err(dev, "invalid vproc value: %d\n", pre_voltage);
ret = pre_voltage;
goto out_unlock;
}
/* scale up: set voltage first then freq. */
target_voltage = max(inter_voltage, voltage);
if (pre_voltage <= target_voltage) {
ret = mtk_ccifreq_set_voltage(drv, target_voltage);
if (ret) {
dev_err(dev, "failed to scale up voltage\n");
goto out_restore_voltage;
}
}
/* switch the cci clock to intermediate clock source. */
ret = clk_set_parent(drv->cci_clk, drv->inter_clk);
if (ret) {
dev_err(dev, "failed to re-parent cci clock\n");
goto out_restore_voltage;
}
/* set the original clock to target rate. */
ret = clk_set_rate(cci_pll, *freq);
if (ret) {
dev_err(dev, "failed to set cci pll rate: %d\n", ret);
clk_set_parent(drv->cci_clk, cci_pll);
goto out_restore_voltage;
}
/* switch the cci clock back to the original clock source. */
ret = clk_set_parent(drv->cci_clk, cci_pll);
if (ret) {
dev_err(dev, "failed to re-parent cci clock\n");
mtk_ccifreq_set_voltage(drv, inter_voltage);
goto out_unlock;
}
/*
* If the new voltage is lower than the intermediate voltage or the
* original voltage, scale down to the new voltage.
*/
if (voltage < inter_voltage || voltage < pre_voltage) {
ret = mtk_ccifreq_set_voltage(drv, voltage);
if (ret) {
dev_err(dev, "failed to scale down voltage\n");
goto out_unlock;
}
}
drv->pre_freq = *freq;
mutex_unlock(&drv->reg_lock);
return 0;
out_restore_voltage:
mtk_ccifreq_set_voltage(drv, pre_voltage);
out_unlock:
mutex_unlock(&drv->reg_lock);
return ret;
}
static int mtk_ccifreq_opp_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct dev_pm_opp *opp = data;
struct mtk_ccifreq_drv *drv;
unsigned long freq, volt;
drv = container_of(nb, struct mtk_ccifreq_drv, opp_nb);
if (event == OPP_EVENT_ADJUST_VOLTAGE) {
freq = dev_pm_opp_get_freq(opp);
mutex_lock(&drv->reg_lock);
/* current opp item is changed */
if (freq == drv->pre_freq) {
volt = dev_pm_opp_get_voltage(opp);
mtk_ccifreq_set_voltage(drv, volt);
}
mutex_unlock(&drv->reg_lock);
}
return 0;
}
static struct devfreq_dev_profile mtk_ccifreq_profile = {
.target = mtk_ccifreq_target,
};
static int mtk_ccifreq_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_ccifreq_drv *drv;
struct devfreq_passive_data *passive_data;
struct dev_pm_opp *opp;
unsigned long rate, opp_volt;
int ret;
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
drv->dev = dev;
drv->soc_data = (const struct mtk_ccifreq_platform_data *)
of_device_get_match_data(&pdev->dev);
mutex_init(&drv->reg_lock);
platform_set_drvdata(pdev, drv);
drv->cci_clk = devm_clk_get(dev, "cci");
if (IS_ERR(drv->cci_clk)) {
ret = PTR_ERR(drv->cci_clk);
return dev_err_probe(dev, ret, "failed to get cci clk\n");
}
drv->inter_clk = devm_clk_get(dev, "intermediate");
if (IS_ERR(drv->inter_clk)) {
ret = PTR_ERR(drv->inter_clk);
return dev_err_probe(dev, ret,
"failed to get intermediate clk\n");
}
drv->proc_reg = devm_regulator_get_optional(dev, "proc");
if (IS_ERR(drv->proc_reg)) {
ret = PTR_ERR(drv->proc_reg);
return dev_err_probe(dev, ret,
"failed to get proc regulator\n");
}
ret = regulator_enable(drv->proc_reg);
if (ret) {
dev_err(dev, "failed to enable proc regulator\n");
return ret;
}
drv->sram_reg = devm_regulator_get_optional(dev, "sram");
if (IS_ERR(drv->sram_reg))
drv->sram_reg = NULL;
else {
ret = regulator_enable(drv->sram_reg);
if (ret) {
dev_err(dev, "failed to enable sram regulator\n");
goto out_free_resources;
}
}
/*
* We assume min voltage is 0 and tracking target voltage using
* min_volt_shift for each iteration.
* The retry_max is 3 times of expected iteration count.
*/
drv->vtrack_max = 3 * DIV_ROUND_UP(max(drv->soc_data->sram_max_volt,
drv->soc_data->proc_max_volt),
drv->soc_data->min_volt_shift);
ret = clk_prepare_enable(drv->cci_clk);
if (ret)
goto out_free_resources;
ret = dev_pm_opp_of_add_table(dev);
if (ret) {
dev_err(dev, "failed to add opp table: %d\n", ret);
goto out_disable_cci_clk;
}
rate = clk_get_rate(drv->inter_clk);
opp = dev_pm_opp_find_freq_ceil(dev, &rate);
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
dev_err(dev, "failed to get intermediate opp: %d\n", ret);
goto out_remove_opp_table;
}
drv->inter_voltage = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
rate = U32_MAX;
opp = dev_pm_opp_find_freq_floor(drv->dev, &rate);
if (IS_ERR(opp)) {
dev_err(dev, "failed to get opp\n");
ret = PTR_ERR(opp);
goto out_remove_opp_table;
}
opp_volt = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
ret = mtk_ccifreq_set_voltage(drv, opp_volt);
if (ret) {
dev_err(dev, "failed to scale to highest voltage %lu in proc_reg\n",
opp_volt);
goto out_remove_opp_table;
}
passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
if (!passive_data) {
ret = -ENOMEM;
goto out_remove_opp_table;
}
passive_data->parent_type = CPUFREQ_PARENT_DEV;
drv->devfreq = devm_devfreq_add_device(dev, &mtk_ccifreq_profile,
DEVFREQ_GOV_PASSIVE,
passive_data);
if (IS_ERR(drv->devfreq)) {
ret = -EPROBE_DEFER;
dev_err(dev, "failed to add devfreq device: %ld\n",
PTR_ERR(drv->devfreq));
goto out_remove_opp_table;
}
drv->opp_nb.notifier_call = mtk_ccifreq_opp_notifier;
ret = dev_pm_opp_register_notifier(dev, &drv->opp_nb);
if (ret) {
dev_err(dev, "failed to register opp notifier: %d\n", ret);
goto out_remove_opp_table;
}
return 0;
out_remove_opp_table:
dev_pm_opp_of_remove_table(dev);
out_disable_cci_clk:
clk_disable_unprepare(drv->cci_clk);
out_free_resources:
if (regulator_is_enabled(drv->proc_reg))
regulator_disable(drv->proc_reg);
if (drv->sram_reg && regulator_is_enabled(drv->sram_reg))
regulator_disable(drv->sram_reg);
return ret;
}
static int mtk_ccifreq_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_ccifreq_drv *drv;
drv = platform_get_drvdata(pdev);
dev_pm_opp_unregister_notifier(dev, &drv->opp_nb);
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(drv->cci_clk);
regulator_disable(drv->proc_reg);
if (drv->sram_reg)
regulator_disable(drv->sram_reg);
return 0;
}
static const struct mtk_ccifreq_platform_data mt8183_platform_data = {
.min_volt_shift = 100000,
.max_volt_shift = 200000,
.proc_max_volt = 1150000,
};
static const struct mtk_ccifreq_platform_data mt8186_platform_data = {
.min_volt_shift = 100000,
.max_volt_shift = 250000,
.proc_max_volt = 1118750,
.sram_min_volt = 850000,
.sram_max_volt = 1118750,
};
static const struct of_device_id mtk_ccifreq_machines[] = {
{ .compatible = "mediatek,mt8183-cci", .data = &mt8183_platform_data },
{ .compatible = "mediatek,mt8186-cci", .data = &mt8186_platform_data },
{ },
};
MODULE_DEVICE_TABLE(of, mtk_ccifreq_machines);
static struct platform_driver mtk_ccifreq_platdrv = {
.probe = mtk_ccifreq_probe,
.remove = mtk_ccifreq_remove,
.driver = {
.name = "mtk-ccifreq",
.of_match_table = mtk_ccifreq_machines,
},
};
module_platform_driver(mtk_ccifreq_platdrv);
MODULE_DESCRIPTION("MediaTek CCI devfreq driver");
MODULE_AUTHOR("Jia-Wei Chang <jia-wei.chang@mediatek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -922,8 +922,10 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
devfreq = devm_devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL);
if (IS_ERR(devfreq))
if (IS_ERR(devfreq)) {
dev_err(&pdev->dev, "Failed to add device: %pe\n", devfreq);
return PTR_ERR(devfreq);
}
return 0;
}

View File

@ -140,8 +140,7 @@ struct perf_dom_info {
struct scmi_perf_info {
u32 version;
int num_domains;
bool power_scale_mw;
bool power_scale_uw;
enum scmi_power_scale power_scale;
u64 stats_addr;
u32 stats_size;
struct perf_dom_info *dom_info;
@ -171,9 +170,13 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
u16 flags = le16_to_cpu(attr->flags);
pi->num_domains = le16_to_cpu(attr->num_domains);
pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags);
if (POWER_SCALE_IN_MILLIWATT(flags))
pi->power_scale = SCMI_POWER_MILLIWATTS;
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3)
pi->power_scale_uw = POWER_SCALE_IN_MICROWATT(flags);
if (POWER_SCALE_IN_MICROWATT(flags))
pi->power_scale = SCMI_POWER_MICROWATTS;
pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
(u64)le32_to_cpu(attr->stats_addr_high) << 32;
pi->stats_size = le32_to_cpu(attr->stats_size);
@ -675,11 +678,12 @@ static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph,
return dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr;
}
static bool scmi_power_scale_mw_get(const struct scmi_protocol_handle *ph)
static enum scmi_power_scale
scmi_power_scale_get(const struct scmi_protocol_handle *ph)
{
struct scmi_perf_info *pi = ph->get_priv(ph);
return pi->power_scale_mw;
return pi->power_scale;
}
static const struct scmi_perf_proto_ops perf_proto_ops = {
@ -694,7 +698,7 @@ static const struct scmi_perf_proto_ops perf_proto_ops = {
.freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get,
.fast_switch_possible = scmi_fast_switch_possible,
.power_scale_mw_get = scmi_power_scale_mw_get,
.power_scale_get = scmi_power_scale_get,
};
static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,

View File

@ -928,16 +928,6 @@ static struct cpuidle_state adl_l_cstates[] __initdata = {
.enter = NULL }
};
/*
* On Sapphire Rapids Xeon C1 has to be disabled if C1E is enabled, and vice
* versa. On SPR C1E is enabled only if "C1E promotion" bit is set in
* MSR_IA32_POWER_CTL. But in this case there effectively no C1, because C1
* requests are promoted to C1E. If the "C1E promotion" bit is cleared, then
* both C1 and C1E requests end up with C1, so there is effectively no C1E.
*
* By default we enable C1 and disable C1E by marking it with
* 'CPUIDLE_FLAG_UNUSABLE'.
*/
static struct cpuidle_state spr_cstates[] __initdata = {
{
.name = "C1",
@ -950,8 +940,7 @@ static struct cpuidle_state spr_cstates[] __initdata = {
{
.name = "C1E",
.desc = "MWAIT 0x01",
.flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_ALWAYS_ENABLE |
CPUIDLE_FLAG_UNUSABLE,
.flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_ALWAYS_ENABLE,
.exit_latency = 2,
.target_residency = 4,
.enter = &intel_idle,
@ -1774,17 +1763,6 @@ static void __init spr_idle_state_table_update(void)
{
unsigned long long msr;
/* Check if user prefers C1E over C1. */
if ((preferred_states_mask & BIT(2)) &&
!(preferred_states_mask & BIT(1))) {
/* Disable C1 and enable C1E. */
spr_cstates[0].flags |= CPUIDLE_FLAG_UNUSABLE;
spr_cstates[1].flags &= ~CPUIDLE_FLAG_UNUSABLE;
/* Enable C1E using the "C1E promotion" bit. */
c1e_promotion = C1E_PROMOTION_ENABLE;
}
/*
* By default, the C6 state assumes the worst-case scenario of package
* C6. However, if PC6 is disabled, we update the numbers to match

View File

@ -1443,12 +1443,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node);
* It provides the power used by @dev at @kHz if it is the frequency of an
* existing OPP, or at the frequency of the first OPP above @kHz otherwise
* (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
* frequency and @mW to the associated power.
* frequency and @uW to the associated power.
*
* Returns 0 on success or a proper -EINVAL value in case of error.
*/
static int __maybe_unused
_get_dt_power(struct device *dev, unsigned long *mW, unsigned long *kHz)
_get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz)
{
struct dev_pm_opp *opp;
unsigned long opp_freq, opp_power;
@ -1465,7 +1465,7 @@ _get_dt_power(struct device *dev, unsigned long *mW, unsigned long *kHz)
return -EINVAL;
*kHz = opp_freq / 1000;
*mW = opp_power / 1000;
*uW = opp_power;
return 0;
}
@ -1475,14 +1475,14 @@ _get_dt_power(struct device *dev, unsigned long *mW, unsigned long *kHz)
* This computes the power estimated by @dev at @kHz if it is the frequency
* of an existing OPP, or at the frequency of the first OPP above @kHz otherwise
* (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
* frequency and @mW to the associated power. The power is estimated as
* frequency and @uW to the associated power. The power is estimated as
* P = C * V^2 * f with C being the device's capacitance and V and f
* respectively the voltage and frequency of the OPP.
*
* Returns -EINVAL if the power calculation failed because of missing
* parameters, 0 otherwise.
*/
static int __maybe_unused _get_power(struct device *dev, unsigned long *mW,
static int __maybe_unused _get_power(struct device *dev, unsigned long *uW,
unsigned long *kHz)
{
struct dev_pm_opp *opp;
@ -1512,9 +1512,10 @@ static int __maybe_unused _get_power(struct device *dev, unsigned long *mW,
return -EINVAL;
tmp = (u64)cap * mV * mV * (Hz / 1000000);
do_div(tmp, 1000000000);
/* Provide power in micro-Watts */
do_div(tmp, 1000000);
*mW = (unsigned long)tmp;
*uW = (unsigned long)tmp;
*kHz = Hz / 1000;
return 0;

View File

@ -53,7 +53,7 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
for (i = 0; i < pd->nr_perf_states; i++) {
power = pd->table[i].power * MICROWATT_PER_MILLIWATT * nr_cpus;
power = pd->table[i].power * nr_cpus;
if (power > power_limit)
break;
@ -63,8 +63,7 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
freq_qos_update_request(&dtpm_cpu->qos_req, freq);
power_limit = pd->table[i - 1].power *
MICROWATT_PER_MILLIWATT * nr_cpus;
power_limit = pd->table[i - 1].power * nr_cpus;
return power_limit;
}

View File

@ -1109,6 +1109,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server),
X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core),

View File

@ -140,7 +140,9 @@ static const struct x86_cpu_id pl4_support_ids[] = {
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_TIGERLAKE_L, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE_L, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE_N, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_RAPTORLAKE, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_RAPTORLAKE_P, X86_FEATURE_ANY },
{}
};

View File

@ -21,6 +21,7 @@
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <linux/units.h>
#include <trace/events/thermal.h>
@ -101,6 +102,7 @@ static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev,
static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev,
u32 freq)
{
unsigned long power_mw;
int i;
for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) {
@ -108,16 +110,23 @@ static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev,
break;
}
return cpufreq_cdev->em->table[i + 1].power;
power_mw = cpufreq_cdev->em->table[i + 1].power;
power_mw /= MICROWATT_PER_MILLIWATT;
return power_mw;
}
static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev,
u32 power)
{
unsigned long em_power_mw;
int i;
for (i = cpufreq_cdev->max_level; i > 0; i--) {
if (power >= cpufreq_cdev->em->table[i].power)
/* Convert EM power to milli-Watts to make safe comparison */
em_power_mw = cpufreq_cdev->em->table[i].power;
em_power_mw /= MICROWATT_PER_MILLIWATT;
if (power >= em_power_mw)
break;
}

View File

@ -200,7 +200,11 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd
res = dfc->power_ops->get_real_power(df, power, freq, voltage);
if (!res) {
state = dfc->capped_state;
/* Convert EM power into milli-Watts first */
dfc->res_util = dfc->em_pd->table[state].power;
dfc->res_util /= MICROWATT_PER_MILLIWATT;
dfc->res_util *= SCALE_ERROR_MITIGATION;
if (*power > 1)
@ -218,8 +222,10 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd
_normalize_load(&status);
/* Scale power for utilization */
/* Convert EM power into milli-Watts first */
*power = dfc->em_pd->table[perf_idx].power;
*power /= MICROWATT_PER_MILLIWATT;
/* Scale power for utilization */
*power *= status.busy_time;
*power >>= 10;
}
@ -244,6 +250,7 @@ static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
perf_idx = dfc->max_state - state;
*power = dfc->em_pd->table[perf_idx].power;
*power /= MICROWATT_PER_MILLIWATT;
return 0;
}
@ -254,7 +261,7 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev,
struct devfreq_cooling_device *dfc = cdev->devdata;
struct devfreq *df = dfc->devfreq;
struct devfreq_dev_status status;
unsigned long freq;
unsigned long freq, em_power_mw;
s32 est_power;
int i;
@ -279,9 +286,13 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev,
* Find the first cooling state that is within the power
* budget. The EM power table is sorted ascending.
*/
for (i = dfc->max_state; i > 0; i--)
if (est_power >= dfc->em_pd->table[i].power)
for (i = dfc->max_state; i > 0; i--) {
/* Convert EM power to milli-Watts to make safe comparison */
em_power_mw = dfc->em_pd->table[i].power;
em_power_mw /= MICROWATT_PER_MILLIWATT;
if (est_power >= em_power_mw)
break;
}
*state = dfc->max_state - i;
dfc->capped_state = *state;

View File

@ -62,7 +62,7 @@ struct em_perf_domain {
/*
* em_perf_domain flags:
*
* EM_PERF_DOMAIN_MILLIWATTS: The power values are in milli-Watts or some
* EM_PERF_DOMAIN_MICROWATTS: The power values are in micro-Watts or some
* other scale.
*
* EM_PERF_DOMAIN_SKIP_INEFFICIENCIES: Skip inefficient states when estimating
@ -71,7 +71,7 @@ struct em_perf_domain {
* EM_PERF_DOMAIN_ARTIFICIAL: The power values are artificial and might be
* created by platform missing real power information
*/
#define EM_PERF_DOMAIN_MILLIWATTS BIT(0)
#define EM_PERF_DOMAIN_MICROWATTS BIT(0)
#define EM_PERF_DOMAIN_SKIP_INEFFICIENCIES BIT(1)
#define EM_PERF_DOMAIN_ARTIFICIAL BIT(2)
@ -79,22 +79,44 @@ struct em_perf_domain {
#define em_is_artificial(em) ((em)->flags & EM_PERF_DOMAIN_ARTIFICIAL)
#ifdef CONFIG_ENERGY_MODEL
#define EM_MAX_POWER 0xFFFF
/*
* The max power value in micro-Watts. The limit of 64 Watts is set as
* a safety net to not overflow multiplications on 32bit platforms. The
* 32bit value limit for total Perf Domain power implies a limit of
* maximum CPUs in such domain to 64.
*/
#define EM_MAX_POWER (64000000) /* 64 Watts */
/*
* Increase resolution of energy estimation calculations for 64-bit
* architectures. The extra resolution improves decision made by EAS for the
* task placement when two Performance Domains might provide similar energy
* estimation values (w/o better resolution the values could be equal).
*
* We increase resolution only if we have enough bits to allow this increased
* resolution (i.e. 64-bit). The costs for increasing resolution when 32-bit
* are pretty high and the returns do not justify the increased costs.
* To avoid possible energy estimation overflow on 32bit machines add
* limits to number of CPUs in the Perf. Domain.
* We are safe on 64bit machine, thus some big number.
*/
#ifdef CONFIG_64BIT
#define em_scale_power(p) ((p) * 1000)
#define EM_MAX_NUM_CPUS 4096
#else
#define em_scale_power(p) (p)
#define EM_MAX_NUM_CPUS 16
#endif
/*
* To avoid an overflow on 32bit machines while calculating the energy
* use a different order in the operation. First divide by the 'cpu_scale'
* which would reduce big value stored in the 'cost' field, then multiply by
* the 'sum_util'. This would allow to handle existing platforms, which have
* e.g. power ~1.3 Watt at max freq, so the 'cost' value > 1mln micro-Watts.
* In such scenario, where there are 4 CPUs in the Perf. Domain the 'sum_util'
* could be 4096, then multiplication: 'cost' * 'sum_util' would overflow.
* This reordering of operations has some limitations, we lose small
* precision in the estimation (comparing to 64bit platform w/o reordering).
*
* We are safe on 64bit machine.
*/
#ifdef CONFIG_64BIT
#define em_estimate_energy(cost, sum_util, scale_cpu) \
(((cost) * (sum_util)) / (scale_cpu))
#else
#define em_estimate_energy(cost, sum_util, scale_cpu) \
(((cost) / (scale_cpu)) * (sum_util))
#endif
struct em_data_callback {
@ -112,7 +134,7 @@ struct em_data_callback {
* and frequency.
*
* In case of CPUs, the power is the one of a single CPU in the domain,
* expressed in milli-Watts or an abstract scale. It is expected to
* expressed in micro-Watts or an abstract scale. It is expected to
* fit in the [0, EM_MAX_POWER] range.
*
* Return 0 on success.
@ -148,7 +170,7 @@ struct em_perf_domain *em_cpu_get(int cpu);
struct em_perf_domain *em_pd_get(struct device *dev);
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *span,
bool milliwatts);
bool microwatts);
void em_dev_unregister_perf_domain(struct device *dev);
/**
@ -273,7 +295,7 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
* pd_nrg = ------------------------ (4)
* scale_cpu
*/
return ps->cost * sum_util / scale_cpu;
return em_estimate_energy(ps->cost, sum_util, scale_cpu);
}
/**
@ -297,7 +319,7 @@ struct em_data_callback {};
static inline
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *span,
bool milliwatts)
bool microwatts)
{
return -EINVAL;
}

View File

@ -109,7 +109,6 @@ extern struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws);
extern int device_wakeup_enable(struct device *dev);
extern int device_wakeup_disable(struct device *dev);
extern void device_set_wakeup_capable(struct device *dev, bool capable);
extern int device_init_wakeup(struct device *dev, bool val);
extern int device_set_wakeup_enable(struct device *dev, bool enable);
extern void __pm_stay_awake(struct wakeup_source *ws);
extern void pm_stay_awake(struct device *dev);
@ -167,13 +166,6 @@ static inline int device_set_wakeup_enable(struct device *dev, bool enable)
return 0;
}
static inline int device_init_wakeup(struct device *dev, bool val)
{
device_set_wakeup_capable(dev, val);
device_set_wakeup_enable(dev, val);
return 0;
}
static inline bool device_may_wakeup(struct device *dev)
{
return dev->power.can_wakeup && dev->power.should_wakeup;
@ -217,4 +209,27 @@ static inline void pm_wakeup_hard_event(struct device *dev)
return pm_wakeup_dev_event(dev, 0, true);
}
/**
* device_init_wakeup - Device wakeup initialization.
* @dev: Device to handle.
* @enable: Whether or not to enable @dev as a wakeup device.
*
* By default, most devices should leave wakeup disabled. The exceptions are
* devices that everyone expects to be wakeup sources: keyboards, power buttons,
* possibly network interfaces, etc. Also, devices that don't generate their
* own wakeup requests but merely forward requests from one bus to another
* (like PCI bridges) should have wakeup enabled by default.
*/
static inline int device_init_wakeup(struct device *dev, bool enable)
{
if (enable) {
device_set_wakeup_capable(dev, true);
return device_wakeup_enable(dev);
} else {
device_wakeup_disable(dev);
device_set_wakeup_capable(dev, false);
return 0;
}
}
#endif /* _LINUX_PM_WAKEUP_H */

View File

@ -60,6 +60,12 @@ struct scmi_clock_info {
};
};
enum scmi_power_scale {
SCMI_POWER_BOGOWATTS,
SCMI_POWER_MILLIWATTS,
SCMI_POWER_MICROWATTS
};
struct scmi_handle;
struct scmi_device;
struct scmi_protocol_handle;
@ -135,7 +141,7 @@ struct scmi_perf_proto_ops {
unsigned long *rate, unsigned long *power);
bool (*fast_switch_possible)(const struct scmi_protocol_handle *ph,
struct device *dev);
bool (*power_scale_mw_get)(const struct scmi_protocol_handle *ph);
enum scmi_power_scale (*power_scale_get)(const struct scmi_protocol_handle *ph);
};
/**

View File

@ -500,6 +500,35 @@ DEFINE_EVENT(dev_pm_qos_request, dev_pm_qos_remove_request,
TP_ARGS(name, type, new_value)
);
TRACE_EVENT(guest_halt_poll_ns,
TP_PROTO(bool grow, unsigned int new, unsigned int old),
TP_ARGS(grow, new, old),
TP_STRUCT__entry(
__field(bool, grow)
__field(unsigned int, new)
__field(unsigned int, old)
),
TP_fast_assign(
__entry->grow = grow;
__entry->new = new;
__entry->old = old;
),
TP_printk("halt_poll_ns %u (%s %u)",
__entry->new,
__entry->grow ? "grow" : "shrink",
__entry->old)
);
#define trace_guest_halt_poll_ns_grow(new, old) \
trace_guest_halt_poll_ns(true, new, old)
#define trace_guest_halt_poll_ns_shrink(new, old) \
trace_guest_halt_poll_ns(false, new, old)
#endif /* _TRACE_POWER_H */
/* This part must be outside protection */

View File

@ -145,7 +145,7 @@ static int em_create_perf_table(struct device *dev, struct em_perf_domain *pd,
/*
* The power returned by active_state() is expected to be
* positive and to fit into 16 bits.
* positive and be in range.
*/
if (!power || power > EM_MAX_POWER) {
dev_err(dev, "EM: invalid power: %lu\n",
@ -170,7 +170,7 @@ static int em_create_perf_table(struct device *dev, struct em_perf_domain *pd,
goto free_ps_table;
}
} else {
power_res = em_scale_power(table[i].power);
power_res = table[i].power;
cost = div64_u64(fmax * power_res, table[i].frequency);
}
@ -201,9 +201,17 @@ static int em_create_pd(struct device *dev, int nr_states,
{
struct em_perf_domain *pd;
struct device *cpu_dev;
int cpu, ret;
int cpu, ret, num_cpus;
if (_is_cpu_device(dev)) {
num_cpus = cpumask_weight(cpus);
/* Prevent max possible energy calculation to not overflow */
if (num_cpus > EM_MAX_NUM_CPUS) {
dev_err(dev, "EM: too many CPUs, overflow possible\n");
return -EINVAL;
}
pd = kzalloc(sizeof(*pd) + cpumask_size(), GFP_KERNEL);
if (!pd)
return -ENOMEM;
@ -314,13 +322,13 @@ EXPORT_SYMBOL_GPL(em_cpu_get);
* @cpus : Pointer to cpumask_t, which in case of a CPU device is
* obligatory. It can be taken from i.e. 'policy->cpus'. For other
* type of devices this should be set to NULL.
* @milliwatts : Flag indicating that the power values are in milliWatts or
* @microwatts : Flag indicating that the power values are in micro-Watts or
* in some other scale. It must be set properly.
*
* Create Energy Model tables for a performance domain using the callbacks
* defined in cb.
*
* The @milliwatts is important to set with correct value. Some kernel
* The @microwatts is important to set with correct value. Some kernel
* sub-systems might rely on this flag and check if all devices in the EM are
* using the same scale.
*
@ -331,7 +339,7 @@ EXPORT_SYMBOL_GPL(em_cpu_get);
*/
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *cpus,
bool milliwatts)
bool microwatts)
{
unsigned long cap, prev_cap = 0;
unsigned long flags = 0;
@ -381,8 +389,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
}
}
if (milliwatts)
flags |= EM_PERF_DOMAIN_MILLIWATTS;
if (microwatts)
flags |= EM_PERF_DOMAIN_MICROWATTS;
else if (cb->get_cost)
flags |= EM_PERF_DOMAIN_ARTIFICIAL;

View File

@ -531,7 +531,7 @@ int freq_qos_add_request(struct freq_constraints *qos,
{
int ret;
if (IS_ERR_OR_NULL(qos) || !req)
if (IS_ERR_OR_NULL(qos) || !req || value < 0)
return -EINVAL;
if (WARN(freq_qos_request_active(req),
@ -563,7 +563,7 @@ EXPORT_SYMBOL_GPL(freq_qos_add_request);
*/
int freq_qos_update_request(struct freq_qos_request *req, s32 new_value)
{
if (!req)
if (!req || new_value < 0)
return -EINVAL;
if (WARN(!freq_qos_request_active(req),

View File

@ -26,6 +26,7 @@
#include "power.h"
static bool need_wait;
static struct snapshot_data {
struct snapshot_handle handle;
@ -78,7 +79,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
* Resuming. We may need to wait for the image device to
* appear.
*/
wait_for_device_probe();
need_wait = true;
data->swap = -1;
data->mode = O_WRONLY;
@ -168,6 +169,11 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,
ssize_t res;
loff_t pg_offp = *offp & ~PAGE_MASK;
if (need_wait) {
wait_for_device_probe();
need_wait = false;
}
lock_system_sleep();
data = filp->private_data;
@ -244,6 +250,11 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
loff_t size;
sector_t offset;
if (need_wait) {
wait_for_device_probe();
need_wait = false;
}
if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)

View File

@ -6,7 +6,7 @@
|_| |___/ |_|
pm-graph: suspend/resume/boot timing analysis tools
Version: 5.8
Version: 5.9
Author: Todd Brandt <todd.e.brandt@intel.com>
Home Page: https://01.org/pm-graph
@ -97,8 +97,8 @@
(kernel/pre-3.15/enable_trace_events_suspend_resume.patch)
(kernel/pre-3.15/enable_trace_events_device_pm_callback.patch)
If you're using a kernel older than 3.15.0, the following
additional kernel parameters are required:
If you're using bootgraph, or sleepgraph with a kernel older than 3.15.0,
the following additional kernel parameters are required:
(e.g. in file /etc/default/grub)
GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=32M ..."

View File

@ -69,20 +69,22 @@ class SystemValues(aslib.SystemValues):
bootloader = 'grub'
blexec = []
def __init__(self):
self.hostname = platform.node()
self.kernel, self.hostname = 'unknown', platform.node()
self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
if os.path.exists('/proc/version'):
fp = open('/proc/version', 'r')
val = fp.read().strip()
self.kernel = self.kernelVersion(fp.read().strip())
fp.close()
self.kernel = self.kernelVersion(val)
else:
self.kernel = 'unknown'
self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S')
def kernelVersion(self, msg):
return msg.split()[2]
m = re.match('^[Ll]inux *[Vv]ersion *(?P<v>\S*) .*', msg)
if m:
return m.group('v')
return 'unknown'
def checkFtraceKernelVersion(self):
val = tuple(map(int, self.kernel.split('-')[0].split('.')))
m = re.match('^(?P<x>[0-9]*)\.(?P<y>[0-9]*)\.(?P<z>[0-9]*).*', self.kernel)
if m:
val = tuple(map(int, m.groups()))
if val >= (4, 10, 0):
return True
return False

View File

@ -125,7 +125,7 @@ acpi_suspend_begin:
suspend_console:
acpi_pm_prepare:
syscore_suspend:
arch_thaw_secondary_cpus_end:
arch_enable_nonboot_cpus_end:
syscore_resume:
acpi_pm_finish:
resume_console:

View File

@ -66,7 +66,12 @@ from threading import Thread
from subprocess import call, Popen, PIPE
import base64
debugtiming = False
mystarttime = time.time()
def pprint(msg):
if debugtiming:
print('[%09.3f] %s' % (time.time()-mystarttime, msg))
else:
print(msg)
sys.stdout.flush()
@ -81,13 +86,14 @@ def ascii(text):
# store system values and test parameters
class SystemValues:
title = 'SleepGraph'
version = '5.8'
version = '5.9'
ansi = False
rs = 0
display = ''
gzip = False
sync = False
wifi = False
netfix = False
verbose = False
testlog = True
dmesglog = True
@ -108,6 +114,7 @@ class SystemValues:
cpucount = 0
memtotal = 204800
memfree = 204800
osversion = ''
srgap = 0
cgexp = False
testdir = ''
@ -116,6 +123,7 @@ class SystemValues:
fpdtpath = '/sys/firmware/acpi/tables/FPDT'
epath = '/sys/kernel/debug/tracing/events/power/'
pmdpath = '/sys/power/pm_debug_messages'
s0ixpath = '/sys/module/intel_pmc_core/parameters/warn_on_s0ix_failures'
acpipath='/sys/module/acpi/parameters/debug_level'
traceevents = [
'suspend_resume',
@ -156,6 +164,7 @@ class SystemValues:
ftop = False
usetraceevents = False
usetracemarkers = True
useftrace = True
usekprobes = True
usedevsrc = False
useprocmon = False
@ -279,10 +288,16 @@ class SystemValues:
'intel_fbdev_set_suspend': {},
}
infocmds = [
[0, 'sysinfo', 'uname', '-a'],
[0, 'cpuinfo', 'head', '-7', '/proc/cpuinfo'],
[0, 'kparams', 'cat', '/proc/cmdline'],
[0, 'mcelog', 'mcelog'],
[0, 'pcidevices', 'lspci', '-tv'],
[0, 'usbdevices', 'lsusb', '-t'],
[0, 'usbdevices', 'lsusb', '-tv'],
[0, 'acpidevices', 'sh', '-c', 'ls -l /sys/bus/acpi/devices/*/physical_node'],
[0, 's0ix_require', 'cat', '/sys/kernel/debug/pmc_core/substate_requirements'],
[0, 's0ix_debug', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_debug_status'],
[1, 's0ix_residency', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_residency_usec'],
[1, 'interrupts', 'cat', '/proc/interrupts'],
[1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
[2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
@ -358,8 +373,19 @@ class SystemValues:
self.outputResult({'error':msg})
sys.exit(1)
return False
def usable(self, file):
return (os.path.exists(file) and os.path.getsize(file) > 0)
def usable(self, file, ishtml=False):
if not os.path.exists(file) or os.path.getsize(file) < 1:
return False
if ishtml:
try:
fp = open(file, 'r')
res = fp.read(1000)
fp.close()
except:
return False
if '<html>' not in res:
return False
return True
def getExec(self, cmd):
try:
fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
@ -413,12 +439,16 @@ class SystemValues:
r = info['bios-release-date'] if 'bios-release-date' in info else ''
self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
(m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
if self.osversion:
self.sysstamp += ' | os:%s' % self.osversion
def printSystemInfo(self, fatal=False):
self.rootCheck(True)
out = dmidecode(self.mempath, fatal)
if len(out) < 1:
return
fmt = '%-24s: %s'
if self.osversion:
print(fmt % ('os-version', self.osversion))
for name in sorted(out):
print(fmt % (name, out[name]))
print(fmt % ('cpucount', ('%d' % self.cpucount)))
@ -426,12 +456,13 @@ class SystemValues:
print(fmt % ('memfree', ('%d kB' % self.memfree)))
def cpuInfo(self):
self.cpucount = 0
fp = open('/proc/cpuinfo', 'r')
if os.path.exists('/proc/cpuinfo'):
with open('/proc/cpuinfo', 'r') as fp:
for line in fp:
if re.match('^processor[ \t]*:[ \t]*[0-9]*', line):
self.cpucount += 1
fp.close()
fp = open('/proc/meminfo', 'r')
if os.path.exists('/proc/meminfo'):
with open('/proc/meminfo', 'r') as fp:
for line in fp:
m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line)
if m:
@ -439,7 +470,11 @@ class SystemValues:
m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line)
if m:
self.memfree = int(m.group('sz'))
fp.close()
if os.path.exists('/etc/os-release'):
with open('/etc/os-release', 'r') as fp:
for line in fp:
if line.startswith('PRETTY_NAME='):
self.osversion = line[12:].strip().replace('"', '')
def initTestOutput(self, name):
self.prefix = self.hostname
v = open('/proc/version', 'r').read().strip()
@ -698,6 +733,8 @@ class SystemValues:
return False
return True
def fsetVal(self, val, path):
if not self.useftrace:
return False
return self.setVal(val, self.tpath+path)
def getVal(self, file):
res = ''
@ -711,9 +748,11 @@ class SystemValues:
pass
return res
def fgetVal(self, path):
if not self.useftrace:
return ''
return self.getVal(self.tpath+path)
def cleanupFtrace(self):
if(self.usecallgraph or self.usetraceevents or self.usedevsrc):
if self.useftrace:
self.fsetVal('0', 'events/kprobes/enable')
self.fsetVal('', 'kprobe_events')
self.fsetVal('1024', 'buffer_size_kb')
@ -734,13 +773,14 @@ class SystemValues:
return True
return False
def initFtrace(self, quiet=False):
if not self.useftrace:
return
if not quiet:
sysvals.printSystemInfo(False)
pprint('INITIALIZING FTRACE...')
# turn trace off
self.fsetVal('0', 'tracing_on')
self.cleanupFtrace()
self.testVal(self.pmdpath, 'basic', '1')
# set the trace clock to global
self.fsetVal('global', 'trace_clock')
self.fsetVal('nop', 'current_tracer')
@ -766,6 +806,10 @@ class SystemValues:
# set trace type
self.fsetVal('function_graph', 'current_tracer')
self.fsetVal('', 'set_ftrace_filter')
# temporary hack to fix https://bugzilla.kernel.org/show_bug.cgi?id=212761
fp = open(self.tpath+'set_ftrace_notrace', 'w')
fp.write('native_queued_spin_lock_slowpath\ndev_driver_string')
fp.close()
# set trace format options
self.fsetVal('print-parent', 'trace_options')
self.fsetVal('funcgraph-abstime', 'trace_options')
@ -846,6 +890,8 @@ class SystemValues:
fp.write('# turbostat %s\n' % test['turbo'])
if 'wifi' in test:
fp.write('# wifi %s\n' % test['wifi'])
if 'netfix' in test:
fp.write('# netfix %s\n' % test['netfix'])
if test['error'] or len(testdata) > 1:
fp.write('# enter_sleep_error %s\n' % test['error'])
return fp
@ -865,6 +911,8 @@ class SystemValues:
fp.write('error%s: %s\n' % (n, testdata['error']))
else:
fp.write('result%s: pass\n' % n)
if 'mode' in testdata:
fp.write('mode%s: %s\n' % (n, testdata['mode']))
for v in ['suspend', 'resume', 'boot', 'lastinit']:
if v in testdata:
fp.write('%s%s: %.3f\n' % (v, n, testdata[v]))
@ -901,6 +949,8 @@ class SystemValues:
fp.write(text)
fp.close()
def dlog(self, text):
if not self.dmesgfile:
return
self.putlog(self.dmesgfile, '# %s\n' % text)
def flog(self, text):
self.putlog(self.ftracefile, text)
@ -954,34 +1004,31 @@ class SystemValues:
dirname = props[dev].syspath
if not dirname or not os.path.exists(dirname):
continue
with open(dirname+'/power/async') as fp:
text = fp.read()
props[dev].isasync = False
if 'enabled' in text:
if os.path.exists(dirname+'/power/async'):
fp = open(dirname+'/power/async')
if 'enabled' in fp.read():
props[dev].isasync = True
fp.close()
fields = os.listdir(dirname)
if 'product' in fields:
with open(dirname+'/product', 'rb') as fp:
for file in ['product', 'name', 'model', 'description', 'id', 'idVendor']:
if file not in fields:
continue
try:
with open(os.path.join(dirname, file), 'rb') as fp:
props[dev].altname = ascii(fp.read())
elif 'name' in fields:
with open(dirname+'/name', 'rb') as fp:
props[dev].altname = ascii(fp.read())
elif 'model' in fields:
with open(dirname+'/model', 'rb') as fp:
props[dev].altname = ascii(fp.read())
elif 'description' in fields:
with open(dirname+'/description', 'rb') as fp:
props[dev].altname = ascii(fp.read())
elif 'id' in fields:
with open(dirname+'/id', 'rb') as fp:
props[dev].altname = ascii(fp.read())
elif 'idVendor' in fields and 'idProduct' in fields:
idv, idp = '', ''
with open(dirname+'/idVendor', 'rb') as fp:
idv = ascii(fp.read()).strip()
with open(dirname+'/idProduct', 'rb') as fp:
except:
continue
if file == 'idVendor':
idv, idp = props[dev].altname.strip(), ''
try:
with open(os.path.join(dirname, 'idProduct'), 'rb') as fp:
idp = ascii(fp.read()).strip()
except:
props[dev].altname = ''
break
props[dev].altname = '%s:%s' % (idv, idp)
break
if props[dev].altname:
out = props[dev].altname.strip().replace('\n', ' ')\
.replace(',', ' ').replace(';', ' ')
@ -1047,7 +1094,7 @@ class SystemValues:
self.cmd1[name] = self.dictify(info, delta)
elif not debug and delta and name in self.cmd1:
before, after = self.cmd1[name], self.dictify(info, delta)
dinfo = ('\t%s\n' % before['@']) if '@' in before else ''
dinfo = ('\t%s\n' % before['@']) if '@' in before and len(before) > 1 else ''
prefix = self.commonPrefix(list(before.keys()))
for key in sorted(before):
if key in after and before[key] != after[key]:
@ -1128,6 +1175,22 @@ class SystemValues:
val = valline[idx]
out.append('%s=%s' % (key, val))
return '|'.join(out)
def netfixon(self, net='both'):
cmd = self.getExec('netfix')
if not cmd:
return ''
fp = Popen([cmd, '-s', net, 'on'], stdout=PIPE, stderr=PIPE).stdout
out = ascii(fp.read()).strip()
fp.close()
return out
def wifiRepair(self):
out = self.netfixon('wifi')
if not out or 'error' in out.lower():
return ''
m = re.match('WIFI \S* ONLINE (?P<action>\S*)', out)
if not m:
return 'dead'
return m.group('action')
def wifiDetails(self, dev):
try:
info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
@ -1144,12 +1207,12 @@ class SystemValues:
except:
return ''
for line in reversed(w.split('\n')):
m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', w.split('\n')[-1])
m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', line)
if not m or (dev and dev != m.group('dev')):
continue
return m.group('dev')
return ''
def pollWifi(self, dev, timeout=60):
def pollWifi(self, dev, timeout=10):
start = time.time()
while (time.time() - start) < timeout:
w = self.checkWifi(dev)
@ -1157,6 +1220,11 @@ class SystemValues:
return '%s reconnected %.2f' % \
(self.wifiDetails(dev), max(0, time.time() - start))
time.sleep(0.01)
if self.netfix:
res = self.wifiRepair()
if res:
timeout = max(0, time.time() - start)
return '%s %s %d' % (self.wifiDetails(dev), res, timeout)
return '%s timeout %d' % (self.wifiDetails(dev), timeout)
def errorSummary(self, errinfo, msg):
found = False
@ -1283,10 +1351,10 @@ sysvals = SystemValues()
switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
switchoff = ['disable', 'off', 'false', '0']
suspendmodename = {
'freeze': 'Freeze (S0)',
'standby': 'Standby (S1)',
'mem': 'Suspend (S3)',
'disk': 'Hibernate (S4)'
'standby': 'standby (S1)',
'freeze': 'freeze (S2idle)',
'mem': 'suspend (S3)',
'disk': 'hibernate (S4)'
}
# Class: DevProps
@ -1376,6 +1444,7 @@ class Data:
'INVALID' : r'(?i).*\bINVALID\b.*',
'CRASH' : r'(?i).*\bCRASHED\b.*',
'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*',
'ABORT' : r'(?i).*\bABORT\b.*',
'IRQ' : r'.*\bgenirq: .*',
'TASKFAIL': r'.*Freezing of tasks *.*',
'ACPI' : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
@ -1724,9 +1793,9 @@ class Data:
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))
tTry = '%.0f' % (round(self.dmesg[lp]['waking'][1] * 1000))
else:
tTry = '-%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
tTry = '%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
text = '%.0f (%s ms waking %d times)' % (tL * 1000, tTry, tCnt)
else:
text = '%.0f' % (tL * 1000)
@ -2107,6 +2176,30 @@ class Data:
# set resume complete to end at end marker
if 'resume_complete' in dm:
dm['resume_complete']['end'] = time
def initcall_debug_call(self, line, quick=False):
m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
'PM: *calling .* @ (?P<n>.*), parent: (?P<p>.*)', line)
if not m:
m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
'calling .* @ (?P<n>.*), parent: (?P<p>.*)', line)
if not m:
m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling '+\
'(?P<f>.*)\+ @ (?P<n>.*), parent: (?P<p>.*)', line)
if m:
return True if quick else m.group('t', 'f', 'n', 'p')
return False if quick else ('', '', '', '')
def initcall_debug_return(self, line, quick=False):
m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: PM: '+\
'.* returned (?P<r>[0-9]*) after (?P<dt>[0-9]*) usecs', line)
if not m:
m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
'.* returned (?P<r>[0-9]*) after (?P<dt>[0-9]*) usecs', line)
if not m:
m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
'(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', line)
if m:
return True if quick else m.group('t', 'f', 'dt')
return False if quick else ('', '', '')
def debugPrint(self):
for p in self.sortedPhases():
list = self.dmesg[p]['list']
@ -2880,10 +2973,11 @@ class TestProps:
cmdlinefmt = '^# command \| (?P<cmd>.*)'
kparamsfmt = '^# kparams \| (?P<kp>.*)'
devpropfmt = '# Device Properties: .*'
pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9,_]*): (?P<info>.*)'
tracertypefmt = '# tracer: (?P<t>.*)'
firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
procexecfmt = 'ps - (?P<ps>.*)$'
procmultifmt = '@(?P<n>[0-9]*)\|(?P<ps>.*)$'
ftrace_line_fmt_fg = \
'^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
@ -2893,6 +2987,9 @@ class TestProps:
'(?P<flags>\S*) *(?P<time>[0-9\.]*): *'+\
'(?P<msg>.*)'
machinesuspend = 'machine_suspend\[.*'
multiproclist = dict()
multiproctime = 0.0
multiproccnt = 0
def __init__(self):
self.stamp = ''
self.sysinfo = ''
@ -3063,6 +3160,7 @@ class TestRun:
self.ttemp = dict()
class ProcessMonitor:
maxchars = 512
def __init__(self):
self.proclist = dict()
self.running = False
@ -3088,19 +3186,23 @@ class ProcessMonitor:
if ujiff > 0 or kjiff > 0:
running[pid] = ujiff + kjiff
process.wait()
out = ''
out = ['']
for pid in running:
jiffies = running[pid]
val = self.proclist[pid]
if out:
out += ','
out += '%s-%s %d' % (val['name'], pid, jiffies)
return 'ps - '+out
if len(out[-1]) > self.maxchars:
out.append('')
elif len(out[-1]) > 0:
out[-1] += ','
out[-1] += '%s-%s %d' % (val['name'], pid, jiffies)
if len(out) > 1:
for line in out:
sysvals.fsetVal('ps - @%d|%s' % (len(out), line), 'trace_marker')
else:
sysvals.fsetVal('ps - %s' % out[0], 'trace_marker')
def processMonitor(self, tid):
while self.running:
out = self.procstat()
if out:
sysvals.fsetVal(out, 'trace_marker')
self.procstat()
def start(self):
self.thread = Thread(target=self.processMonitor, args=(0,))
self.running = True
@ -3144,7 +3246,6 @@ def doesTraceLogHaveTraceEvents():
# Function: appendIncompleteTraceLog
# Description:
# [deprecated for kernel 3.15 or newer]
# Adds callgraph data which lacks trace event data. This is only
# for timelines generated from 3.15 or older
# Arguments:
@ -3246,6 +3347,61 @@ def appendIncompleteTraceLog(testruns):
dev['ftrace'] = cg
break
# Function: loadTraceLog
# Description:
# load the ftrace file into memory and fix up any ordering issues
# Output:
# TestProps instance and an array of lines in proper order
def loadTraceLog():
tp, data, lines, trace = TestProps(), dict(), [], []
tf = sysvals.openlog(sysvals.ftracefile, 'r')
for line in tf:
# remove any latent carriage returns
line = line.replace('\r\n', '')
if tp.stampInfo(line, sysvals):
continue
# ignore all other commented lines
if line[0] == '#':
continue
# ftrace line: parse only valid lines
m = re.match(tp.ftrace_line_fmt, line)
if(not m):
continue
dur = m.group('dur') if tp.cgformat else 'traceevent'
info = (m.group('time'), m.group('proc'), m.group('pid'),
m.group('msg'), dur)
# group the data by timestamp
t = float(info[0])
if t in data:
data[t].append(info)
else:
data[t] = [info]
# we only care about trace event ordering
if (info[3].startswith('suspend_resume:') or \
info[3].startswith('tracing_mark_write:')) and t not in trace:
trace.append(t)
tf.close()
for t in sorted(data):
first, last, blk = [], [], data[t]
if len(blk) > 1 and t in trace:
# move certain lines to the start or end of a timestamp block
for i in range(len(blk)):
if 'SUSPEND START' in blk[i][3]:
first.append(i)
elif re.match('.* timekeeping_freeze.*begin', blk[i][3]):
last.append(i)
elif re.match('.* timekeeping_freeze.*end', blk[i][3]):
first.append(i)
elif 'RESUME COMPLETE' in blk[i][3]:
last.append(i)
if len(first) == 1 and len(last) == 0:
blk.insert(0, blk.pop(first[0]))
elif len(last) == 1 and len(first) == 0:
blk.append(blk.pop(last[0]))
for info in blk:
lines.append(info)
return (tp, lines)
# Function: parseTraceLog
# Description:
# Analyze an ftrace log output file generated from this app during
@ -3271,32 +3427,12 @@ def parseTraceLog(live=False):
# extract the callgraph and traceevent data
s2idle_enter = hwsus = False
tp = TestProps()
testruns, testdata = [], []
testrun, data, limbo = 0, 0, True
tf = sysvals.openlog(sysvals.ftracefile, 'r')
phase = 'suspend_prepare'
for line in tf:
# remove any latent carriage returns
line = line.replace('\r\n', '')
if tp.stampInfo(line, sysvals):
continue
# ignore all other commented lines
if line[0] == '#':
continue
# ftrace line: parse only valid lines
m = re.match(tp.ftrace_line_fmt, line)
if(not m):
continue
tp, tf = loadTraceLog()
for m_time, m_proc, m_pid, m_msg, m_param3 in tf:
# gather the basic message data from the line
m_time = m.group('time')
m_proc = m.group('proc')
m_pid = m.group('pid')
m_msg = m.group('msg')
if(tp.cgformat):
m_param3 = m.group('dur')
else:
m_param3 = 'traceevent'
if(m_time and m_pid and m_msg):
t = FTraceLine(m_time, m_msg, m_param3)
pid = int(m_pid)
@ -3322,14 +3458,29 @@ def parseTraceLog(live=False):
if t.type == 'tracing_mark_write':
m = re.match(tp.procexecfmt, t.name)
if(m):
parts, msg = 1, m.group('ps')
m = re.match(tp.procmultifmt, msg)
if(m):
parts, msg = int(m.group('n')), m.group('ps')
if tp.multiproccnt == 0:
tp.multiproctime = t.time
tp.multiproclist = dict()
proclist = tp.multiproclist
tp.multiproccnt += 1
else:
proclist = dict()
for ps in m.group('ps').split(','):
tp.multiproccnt = 0
for ps in msg.split(','):
val = ps.split()
if not val:
if not val or len(val) != 2:
continue
name = val[0].replace('--', '-')
proclist[name] = int(val[1])
if parts == 1:
data.pstl[t.time] = proclist
elif parts == tp.multiproccnt:
data.pstl[tp.multiproctime] = proclist
tp.multiproccnt = 0
continue
# find the end of resume
if(t.endMarker()):
@ -3545,7 +3696,6 @@ def parseTraceLog(live=False):
testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
if(res == -1):
testrun.ftemp[key][-1].addLine(t)
tf.close()
if len(testdata) < 1:
sysvals.vprint('WARNING: ftrace start marker is missing')
if data and not data.devicegroups:
@ -3667,6 +3817,12 @@ def parseTraceLog(live=False):
if p not in data.dmesg:
if not terr:
ph = p if 'machine' in p else lp
if p == 'suspend_machine':
sm = sysvals.suspendmode
if sm in suspendmodename:
sm = suspendmodename[sm]
terr = 'test%s did not enter %s power mode' % (tn, sm)
else:
terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
pprint('TEST%s FAILED: %s' % (tn, terr))
error.append(terr)
@ -3708,9 +3864,7 @@ def parseTraceLog(live=False):
# Function: loadKernelLog
# Description:
# [deprecated for kernel 3.15.0 or newer]
# load the dmesg file into memory and fix up any ordering issues
# The dmesg filename is taken from sysvals
# Output:
# An array of empty Data objects with only their dmesgtext attributes set
def loadKernelLog():
@ -3736,7 +3890,8 @@ def loadKernelLog():
if(not m):
continue
msg = m.group("msg")
if(re.match('PM: Syncing filesystems.*', msg)):
if re.match('PM: Syncing filesystems.*', msg) or \
re.match('PM: suspend entry.*', msg):
if(data):
testruns.append(data)
data = Data(len(testruns))
@ -3747,11 +3902,17 @@ def loadKernelLog():
if(m):
sysvals.stamp['kernel'] = m.group('k')
m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
if(m):
if not m:
m = re.match('PM: Preparing system for sleep \((?P<m>.*)\)', msg)
if m:
sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
data.dmesgtext.append(line)
lf.close()
if sysvals.suspendmode == 's2idle':
sysvals.suspendmode = 'freeze'
elif sysvals.suspendmode == 'deep':
sysvals.suspendmode = 'mem'
if data:
testruns.append(data)
if len(testruns) < 1:
@ -3762,12 +3923,9 @@ def loadKernelLog():
for data in testruns:
last = ''
for line in data.dmesgtext:
mc = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling '+\
'(?P<f>.*)\+ @ .*, parent: .*', line)
mr = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
'(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', last)
if(mc and mr and (mc.group('t') == mr.group('t')) and
(mc.group('f') == mr.group('f'))):
ct, cf, n, p = data.initcall_debug_call(line)
rt, rf, l = data.initcall_debug_return(last)
if ct and rt and ct == rt and cf == rf:
i = data.dmesgtext.index(last)
j = data.dmesgtext.index(line)
data.dmesgtext[i] = line
@ -3777,7 +3935,6 @@ def loadKernelLog():
# Function: parseKernelLog
# Description:
# [deprecated for kernel 3.15.0 or newer]
# Analyse a dmesg log output file generated from this app during
# the execution phase. Create a set of device structures in memory
# for subsequent formatting in the html output file
@ -3796,30 +3953,30 @@ def parseKernelLog(data):
# dmesg phase match table
dm = {
'suspend_prepare': ['PM: Syncing filesystems.*'],
'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*'],
'suspend_late': ['PM: suspend of devices complete after.*'],
'suspend_noirq': ['PM: late suspend of devices complete after.*'],
'suspend_machine': ['PM: noirq suspend of devices complete after.*'],
'resume_machine': ['ACPI: Low-level resume complete.*'],
'resume_noirq': ['ACPI: Waking up from system sleep state.*'],
'resume_early': ['PM: noirq resume of devices complete after.*'],
'resume': ['PM: early resume of devices complete after.*'],
'resume_complete': ['PM: resume of devices complete after.*'],
'suspend_prepare': ['PM: Syncing filesystems.*', 'PM: suspend entry.*'],
'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*',
'PM: Suspending system .*'],
'suspend_late': ['PM: suspend of devices complete after.*',
'PM: freeze of devices complete after.*'],
'suspend_noirq': ['PM: late suspend of devices complete after.*',
'PM: late freeze of devices complete after.*'],
'suspend_machine': ['PM: suspend-to-idle',
'PM: noirq suspend of devices complete after.*',
'PM: noirq freeze of devices complete after.*'],
'resume_machine': ['PM: Timekeeping suspended for.*',
'ACPI: Low-level resume complete.*',
'ACPI: resume from mwait',
'Suspended for [0-9\.]* seconds'],
'resume_noirq': ['PM: resume from suspend-to-idle',
'ACPI: Waking up from system sleep state.*'],
'resume_early': ['PM: noirq resume of devices complete after.*',
'PM: noirq restore of devices complete after.*'],
'resume': ['PM: early resume of devices complete after.*',
'PM: early restore of devices complete after.*'],
'resume_complete': ['PM: resume of devices complete after.*',
'PM: restore of devices complete after.*'],
'post_resume': ['.*Restarting tasks \.\.\..*'],
}
if(sysvals.suspendmode == 'standby'):
dm['resume_machine'] = ['PM: Restoring platform NVS memory']
elif(sysvals.suspendmode == 'disk'):
dm['suspend_late'] = ['PM: freeze of devices complete after.*']
dm['suspend_noirq'] = ['PM: late freeze of devices complete after.*']
dm['suspend_machine'] = ['PM: noirq freeze of devices complete after.*']
dm['resume_machine'] = ['PM: Restoring platform NVS memory']
dm['resume_early'] = ['PM: noirq restore of devices complete after.*']
dm['resume'] = ['PM: early restore of devices complete after.*']
dm['resume_complete'] = ['PM: restore of devices complete after.*']
elif(sysvals.suspendmode == 'freeze'):
dm['resume_machine'] = ['ACPI: resume from mwait']
# action table (expected events that occur and show up in dmesg)
at = {
@ -3867,12 +4024,13 @@ def parseKernelLog(data):
for s in dm[p]:
if(re.match(s, msg)):
phasechange, phase = True, p
dm[p] = [s]
break
# hack for determining resume_machine end for freeze
if(not sysvals.usetraceevents and sysvals.suspendmode == 'freeze' \
and phase == 'resume_machine' and \
re.match('calling (?P<f>.*)\+ @ .*, parent: .*', msg)):
data.initcall_debug_call(line, True)):
data.setPhase(phase, ktime, False)
phase = 'resume_noirq'
data.setPhase(phase, ktime, True)
@ -3945,25 +4103,17 @@ def parseKernelLog(data):
# -- device callbacks --
if(phase in data.sortedPhases()):
# device init call
if(re.match('calling (?P<f>.*)\+ @ .*, parent: .*', msg)):
sm = re.match('calling (?P<f>.*)\+ @ '+\
'(?P<n>.*), parent: (?P<p>.*)', msg);
f = sm.group('f')
n = sm.group('n')
p = sm.group('p')
if(f and n and p):
t, f, n, p = data.initcall_debug_call(line)
if t and f and n and p:
data.newAction(phase, f, int(n), p, ktime, -1, '')
else:
# device init return
elif(re.match('call (?P<f>.*)\+ returned .* after '+\
'(?P<t>.*) usecs', msg)):
sm = re.match('call (?P<f>.*)\+ returned .* after '+\
'(?P<t>.*) usecs(?P<a>.*)', msg);
f = sm.group('f')
t = sm.group('t')
t, f, l = data.initcall_debug_return(line)
if t and f and l:
list = data.dmesg[phase]['list']
if(f in list):
dev = list[f]
dev['length'] = int(t)
dev['length'] = int(l)
dev['end'] = ktime
# if trace events are not available, these are better than nothing
@ -4006,6 +4156,8 @@ def parseKernelLog(data):
# fill in any missing phases
phasedef = data.phasedef
terr, lp = '', 'suspend_prepare'
if lp not in data.dmesg:
doError('dmesg log format has changed, could not find start of suspend')
for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
if p not in data.dmesg:
if not terr:
@ -5302,7 +5454,7 @@ def executeSuspend(quiet=False):
sv.dlog('read dmesg')
sv.initdmesg()
# start ftrace
if(sv.usecallgraph or sv.usetraceevents):
if sv.useftrace:
if not quiet:
pprint('START TRACING')
sv.dlog('start ftrace tracing')
@ -5334,7 +5486,6 @@ def executeSuspend(quiet=False):
sv.dlog('enable RTC wake alarm')
sv.rtcWakeAlarmOn()
# start of suspend trace marker
if(sv.usecallgraph or sv.usetraceevents):
sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
# predelay delay
if(count == 1 and sv.predelay > 0):
@ -5384,11 +5535,17 @@ def executeSuspend(quiet=False):
sv.fsetVal('WAIT END', 'trace_marker')
# return from suspend
pprint('RESUME COMPLETE')
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.netfix:
netfixout = sv.netfixon('wired')
elif sv.netfix:
netfixout = sv.netfixon()
if sv.netfix and netfixout:
tdata['netfix'] = netfixout
sv.dlog('netfix, %s' % tdata['netfix'])
if(sv.suspendmode == 'mem' or sv.suspendmode == 'command'):
sv.dlog('read the ACPI FPDT')
tdata['fw'] = getFPDT(False)
@ -5396,7 +5553,7 @@ def executeSuspend(quiet=False):
sv.dlog('run the cmdinfo list after')
cmdafter = sv.cmdinfo(False)
# stop ftrace
if(sv.usecallgraph or sv.usetraceevents):
if sv.useftrace:
if sv.useprocmon:
sv.dlog('stop the process monitor')
pm.stop()
@ -5407,7 +5564,7 @@ def executeSuspend(quiet=False):
sysvals.dlog('EXECUTION TRACE END')
sv.getdmesg(testdata)
# grab a copy of the ftrace output
if(sv.usecallgraph or sv.usetraceevents):
if sv.useftrace:
if not quiet:
pprint('CAPTURING TRACE')
op = sv.writeDatafileHeader(sv.ftracefile, testdata)
@ -5838,12 +5995,18 @@ def statusCheck(probecheck=False):
pprint(' please choose one with -m')
# check if ftrace is available
if sysvals.useftrace:
res = sysvals.colorText('NO')
ftgood = sysvals.verifyFtrace()
if(ftgood):
sysvals.useftrace = sysvals.verifyFtrace()
efmt = '"{0}" uses ftrace, and it is not properly supported'
if sysvals.useftrace:
res = 'YES'
elif(sysvals.usecallgraph):
status = 'ftrace is not properly supported'
elif sysvals.usecallgraph:
status = efmt.format('-f')
elif sysvals.usedevsrc:
status = efmt.format('-dev')
elif sysvals.useprocmon:
status = efmt.format('-proc')
pprint(' is ftrace supported: %s' % res)
# check if kprobes are available
@ -5857,8 +6020,8 @@ def statusCheck(probecheck=False):
pprint(' are kprobes supported: %s' % res)
# what data source are we using
res = 'DMESG'
if(ftgood):
res = 'DMESG (very limited, ftrace is preferred)'
if sysvals.useftrace:
sysvals.usetraceevents = True
for e in sysvals.traceevents:
if not os.path.exists(sysvals.epath+e):
@ -5879,7 +6042,7 @@ def statusCheck(probecheck=False):
pprint(' optional commands this tool may use for info:')
no = sysvals.colorText('MISSING')
yes = sysvals.colorText('FOUND', 32)
for c in ['turbostat', 'mcelog', 'lspci', 'lsusb']:
for c in ['turbostat', 'mcelog', 'lspci', 'lsusb', 'netfix']:
if c == 'turbostat':
res = yes if sysvals.haveTurbostat() else no
else:
@ -5971,7 +6134,7 @@ def processData(live=False, quiet=False):
if not sysvals.stamp:
pprint('ERROR: data does not include the expected stamp')
return (testruns, {'error': 'timeline generation failed'})
shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
shown = ['os', 'bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
sysvals.vprint('System Info:')
for key in sorted(sysvals.stamp):
@ -6052,6 +6215,8 @@ def runTest(n=0, quiet=False):
if sysvals.display:
ret = sysvals.displayControl('init')
sysvals.dlog('xset display init, ret = %d' % ret)
sysvals.testVal(sysvals.pmdpath, 'basic', '1')
sysvals.testVal(sysvals.s0ixpath, 'basic', 'Y')
sysvals.dlog('initialize ftrace')
sysvals.initFtrace(quiet)
@ -6145,9 +6310,12 @@ def data_from_html(file, outpath, issues, fulldetail=False):
elist[err[0]] += 1
for i in elist:
ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
wifi = find_in_html(html, 'Wifi Resume: ', '</td>')
if wifi:
extra['wifi'] = wifi
line = find_in_html(log, '# wifi ', '\n')
if line:
extra['wifi'] = line
line = find_in_html(log, '# netfix ', '\n')
if line:
extra['netfix'] = line
low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
for lowstr in ['waking', '+']:
if not low:
@ -6243,7 +6411,7 @@ def genHtml(subdir, force=False):
sysvals.ftracefile = file
sysvals.setOutputFile()
if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
(force or not sysvals.usable(sysvals.htmlfile)):
(force or not sysvals.usable(sysvals.htmlfile, True)):
pprint('FTRACE: %s' % sysvals.ftracefile)
if sysvals.dmesgfile:
pprint('DMESG : %s' % sysvals.dmesgfile)
@ -6533,6 +6701,7 @@ def printHelp():
' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
' -result fn Export a results table to a text file for parsing.\n'\
' -wifi If a wifi connection is available, check that it reconnects after resume.\n'\
' -netfix Use netfix to reset the network in the event it fails to resume.\n'\
' [testprep]\n'\
' -sync Sync the filesystems before starting the test\n'\
' -rs on/off Enable/disable runtime suspend for all devices, restore all after test\n'\
@ -6615,6 +6784,8 @@ if __name__ == '__main__':
elif(arg == '-v'):
pprint("Version %s" % sysvals.version)
sys.exit(0)
elif(arg == '-debugtiming'):
debugtiming = True
elif(arg == '-x2'):
sysvals.execcount = 2
elif(arg == '-x2delay'):
@ -6657,6 +6828,8 @@ if __name__ == '__main__':
sysvals.sync = True
elif(arg == '-wifi'):
sysvals.wifi = True
elif(arg == '-netfix'):
sysvals.netfix = True
elif(arg == '-gzip'):
sysvals.gzip = True
elif(arg == '-info'):
@ -6819,7 +6992,7 @@ if __name__ == '__main__':
sysvals.outdir = val
sysvals.notestrun = True
if(os.path.isdir(val) == False):
doError('%s is not accessible' % val)
doError('%s is not accesible' % val)
elif(arg == '-filter'):
try:
val = next(args)
@ -6942,12 +7115,11 @@ if __name__ == '__main__':
time.sleep(sysvals.multitest['delay'])
fmt = 'suspend-%y%m%d-%H%M%S'
sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
ret = runTest(i+1, True)
ret = runTest(i+1, not sysvals.verbose)
failcnt = 0 if not ret else failcnt + 1
if sysvals.maxfail > 0 and failcnt >= sysvals.maxfail:
pprint('Maximum fail count of %d reached, aborting multitest' % (sysvals.maxfail))
break
time.sleep(5)
sysvals.resetlog()
sysvals.multistat(False, i, finish)
if 'time' in sysvals.multitest and datetime.now() >= finish: