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