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 abstraction layer which standardizes the format of power cost tables in the
kernel, hence enabling to avoid redundant work. 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 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 check that the requirements for the power value scale types are met. An example
can be found in the Energy-Aware Scheduler documentation can be found in the Energy-Aware Scheduler documentation
Documentation/scheduler/sched-energy.rst. For some subsystems like thermal or Documentation/scheduler/sched-energy.rst. For some subsystems like thermal or
powercap power values expressed in an 'abstract scale' might cause issues. powercap power values expressed in an 'abstract scale' might cause issues.
These subsystems are more interested in estimation of power used in the past, 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 be found in the Intelligent Power Allocation in
Documentation/driver-api/thermal/power_allocator.rst. Documentation/driver-api/thermal/power_allocator.rst.
Kernel subsystems might implement automatic detection to check whether EM Kernel subsystems might implement automatic detection to check whether EM
registered devices have inconsistent scale (based on EM internal flag). 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 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 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 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:: calling the following API::
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, 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 Drivers must provide a callback function returning <frequency, power> tuples
for each performance state. The callback function provided by the driver is free 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 deemed necessary. Only for CPU devices, drivers must specify the CPUs of the
performance domains using cpumask. For other devices than CPUs the last performance domains using cpumask. For other devices than CPUs the last
argument must be set to NULL. 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 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 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 See Section 3. for an example of driver implementing this
callback, or Section 2.4 for further documentation on this API 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 efficiency of the CPUs. This would allow to provide EAS information which
has different relation than what would be forced by the EM internal has different relation than what would be forced by the EM internal
formulas calculating 'cost' values. To register an EM for such platform, the 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 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 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 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 */ configuration space */
unsigned int pme_support:5; /* Bitmask of states from which PME# unsigned int pme_support:5; /* Bitmask of states from which PME#
can be generated */ 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 d1_support:1; /* Low power state D1 is supported */
unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */
unsigned int no_d1d2:1; /* D1 and D2 are forbidden */ 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 L: linux-samsung-soc@vger.kernel.org
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
F: Documentation/devicetree/bindings/devfreq/exynos-bus.txt F: Documentation/devicetree/bindings/interconnect/samsung,exynos-bus.yaml
F: drivers/devfreq/exynos-bus.c F: drivers/devfreq/exynos-bus.c
BUSLOGIC SCSI DRIVER BUSLOGIC SCSI DRIVER
@ -5890,6 +5890,7 @@ L: linux-pm@vger.kernel.org
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
F: Documentation/devicetree/bindings/devfreq/ F: Documentation/devicetree/bindings/devfreq/
F: Documentation/devicetree/bindings/interconnect/mediatek,cci.yaml
F: drivers/devfreq/ F: drivers/devfreq/
F: include/linux/devfreq.h F: include/linux/devfreq.h
F: include/trace/events/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; struct dentry *d;
if (!genpd_debugfs_dir)
return;
d = debugfs_lookup(genpd->name, genpd_debugfs_dir); d = debugfs_lookup(genpd->name, genpd_debugfs_dir);
debugfs_remove(d); debugfs_remove(d);
} }

View File

@ -1862,10 +1862,13 @@ int pm_runtime_force_suspend(struct device *dev)
callback = RPM_GET_CALLBACK(dev, runtime_suspend); callback = RPM_GET_CALLBACK(dev, runtime_suspend);
dev_pm_enable_wake_irq_check(dev, true);
ret = callback ? callback(dev) : 0; ret = callback ? callback(dev) : 0;
if (ret) if (ret)
goto err; goto err;
dev_pm_enable_wake_irq_complete(dev);
/* /*
* If the device can stay in suspend after the system-wide transition * If the device can stay in suspend after the system-wide transition
* to the working state that will follow, drop the children counter of * 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; return 0;
err: err:
dev_pm_disable_wake_irq_check(dev, true);
pm_runtime_enable(dev); pm_runtime_enable(dev);
return ret; return ret;
} }
@ -1915,9 +1919,11 @@ int pm_runtime_force_resume(struct device *dev)
callback = RPM_GET_CALLBACK(dev, runtime_resume); callback = RPM_GET_CALLBACK(dev, runtime_resume);
dev_pm_disable_wake_irq_check(dev, false);
ret = callback ? callback(dev) : 0; ret = callback ? callback(dev) : 0;
if (ret) { if (ret) {
pm_runtime_set_suspended(dev); pm_runtime_set_suspended(dev);
dev_pm_enable_wake_irq_check(dev, false);
goto out; 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); 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. * device_set_wakeup_enable - Enable or disable a device to wake up the system.
* @dev: Device to handle. * @dev: Device to handle.

View File

@ -268,7 +268,7 @@ config LOONGSON2_CPUFREQ
This option adds a CPUFreq driver for loongson processors which This option adds a CPUFreq driver for loongson processors which
support software configurable cpu frequency. 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. If in doubt, say N.

View File

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

View File

@ -843,12 +843,14 @@ ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf)
unsigned int cpu; unsigned int cpu;
for_each_cpu(cpu, mask) { for_each_cpu(cpu, mask) {
if (i) i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u ", cpu);
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), " ");
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u", cpu);
if (i >= (PAGE_SIZE - 5)) if (i >= (PAGE_SIZE - 5))
break; break;
} }
/* Remove the extra space at the end */
i--;
i += sprintf(&buf[i], "\n"); i += sprintf(&buf[i], "\n");
return i; return i;
} }
@ -971,21 +973,10 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
if (!fattr->store) if (!fattr->store)
return -EIO; return -EIO;
/* down_write(&policy->rwsem);
* cpus_read_trylock() is used here to work around a circular lock if (likely(!policy_is_inactive(policy)))
* dependency problem with respect to the cpufreq_register_driver(). ret = fattr->store(policy, buf, count);
*/ up_write(&policy->rwsem);
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; return ret;
} }
@ -1282,6 +1273,13 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
unsigned long flags; unsigned long flags;
int cpu; 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 */ /* Remove policy from list */
write_lock_irqsave(&cpufreq_driver_lock, flags); write_lock_irqsave(&cpufreq_driver_lock, flags);
list_del(&policy->policy_list); list_del(&policy->policy_list);
@ -1536,8 +1534,6 @@ out_destroy_policy:
for_each_cpu(j, policy->real_cpus) for_each_cpu(j, policy->real_cpus)
remove_cpu_dev_symlink(policy, j, get_cpu_device(j)); remove_cpu_dev_symlink(policy, j, get_cpu_device(j));
cpumask_clear(policy->cpus);
out_offline_policy: out_offline_policy:
if (cpufreq_driver->offline) if (cpufreq_driver->offline)
cpufreq_driver->offline(policy); cpufreq_driver->offline(policy);
@ -1547,6 +1543,7 @@ out_exit_policy:
cpufreq_driver->exit(policy); cpufreq_driver->exit(policy);
out_free_policy: out_free_policy:
cpumask_clear(policy->cpus);
up_write(&policy->rwsem); up_write(&policy->rwsem);
cpufreq_policy_free(policy); 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) static void od_set_powersave_bias(unsigned int powersave_bias)
{ {
unsigned int cpu; unsigned int cpu;
cpumask_t done; cpumask_var_t done;
if (!alloc_cpumask_var(&done, GFP_KERNEL))
return;
default_powersave_bias = powersave_bias; default_powersave_bias = powersave_bias;
cpumask_clear(&done); cpumask_clear(done);
cpus_read_lock(); cpus_read_lock();
for_each_online_cpu(cpu) { 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 dbs_data *dbs_data;
struct od_dbs_tuners *od_tuners; struct od_dbs_tuners *od_tuners;
if (cpumask_test_cpu(cpu, &done)) if (cpumask_test_cpu(cpu, done))
continue; continue;
policy = cpufreq_cpu_get_raw(cpu); policy = cpufreq_cpu_get_raw(cpu);
@ -439,13 +442,15 @@ static void od_set_powersave_bias(unsigned int powersave_bias)
if (!policy_dbs) if (!policy_dbs)
continue; continue;
cpumask_or(&done, &done, policy->cpus); cpumask_or(done, done, policy->cpus);
dbs_data = policy_dbs->dbs_data; dbs_data = policy_dbs->dbs_data;
od_tuners = dbs_data->tuners; od_tuners = dbs_data->tuners;
od_tuners->powersave_bias = default_powersave_bias; od_tuners->powersave_bias = default_powersave_bias;
} }
cpus_read_unlock(); cpus_read_unlock();
free_cpumask_var(done);
} }
void od_register_powersave_bias_handler(unsigned int (*f) 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 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) unsigned long *KHz)
{ {
struct mtk_cpufreq_data *data; struct mtk_cpufreq_data *data;
@ -71,8 +71,9 @@ mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *mW,
i--; i--;
*KHz = data->table[i].frequency; *KHz = data->table[i].frequency;
*mW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] + /* Provide micro-Watts value to the Energy Model */
i * LUT_ROW_SIZE) / 1000; *uW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] +
i * LUT_ROW_SIZE);
return 0; return 0;
} }

View File

@ -19,6 +19,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/scmi_protocol.h> #include <linux/scmi_protocol.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/units.h>
struct scmi_data { struct scmi_data {
int domain_id; int domain_id;
@ -99,6 +100,7 @@ static int __maybe_unused
scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power, scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
unsigned long *KHz) unsigned long *KHz)
{ {
enum scmi_power_scale power_scale = perf_ops->power_scale_get(ph);
unsigned long Hz; unsigned long Hz;
int ret, domain; int ret, domain;
@ -112,6 +114,10 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
if (ret) if (ret)
return 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. */ /* The EM framework specifies the frequency in KHz. */
*KHz = Hz / 1000; *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) static void scmi_cpufreq_register_em(struct cpufreq_policy *policy)
{ {
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power); 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; 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 * 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) if (!priv->nr_opp)
return; 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_dev_register_perf_domain(get_cpu_device(policy->cpu), priv->nr_opp,
&em_cb, priv->opp_shared_cpus, &em_cb, priv->opp_shared_cpus,
power_scale_mw); em_power_scale);
} }
static struct cpufreq_driver scmi_cpufreq_driver = { static struct cpufreq_driver scmi_cpufreq_driver = {

View File

@ -19,6 +19,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kvm_para.h> #include <linux/kvm_para.h>
#include <trace/events/power.h>
static unsigned int guest_halt_poll_ns __read_mostly = 200000; static unsigned int guest_halt_poll_ns __read_mostly = 200000;
module_param(guest_halt_poll_ns, uint, 0644); 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) if (val > guest_halt_poll_ns)
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; dev->poll_limit_ns = val;
} else if (block_ns > guest_halt_poll_ns && } else if (block_ns > guest_halt_poll_ns &&
guest_halt_poll_allow_shrink) { guest_halt_poll_allow_shrink) {
@ -100,6 +102,7 @@ static void adjust_poll_limit(struct cpuidle_device *dev, u64 block_ns)
val = 0; val = 0;
else else
val /= shrink; val /= shrink;
trace_guest_halt_poll_ns_shrink(val, dev->poll_limit_ns);
dev->poll_limit_ns = val; 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 It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support. operating frequencies and voltages with OPP support.
config ARM_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 config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver" tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \ depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \

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_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.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. * qos_min_notifier_call() - Callback for QoS min_freq changes.
* @nb: Should be devfreq->nb_min * @nb: Should be devfreq->nb_min
* @val: not used
* @ptr: not used
*/ */
static int qos_min_notifier_call(struct notifier_block *nb, static int qos_min_notifier_call(struct notifier_block *nb,
unsigned long val, void *ptr) 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. * qos_max_notifier_call() - Callback for QoS max_freq changes.
* @nb: Should be devfreq->nb_max * @nb: Should be devfreq->nb_max
* @val: not used
* @ptr: not used
*/ */
static int qos_max_notifier_call(struct notifier_block *nb, static int qos_max_notifier_call(struct notifier_block *nb,
unsigned long val, void *ptr) 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); struct imx_bus *priv = dev_get_drvdata(dev);
const char *icc_driver_name; 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; return 0;
if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) { if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) {
dev_warn(dev, "imx interconnect drivers disabled\n"); 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, devfreq = devm_devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL); "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 PTR_ERR(devfreq);
}
return 0; return 0;
} }

View File

@ -140,8 +140,7 @@ struct perf_dom_info {
struct scmi_perf_info { struct scmi_perf_info {
u32 version; u32 version;
int num_domains; int num_domains;
bool power_scale_mw; enum scmi_power_scale power_scale;
bool power_scale_uw;
u64 stats_addr; u64 stats_addr;
u32 stats_size; u32 stats_size;
struct perf_dom_info *dom_info; 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); u16 flags = le16_to_cpu(attr->flags);
pi->num_domains = le16_to_cpu(attr->num_domains); 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) 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) | pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
(u64)le32_to_cpu(attr->stats_addr_high) << 32; (u64)le32_to_cpu(attr->stats_addr_high) << 32;
pi->stats_size = le32_to_cpu(attr->stats_size); 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; 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); 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 = { 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, .freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get, .est_power_get = scmi_dvfs_est_power_get,
.fast_switch_possible = scmi_fast_switch_possible, .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, 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 } .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 = { static struct cpuidle_state spr_cstates[] __initdata = {
{ {
.name = "C1", .name = "C1",
@ -950,8 +940,7 @@ static struct cpuidle_state spr_cstates[] __initdata = {
{ {
.name = "C1E", .name = "C1E",
.desc = "MWAIT 0x01", .desc = "MWAIT 0x01",
.flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_ALWAYS_ENABLE | .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_ALWAYS_ENABLE,
CPUIDLE_FLAG_UNUSABLE,
.exit_latency = 2, .exit_latency = 2,
.target_residency = 4, .target_residency = 4,
.enter = &intel_idle, .enter = &intel_idle,
@ -1774,17 +1763,6 @@ static void __init spr_idle_state_table_update(void)
{ {
unsigned long long msr; 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 * By default, the C6 state assumes the worst-case scenario of package
* C6. However, if PC6 is disabled, we update the numbers to match * 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 * 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 * 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 * (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. * Returns 0 on success or a proper -EINVAL value in case of error.
*/ */
static int __maybe_unused 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; struct dev_pm_opp *opp;
unsigned long opp_freq, opp_power; unsigned long opp_freq, opp_power;
@ -1465,7 +1465,7 @@ _get_dt_power(struct device *dev, unsigned long *mW, unsigned long *kHz)
return -EINVAL; return -EINVAL;
*kHz = opp_freq / 1000; *kHz = opp_freq / 1000;
*mW = opp_power / 1000; *uW = opp_power;
return 0; 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 * 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 * 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 * (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 * P = C * V^2 * f with C being the device's capacitance and V and f
* respectively the voltage and frequency of the OPP. * respectively the voltage and frequency of the OPP.
* *
* Returns -EINVAL if the power calculation failed because of missing * Returns -EINVAL if the power calculation failed because of missing
* parameters, 0 otherwise. * 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) unsigned long *kHz)
{ {
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
@ -1512,9 +1512,10 @@ static int __maybe_unused _get_power(struct device *dev, unsigned long *mW,
return -EINVAL; return -EINVAL;
tmp = (u64)cap * mV * mV * (Hz / 1000000); 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; *kHz = Hz / 1000;
return 0; 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++) { 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) if (power > power_limit)
break; 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); freq_qos_update_request(&dtpm_cpu->qos_req, freq);
power_limit = pd->table[i - 1].power * power_limit = pd->table[i - 1].power * nr_cpus;
MICROWATT_PER_MILLIWATT * nr_cpus;
return power_limit; 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_L, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, &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, &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(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server),
X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core), 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_TIGERLAKE_L, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE, 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_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, 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/pm_qos.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include <linux/units.h>
#include <trace/events/thermal.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, static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev,
u32 freq) u32 freq)
{ {
unsigned long power_mw;
int i; int i;
for (i = cpufreq_cdev->max_level - 1; i >= 0; 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; 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, static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev,
u32 power) u32 power)
{ {
unsigned long em_power_mw;
int i; int i;
for (i = cpufreq_cdev->max_level; i > 0; 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; 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); res = dfc->power_ops->get_real_power(df, power, freq, voltage);
if (!res) { if (!res) {
state = dfc->capped_state; state = dfc->capped_state;
/* Convert EM power into milli-Watts first */
dfc->res_util = dfc->em_pd->table[state].power; dfc->res_util = dfc->em_pd->table[state].power;
dfc->res_util /= MICROWATT_PER_MILLIWATT;
dfc->res_util *= SCALE_ERROR_MITIGATION; dfc->res_util *= SCALE_ERROR_MITIGATION;
if (*power > 1) if (*power > 1)
@ -218,8 +222,10 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd
_normalize_load(&status); _normalize_load(&status);
/* Scale power for utilization */ /* Convert EM power into milli-Watts first */
*power = dfc->em_pd->table[perf_idx].power; *power = dfc->em_pd->table[perf_idx].power;
*power /= MICROWATT_PER_MILLIWATT;
/* Scale power for utilization */
*power *= status.busy_time; *power *= status.busy_time;
*power >>= 10; *power >>= 10;
} }
@ -244,6 +250,7 @@ static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
perf_idx = dfc->max_state - state; perf_idx = dfc->max_state - state;
*power = dfc->em_pd->table[perf_idx].power; *power = dfc->em_pd->table[perf_idx].power;
*power /= MICROWATT_PER_MILLIWATT;
return 0; 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_cooling_device *dfc = cdev->devdata;
struct devfreq *df = dfc->devfreq; struct devfreq *df = dfc->devfreq;
struct devfreq_dev_status status; struct devfreq_dev_status status;
unsigned long freq; unsigned long freq, em_power_mw;
s32 est_power; s32 est_power;
int i; 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 * Find the first cooling state that is within the power
* budget. The EM power table is sorted ascending. * budget. The EM power table is sorted ascending.
*/ */
for (i = dfc->max_state; i > 0; i--) for (i = dfc->max_state; i > 0; i--) {
if (est_power >= dfc->em_pd->table[i].power) /* 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; break;
}
*state = dfc->max_state - i; *state = dfc->max_state - i;
dfc->capped_state = *state; dfc->capped_state = *state;

View File

@ -62,7 +62,7 @@ struct em_perf_domain {
/* /*
* em_perf_domain flags: * 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. * other scale.
* *
* EM_PERF_DOMAIN_SKIP_INEFFICIENCIES: Skip inefficient states when estimating * 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 * EM_PERF_DOMAIN_ARTIFICIAL: The power values are artificial and might be
* created by platform missing real power information * 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_SKIP_INEFFICIENCIES BIT(1)
#define EM_PERF_DOMAIN_ARTIFICIAL BIT(2) #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) #define em_is_artificial(em) ((em)->flags & EM_PERF_DOMAIN_ARTIFICIAL)
#ifdef CONFIG_ENERGY_MODEL #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 * To avoid possible energy estimation overflow on 32bit machines add
* architectures. The extra resolution improves decision made by EAS for the * limits to number of CPUs in the Perf. Domain.
* task placement when two Performance Domains might provide similar energy * We are safe on 64bit machine, thus some big number.
* 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.
*/ */
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
#define em_scale_power(p) ((p) * 1000) #define EM_MAX_NUM_CPUS 4096
#else #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 #endif
struct em_data_callback { struct em_data_callback {
@ -112,7 +134,7 @@ struct em_data_callback {
* and frequency. * and frequency.
* *
* In case of CPUs, the power is the one of a single CPU in the domain, * 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. * fit in the [0, EM_MAX_POWER] range.
* *
* Return 0 on success. * 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); struct em_perf_domain *em_pd_get(struct device *dev);
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *span, struct em_data_callback *cb, cpumask_t *span,
bool milliwatts); bool microwatts);
void em_dev_unregister_perf_domain(struct device *dev); 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) * pd_nrg = ------------------------ (4)
* scale_cpu * 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 static inline
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *span, struct em_data_callback *cb, cpumask_t *span,
bool milliwatts) bool microwatts)
{ {
return -EINVAL; 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_enable(struct device *dev);
extern int device_wakeup_disable(struct device *dev); extern int device_wakeup_disable(struct device *dev);
extern void device_set_wakeup_capable(struct device *dev, bool capable); 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 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 wakeup_source *ws);
extern void pm_stay_awake(struct device *dev); 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; 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) static inline bool device_may_wakeup(struct device *dev)
{ {
return dev->power.can_wakeup && dev->power.should_wakeup; 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); 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 */ #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_handle;
struct scmi_device; struct scmi_device;
struct scmi_protocol_handle; struct scmi_protocol_handle;
@ -135,7 +141,7 @@ struct scmi_perf_proto_ops {
unsigned long *rate, unsigned long *power); unsigned long *rate, unsigned long *power);
bool (*fast_switch_possible)(const struct scmi_protocol_handle *ph, bool (*fast_switch_possible)(const struct scmi_protocol_handle *ph,
struct device *dev); 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) 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 */ #endif /* _TRACE_POWER_H */
/* This part must be outside protection */ /* 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 * 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) { if (!power || power > EM_MAX_POWER) {
dev_err(dev, "EM: invalid power: %lu\n", 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; goto free_ps_table;
} }
} else { } else {
power_res = em_scale_power(table[i].power); power_res = table[i].power;
cost = div64_u64(fmax * power_res, table[i].frequency); 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 em_perf_domain *pd;
struct device *cpu_dev; struct device *cpu_dev;
int cpu, ret; int cpu, ret, num_cpus;
if (_is_cpu_device(dev)) { 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); pd = kzalloc(sizeof(*pd) + cpumask_size(), GFP_KERNEL);
if (!pd) if (!pd)
return -ENOMEM; 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 * @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 * obligatory. It can be taken from i.e. 'policy->cpus'. For other
* type of devices this should be set to NULL. * 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. * in some other scale. It must be set properly.
* *
* Create Energy Model tables for a performance domain using the callbacks * Create Energy Model tables for a performance domain using the callbacks
* defined in cb. * 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 * sub-systems might rely on this flag and check if all devices in the EM are
* using the same scale. * 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, int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *cpus, struct em_data_callback *cb, cpumask_t *cpus,
bool milliwatts) bool microwatts)
{ {
unsigned long cap, prev_cap = 0; unsigned long cap, prev_cap = 0;
unsigned long flags = 0; unsigned long flags = 0;
@ -381,8 +389,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
} }
} }
if (milliwatts) if (microwatts)
flags |= EM_PERF_DOMAIN_MILLIWATTS; flags |= EM_PERF_DOMAIN_MICROWATTS;
else if (cb->get_cost) else if (cb->get_cost)
flags |= EM_PERF_DOMAIN_ARTIFICIAL; flags |= EM_PERF_DOMAIN_ARTIFICIAL;

View File

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

View File

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

View File

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

View File

@ -69,22 +69,24 @@ class SystemValues(aslib.SystemValues):
bootloader = 'grub' bootloader = 'grub'
blexec = [] blexec = []
def __init__(self): 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') self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
if os.path.exists('/proc/version'): if os.path.exists('/proc/version'):
fp = open('/proc/version', 'r') fp = open('/proc/version', 'r')
val = fp.read().strip() self.kernel = self.kernelVersion(fp.read().strip())
fp.close() fp.close()
self.kernel = self.kernelVersion(val)
else:
self.kernel = 'unknown'
self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S') self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S')
def kernelVersion(self, msg): 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): 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 val >= (4, 10, 0): if m:
return True val = tuple(map(int, m.groups()))
if val >= (4, 10, 0):
return True
return False return False
def kernelParams(self): def kernelParams(self):
cmdline = 'initcall_debug log_buf_len=32M' cmdline = 'initcall_debug log_buf_len=32M'

View File

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

View File

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