Merge branches 'pm-devfreq', 'pm-qos', 'pm-tools' and 'pm-docs'

Merge devfreq changes, PM QoS change, and power management tools and
documentation changes for v5.20-rc1:

 - 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).

* pm-devfreq:
  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-qos:
  PM: QoS: Add check to make sure CPU freq is non-negative

* pm-tools:
  pm-graph v5.9

* pm-docs:
  Documentation: PM: Drop pme_interrupt reference
This commit is contained in:
Rafael J. Wysocki 2022-07-29 19:46:00 +02:00
16 changed files with 1255 additions and 680 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

@ -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

@ -4373,7 +4373,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
@ -5855,6 +5855,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

@ -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

@ -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

@ -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,22 +69,24 @@ 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('.')))
if val >= (4, 10, 0):
return True
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
def kernelParams(self):
cmdline = 'initcall_debug log_buf_len=32M'

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,8 +66,13 @@ from threading import Thread
from subprocess import call, Popen, PIPE
import base64
debugtiming = False
mystarttime = time.time()
def pprint(msg):
print(msg)
if debugtiming:
print('[%09.3f] %s' % (time.time()-mystarttime, msg))
else:
print(msg)
sys.stdout.flush()
def ascii(text):
@ -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,20 +456,25 @@ class SystemValues:
print(fmt % ('memfree', ('%d kB' % self.memfree)))
def cpuInfo(self):
self.cpucount = 0
fp = open('/proc/cpuinfo', 'r')
for line in fp:
if re.match('^processor[ \t]*:[ \t]*[0-9]*', line):
self.cpucount += 1
fp.close()
fp = open('/proc/meminfo', 'r')
for line in fp:
m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line)
if m:
self.memtotal = int(m.group('sz'))
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('/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
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:
self.memtotal = int(m.group('sz'))
m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line)
if m:
self.memfree = int(m.group('sz'))
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:
props[dev].isasync = False
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:
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:
idp = ascii(fp.read()).strip()
props[dev].altname = '%s:%s' % (idv, idp)
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())
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):
proclist = dict()
for ps in m.group('ps').split(','):
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()
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])
data.pstl[t.time] = proclist
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,7 +3817,13 @@ def parseTraceLog(live=False):
if p not in data.dmesg:
if not terr:
ph = p if 'machine' in p else lp
terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
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)
if data.tSuspended == 0:
@ -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,26 +4103,18 @@ 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):
data.newAction(phase, f, int(n), p, ktime, -1, '')
# 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')
list = data.dmesg[phase]['list']
if(f in list):
dev = list[f]
dev['length'] = int(t)
dev['end'] = ktime
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
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(l)
dev['end'] = ktime
# if trace events are not available, these are better than nothing
if(not sysvals.usetraceevents):
@ -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,8 +5486,7 @@ 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')
sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
# predelay delay
if(count == 1 and sv.predelay > 0):
sv.fsetVal('WAIT %d' % sv.predelay, 'trace_marker')
@ -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')
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,13 +5995,19 @@ def statusCheck(probecheck=False):
pprint(' please choose one with -m')
# check if ftrace is available
res = sysvals.colorText('NO')
ftgood = sysvals.verifyFtrace()
if(ftgood):
res = 'YES'
elif(sysvals.usecallgraph):
status = 'ftrace is not properly supported'
pprint(' is ftrace supported: %s' % res)
if sysvals.useftrace:
res = sysvals.colorText('NO')
sysvals.useftrace = sysvals.verifyFtrace()
efmt = '"{0}" uses ftrace, and it is not properly supported'
if sysvals.useftrace:
res = 'YES'
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
if sysvals.usekprobes:
@ -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: