Commit 1d59c3b6 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull more power management updates from Rafael Wysocki:
 "These update ARM cpufreq drivers, the OPP (Operating Performance
  Points) library and the power management documentation.

  Specifics:

   - Add per core DVFS support for QCom SoC (Bjorn Andersson), convert
     to yaml binding (Manivannan Sadhasivam) and various other fixes to
     the QCom drivers (Luca Weiss).

   - Add OPP table for imx7s SoC (Denys Drozdov) and minor fixes (Stefan
     Agner).

   - Fix CPPC driver's freq/performance conversions (Pierre Gondois).

   - Minor generic cleanups (Yury Norov).

   - Introduce opp-microwatt property to the OPP core, bindings, etc
     (Lukasz Luba).

   - Convert DT bindings to schema format and various related fixes
     (Yassine Oudjana).

   - Expose OPP's OF node in debugfs (Viresh Kumar).

   - Add Intel uncore frequency scaling documentation file to its
     MAINTAINERS entry (Srinivas Pandruvada).

   - Clean up the AMD P-state driver documentation (Jan Engelhardt)"

* tag 'pm-5.18-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (24 commits)
  Documentation: amd-pstate: grammar and sentence structure updates
  dt-bindings: cpufreq: cpufreq-qcom-hw: Convert to YAML bindings
  dt-bindings: dvfs: Use MediaTek CPUFREQ HW as an example
  Documentation: EM: Describe new registration method using DT
  OPP: Add support of "opp-microwatt" for EM registration
  PM: EM: add macro to set .active_power() callback conditionally
  OPP: Add "opp-microwatt" supporting code
  dt-bindings: opp: Add "opp-microwatt" entry in the OPP
  MAINTAINERS: Add additional file to uncore frequency control
  cpufreq: blocklist Qualcomm sc8280xp and sa8540p in cpufreq-dt-platdev
  cpufreq: qcom-hw: Add support for per-core-dcvs
  dt-bindings: power: avs: qcom,cpr: Convert to DT schema
  arm64: dts: qcom: qcs404: Rename CPU and CPR OPP tables
  arm64: dts: qcom: msm8996: Rename cluster OPP tables
  dt-bindings: opp: Convert qcom-nvmem-cpufreq to DT schema
  dt-bindings: opp: qcom-opp: Convert to DT schema
  arm64: dts: qcom: msm8996-mtp: Add msm8996 compatible
  dt-bindings: arm: qcom: Add msm8996 and apq8096 compatibles
  opp: Expose of-node's name in debugfs
  cpufreq: CPPC: Fix performance/frequency conversion
  ...
parents 5efabdad 3b65dd5b
This diff is collapsed.
Qualcomm Technologies, Inc. CPUFREQ Bindings
CPUFREQ HW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI)
SoCs to manage frequency in hardware. It is capable of controlling frequency
for multiple clusters.
Properties:
- compatible
Usage: required
Value type: <string>
Definition: must be "qcom,cpufreq-hw" or "qcom,cpufreq-epss".
- clocks
Usage: required
Value type: <phandle> From common clock binding.
Definition: clock handle for XO clock and GPLL0 clock.
- clock-names
Usage: required
Value type: <string> From common clock binding.
Definition: must be "xo", "alternate".
- reg
Usage: required
Value type: <prop-encoded-array>
Definition: Addresses and sizes for the memory of the HW bases in
each frequency domain.
- reg-names
Usage: Optional
Value type: <string>
Definition: Frequency domain name i.e.
"freq-domain0", "freq-domain1".
- #freq-domain-cells:
Usage: required.
Definition: Number of cells in a freqency domain specifier.
* Property qcom,freq-domain
Devices supporting freq-domain must set their "qcom,freq-domain" property with
phandle to a cpufreq_hw followed by the Domain ID(0/1) in the CPU DT node.
Example:
Example 1: Dual-cluster, Quad-core per cluster. CPUs within a cluster switch
DCVS state together.
/ {
cpus {
#address-cells = <2>;
#size-cells = <0>;
CPU0: cpu@0 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x0>;
enable-method = "psci";
next-level-cache = <&L2_0>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_0: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
L3_0: l3-cache {
compatible = "cache";
};
};
};
CPU1: cpu@100 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x100>;
enable-method = "psci";
next-level-cache = <&L2_100>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_100: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU2: cpu@200 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x200>;
enable-method = "psci";
next-level-cache = <&L2_200>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_200: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU3: cpu@300 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x300>;
enable-method = "psci";
next-level-cache = <&L2_300>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_300: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU4: cpu@400 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x400>;
enable-method = "psci";
next-level-cache = <&L2_400>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_400: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU5: cpu@500 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x500>;
enable-method = "psci";
next-level-cache = <&L2_500>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_500: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU6: cpu@600 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x600>;
enable-method = "psci";
next-level-cache = <&L2_600>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_600: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU7: cpu@700 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x700>;
enable-method = "psci";
next-level-cache = <&L2_700>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_700: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
};
soc {
cpufreq_hw: cpufreq@17d43000 {
compatible = "qcom,cpufreq-hw";
reg = <0x17d43000 0x1400>, <0x17d45800 0x1400>;
reg-names = "freq-domain0", "freq-domain1";
clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GPLL0>;
clock-names = "xo", "alternate";
#freq-domain-cells = <1>;
};
}
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/cpufreq/cpufreq-qcom-hw.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. CPUFREQ
maintainers:
- Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
description: |
CPUFREQ HW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI)
SoCs to manage frequency in hardware. It is capable of controlling frequency
for multiple clusters.
properties:
compatible:
oneOf:
- description: v1 of CPUFREQ HW
items:
- const: qcom,cpufreq-hw
- description: v2 of CPUFREQ HW (EPSS)
items:
- enum:
- qcom,sm8250-cpufreq-epss
- const: qcom,cpufreq-epss
reg:
minItems: 2
items:
- description: Frequency domain 0 register region
- description: Frequency domain 1 register region
- description: Frequency domain 2 register region
reg-names:
minItems: 2
items:
- const: freq-domain0
- const: freq-domain1
- const: freq-domain2
clocks:
items:
- description: XO Clock
- description: GPLL0 Clock
clock-names:
items:
- const: xo
- const: alternate
'#freq-domain-cells':
const: 1
required:
- compatible
- reg
- clocks
- clock-names
- '#freq-domain-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
#include <dt-bindings/clock/qcom,rpmh.h>
// Example 1: Dual-cluster, Quad-core per cluster. CPUs within a cluster
// switch DCVS state together.
cpus {
#address-cells = <2>;
#size-cells = <0>;
CPU0: cpu@0 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x0>;
enable-method = "psci";
next-level-cache = <&L2_0>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_0: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
L3_0: l3-cache {
compatible = "cache";
};
};
};
CPU1: cpu@100 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x100>;
enable-method = "psci";
next-level-cache = <&L2_100>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_100: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU2: cpu@200 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x200>;
enable-method = "psci";
next-level-cache = <&L2_200>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_200: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU3: cpu@300 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x300>;
enable-method = "psci";
next-level-cache = <&L2_300>;
qcom,freq-domain = <&cpufreq_hw 0>;
L2_300: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU4: cpu@400 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x400>;
enable-method = "psci";
next-level-cache = <&L2_400>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_400: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU5: cpu@500 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x500>;
enable-method = "psci";
next-level-cache = <&L2_500>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_500: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU6: cpu@600 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x600>;
enable-method = "psci";
next-level-cache = <&L2_600>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_600: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
CPU7: cpu@700 {
device_type = "cpu";
compatible = "qcom,kryo385";
reg = <0x0 0x700>;
enable-method = "psci";
next-level-cache = <&L2_700>;
qcom,freq-domain = <&cpufreq_hw 1>;
L2_700: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
};
};
};
soc {
#address-cells = <1>;
#size-cells = <1>;
cpufreq@17d43000 {
compatible = "qcom,cpufreq-hw";
reg = <0x17d43000 0x1400>, <0x17d45800 0x1400>;
reg-names = "freq-domain0", "freq-domain1";
clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GPLL0>;
clock-names = "xo", "alternate";
#freq-domain-cells = <1>;
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/cpufreq/qcom-cpufreq-nvmem.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. NVMEM CPUFreq bindings
maintainers:
- Ilia Lin <ilia.lin@kernel.org>
description: |
In certain Qualcomm Technologies, Inc. SoCs such as QCS404, The CPU supply
voltage is dynamically configured by Core Power Reduction (CPR) depending on
current CPU frequency and efuse values.
CPR provides a power domain with multiple levels that are selected depending
on the CPU OPP in use. The CPUFreq driver sets the CPR power domain level
according to the required OPPs defined in the CPU OPP tables.
select:
properties:
compatible:
contains:
enum:
- qcom,qcs404
required:
- compatible
properties:
cpus:
type: object
patternProperties:
'cpu@[0-9a-f]+':
type: object
properties:
power-domains:
maxItems: 1
power-domain-names:
items:
- const: cpr
required:
- power-domains
- power-domain-names
patternProperties:
'^opp-table(-[a-z0-9]+)?$':
if:
properties:
compatible:
const: operating-points-v2-kryo-cpu
then:
patternProperties:
'^opp-?[0-9]+$':
required:
- required-opps
additionalProperties: true
examples:
- |
/ {
model = "Qualcomm Technologies, Inc. QCS404";
compatible = "qcom,qcs404";
#address-cells = <2>;
#size-cells = <2>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
CPU0: cpu@100 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x100>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
next-level-cache = <&L2_0>;
#cooling-cells = <2>;
clocks = <&apcs_glb>;
operating-points-v2 = <&cpu_opp_table>;
power-domains = <&cpr>;
power-domain-names = "cpr";
};
CPU1: cpu@101 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x101>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
next-level-cache = <&L2_0>;
#cooling-cells = <2>;
clocks = <&apcs_glb>;
operating-points-v2 = <&cpu_opp_table>;
power-domains = <&cpr>;
power-domain-names = "cpr";
};
CPU2: cpu@102 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x102>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
next-level-cache = <&L2_0>;
#cooling-cells = <2>;
clocks = <&apcs_glb>;
operating-points-v2 = <&cpu_opp_table>;
power-domains = <&cpr>;
power-domain-names = "cpr";
};
CPU3: cpu@103 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x103>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
next-level-cache = <&L2_0>;
#cooling-cells = <2>;
clocks = <&apcs_glb>;
operating-points-v2 = <&cpu_opp_table>;
power-domains = <&cpr>;
power-domain-names = "cpr";
};
};
cpu_opp_table: opp-table-cpu {
compatible = "operating-points-v2-kryo-cpu";
opp-shared;
opp-1094400000 {
opp-hz = /bits/ 64 <1094400000>;
required-opps = <&cpr_opp1>;
};
opp-1248000000 {
opp-hz = /bits/ 64 <1248000000>;
required-opps = <&cpr_opp2>;
};
opp-1401600000 {
opp-hz = /bits/ 64 <1401600000>;
required-opps = <&cpr_opp3>;
};
};
cpr_opp_table: opp-table-cpr {
compatible = "operating-points-v2-qcom-level";
cpr_opp1: opp1 {
opp-level = <1>;
qcom,opp-fuse-level = <1>;
};
cpr_opp2: opp2 {
opp-level = <2>;
qcom,opp-fuse-level = <2>;
};
cpr_opp3: opp3 {
opp-level = <3>;
qcom,opp-fuse-level = <3>;
};
};
};
......@@ -51,10 +51,16 @@ additionalProperties: true
examples:
- |
performance: performance-controller@12340000 {
compatible = "qcom,cpufreq-hw";
reg = <0x12340000 0x1000>;
#performance-domain-cells = <1>;
soc {
#address-cells = <2>;
#size-cells = <2>;
performance: performance-controller@11bc00 {
compatible = "mediatek,cpufreq-hw";
reg = <0 0x0011bc10 0 0x120>, <0 0x0011bd30 0 0x120>;
#performance-domain-cells = <1>;
};
};
// The node above defines a performance controller that is a performance
......
......@@ -93,6 +93,21 @@ patternProperties:
minItems: 1
maxItems: 8 # Should be enough regulators
opp-microwatt:
description: |
The power for the OPP in micro-Watts.
Entries for multiple regulators shall be provided in the same field
separated by angular brackets <>. If current values aren't required
for a regulator, then it shall be filled with 0. If power values
aren't required for any of the regulators, then this field is not
required. The OPP binding doesn't provide any provisions to relate the
values to their power supplies or the order in which the supplies need
to be configured and that is left for the implementation specific
binding.
minItems: 1
maxItems: 8 # Should be enough regulators
opp-level:
description:
A value representing the performance level of the device.
......@@ -205,6 +220,14 @@ patternProperties:
minItems: 1
maxItems: 8 # Should be enough regulators
'^opp-microwatt':
description:
Named opp-microwatt property. Similar to opp-microamp property,
but for microwatt instead.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 8 # Should be enough regulators
dependencies:
opp-avg-kBps: [ opp-peak-kBps ]
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/opp/opp-v2-kryo-cpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. NVMEM OPP bindings
maintainers:
- Ilia Lin <ilia.lin@kernel.org>
allOf:
- $ref: opp-v2-base.yaml#
description: |
In certain Qualcomm Technologies, Inc. SoCs like APQ8096 and MSM8996,
the CPU frequencies subset and voltage value of each OPP varies based on
the silicon variant in use.
Qualcomm Technologies, Inc. Process Voltage Scaling Tables
defines the voltage and frequency value based on the msm-id in SMEM
and speedbin blown in the efuse combination.
The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC
to provide the OPP framework with required information (existing HW bitmap).
This is used to determine the voltage and frequency value for each OPP of
operating-points-v2 table when it is parsed by the OPP framework.
properties:
compatible:
const: operating-points-v2-kryo-cpu
nvmem-cells:
description: |
A phandle pointing to a nvmem-cells node representing the
efuse registers that has information about the
speedbin that is used to select the right frequency/voltage
value pair.
opp-shared: true
patternProperties:
'^opp-?[0-9]+$':
type: object
properties:
opp-hz: true
opp-microvolt: true
opp-supported-hw:
description: |
A single 32 bit bitmap value, representing compatible HW.
Bitmap:
0: MSM8996 V3, speedbin 0
1: MSM8996 V3, speedbin 1
2: MSM8996 V3, speedbin 2
3: unused
4: MSM8996 SG, speedbin 0
5: MSM8996 SG, speedbin 1
6: MSM8996 SG, speedbin 2
7-31: unused
maximum: 0x77
clock-latency-ns: true
required-opps: true
required:
- opp-hz
required:
- compatible
if:
required:
- nvmem-cells
then:
patternProperties:
'^opp-?[0-9]+$':
required:
- opp-supported-hw
additionalProperties: false
examples:
- |
/ {
model = "Qualcomm Technologies, Inc. DB820c";
compatible = "arrow,apq8096-db820c", "qcom,apq8096-sbc", "qcom,apq8096";
#address-cells = <2>;
#size-cells = <2>;
cpus {
#address-cells = <2>;
#size-cells = <0>;
CPU0: cpu@0 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x0>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
capacity-dmips-mhz = <1024>;
clocks = <&kryocc 0>;
operating-points-v2 = <&cluster0_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_0>;
L2_0: l2-cache {
compatible = "cache";
cache-level = <2>;
};
};
CPU1: cpu@1 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x1>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
capacity-dmips-mhz = <1024>;
clocks = <&kryocc 0>;
operating-points-v2 = <&cluster0_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_0>;
};
CPU2: cpu@100 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x100>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
capacity-dmips-mhz = <1024>;
clocks = <&kryocc 1>;
operating-points-v2 = <&cluster1_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_1>;
L2_1: l2-cache {
compatible = "cache";
cache-level = <2>;
};
};
CPU3: cpu@101 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x101>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
capacity-dmips-mhz = <1024>;
clocks = <&kryocc 1>;
operating-points-v2 = <&cluster1_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_1>;
};
cpu-map {
cluster0 {
core0 {
cpu = <&CPU0>;
};
core1 {
cpu = <&CPU1>;
};
};
cluster1 {
core0 {
cpu = <&CPU2>;
};
core1 {
cpu = <&CPU3>;
};
};
};
};
cluster0_opp: opp-table-0 {
compatible = "operating-points-v2-kryo-cpu";
nvmem-cells = <&speedbin_efuse>;
opp-shared;
opp-307200000 {
opp-hz = /bits/ 64 <307200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-1593600000 {
opp-hz = /bits/ 64 <1593600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x71>;
clock-latency-ns = <200000>;
};
opp-2188800000 {
opp-hz = /bits/ 64 <2188800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
};
cluster1_opp: opp-table-1 {
compatible = "operating-points-v2-kryo-cpu";
nvmem-cells = <&speedbin_efuse>;
opp-shared;
opp-307200000 {
opp-hz = /bits/ 64 <307200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-1593600000 {
opp-hz = /bits/ 64 <1593600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-2150400000 {
opp-hz = /bits/ 64 <2150400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x31>;
clock-latency-ns = <200000>;
};
opp-2342400000 {
opp-hz = /bits/ 64 <2342400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
};
smem {
compatible = "qcom,smem";
memory-region = <&smem_mem>;
hwlocks = <&tcsr_mutex 3>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
qfprom: qfprom@74000 {
compatible = "qcom,msm8996-qfprom", "qcom,qfprom";
reg = <0x00074000 0x8ff>;
#address-cells = <1>;
#size-cells = <1>;
speedbin_efuse: speedbin@133 {
reg = <0x133 0x1>;
bits = <5 3>;
};
};
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/opp/opp-v2-qcom-level.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm OPP bindings to describe OPP nodes.
maintainers:
- Niklas Cassel <nks@flawful.org>
allOf:
- $ref: opp-v2-base.yaml#
properties:
compatible:
const: operating-points-v2-qcom-level
patternProperties:
'^opp-?[0-9]+$':
type: object
properties:
opp-level: true
qcom,opp-fuse-level:
description: |
A positive value representing the fuse corner/level associated with
this OPP node. Sometimes several corners/levels shares a certain fuse
corner/level. A fuse corner/level contains e.g. ref uV, min uV,
and max uV.
$ref: /schemas/types.yaml#/definitions/uint32
required:
- opp-level
- qcom,opp-fuse-level
required:
- compatible
additionalProperties: false
examples:
- |
cpr_opp_table: opp-table-cpr {
compatible = "operating-points-v2-qcom-level";
cpr_opp1: opp1 {
opp-level = <1>;
qcom,opp-fuse-level = <1>;
};
cpr_opp2: opp2 {
opp-level = <2>;
qcom,opp-fuse-level = <2>;
};
cpr_opp3: opp3 {
opp-level = <3>;
qcom,opp-fuse-level = <3>;
};
};
Qualcomm OPP bindings to describe OPP nodes
The bindings are based on top of the operating-points-v2 bindings
described in Documentation/devicetree/bindings/opp/opp-v2-base.yaml
Additional properties are described below.
* OPP Table Node
Required properties:
- compatible: Allow OPPs to express their compatibility. It should be:
"operating-points-v2-qcom-level"
* OPP Node
Required properties:
- qcom,opp-fuse-level: A positive value representing the fuse corner/level
associated with this OPP node. Sometimes several corners/levels shares
a certain fuse corner/level. A fuse corner/level contains e.g. ref uV,
min uV, and max uV.
QCOM CPR (Core Power Reduction)
CPR (Core Power Reduction) is a technology to reduce core power on a CPU
or other device. Each OPP of a device corresponds to a "corner" that has
a range of valid voltages for a particular frequency. While the device is
running at a particular frequency, CPR monitors dynamic factors such as
temperature, etc. and suggests adjustments to the voltage to save power
and meet silicon characteristic requirements.
- compatible:
Usage: required
Value type: <string>
Definition: should be "qcom,qcs404-cpr", "qcom,cpr" for qcs404
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: base address and size of the rbcpr register region
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify the CPR interrupt
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: phandle to the reference clock
- clock-names:
Usage: required
Value type: <stringlist>
Definition: must be "ref"
- vdd-apc-supply:
Usage: required
Value type: <phandle>
Definition: phandle to the vdd-apc-supply regulator
- #power-domain-cells:
Usage: required
Value type: <u32>
Definition: should be 0
- operating-points-v2:
Usage: required
Value type: <phandle>
Definition: A phandle to the OPP table containing the
performance states supported by the CPR
power domain
- acc-syscon:
Usage: optional
Value type: <phandle>
Definition: phandle to syscon for writing ACC settings
- nvmem-cells:
Usage: required
Value type: <phandle>
Definition: phandle to nvmem cells containing the data
that makes up a fuse corner, for each fuse corner.
As well as the CPR fuse revision.
- nvmem-cell-names:
Usage: required
Value type: <stringlist>
Definition: should be "cpr_quotient_offset1", "cpr_quotient_offset2",
"cpr_quotient_offset3", "cpr_init_voltage1",
"cpr_init_voltage2", "cpr_init_voltage3", "cpr_quotient1",
"cpr_quotient2", "cpr_quotient3", "cpr_ring_osc1",
"cpr_ring_osc2", "cpr_ring_osc3", "cpr_fuse_revision"
for qcs404.
Example:
cpr_opp_table: cpr-opp-table {
compatible = "operating-points-v2-qcom-level";
cpr_opp1: opp1 {
opp-level = <1>;
qcom,opp-fuse-level = <1>;
};
cpr_opp2: opp2 {
opp-level = <2>;
qcom,opp-fuse-level = <2>;
};
cpr_opp3: opp3 {
opp-level = <3>;
qcom,opp-fuse-level = <3>;
};
};
power-controller@b018000 {
compatible = "qcom,qcs404-cpr", "qcom,cpr";
reg = <0x0b018000 0x1000>;
interrupts = <0 15 IRQ_TYPE_EDGE_RISING>;
clocks = <&xo_board>;
clock-names = "ref";
vdd-apc-supply = <&pms405_s3>;
#power-domain-cells = <0>;
operating-points-v2 = <&cpr_opp_table>;
acc-syscon = <&tcsr>;
nvmem-cells = <&cpr_efuse_quot_offset1>,
<&cpr_efuse_quot_offset2>,
<&cpr_efuse_quot_offset3>,
<&cpr_efuse_init_voltage1>,
<&cpr_efuse_init_voltage2>,
<&cpr_efuse_init_voltage3>,
<&cpr_efuse_quot1>,
<&cpr_efuse_quot2>,
<&cpr_efuse_quot3>,
<&cpr_efuse_ring1>,
<&cpr_efuse_ring2>,
<&cpr_efuse_ring3>,
<&cpr_efuse_revision>;
nvmem-cell-names = "cpr_quotient_offset1",
"cpr_quotient_offset2",
"cpr_quotient_offset3",
"cpr_init_voltage1",
"cpr_init_voltage2",
"cpr_init_voltage3",
"cpr_quotient1",
"cpr_quotient2",
"cpr_quotient3",
"cpr_ring_osc1",
"cpr_ring_osc2",
"cpr_ring_osc3",
"cpr_fuse_revision";
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/avs/qcom,cpr.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Core Power Reduction (CPR) bindings
maintainers:
- Niklas Cassel <nks@flawful.org>
description: |
CPR (Core Power Reduction) is a technology to reduce core power on a CPU
or other device. Each OPP of a device corresponds to a "corner" that has
a range of valid voltages for a particular frequency. While the device is
running at a particular frequency, CPR monitors dynamic factors such as
temperature, etc. and suggests adjustments to the voltage to save power
and meet silicon characteristic requirements.
properties:
compatible:
items:
- enum:
- qcom,qcs404-cpr
- const: qcom,cpr
reg:
description: Base address and size of the RBCPR register region.
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: Reference clock.
clock-names:
items:
- const: ref
vdd-apc-supply:
description: APC regulator supply.
'#power-domain-cells':
const: 0
operating-points-v2:
description: |
A phandle to the OPP table containing the performance states
supported by the CPR power domain.
acc-syscon:
description: A phandle to the syscon used for writing ACC settings.
nvmem-cells:
items:
- description: Corner 1 quotient offset
- description: Corner 2 quotient offset
- description: Corner 3 quotient offset
- description: Corner 1 initial voltage
- description: Corner 2 initial voltage
- description: Corner 3 initial voltage
- description: Corner 1 quotient
- description: Corner 2 quotient
- description: Corner 3 quotient
- description: Corner 1 ring oscillator
- description: Corner 2 ring oscillator
- description: Corner 3 ring oscillator
- description: Fuse revision
nvmem-cell-names:
items:
- const: cpr_quotient_offset1
- const: cpr_quotient_offset2
- const: cpr_quotient_offset3
- const: cpr_init_voltage1
- const: cpr_init_voltage2
- const: cpr_init_voltage3
- const: cpr_quotient1
- const: cpr_quotient2
- const: cpr_quotient3
- const: cpr_ring_osc1
- const: cpr_ring_osc2
- const: cpr_ring_osc3
- const: cpr_fuse_revision
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- vdd-apc-supply
- '#power-domain-cells'
- operating-points-v2
- nvmem-cells
- nvmem-cell-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
cpr_opp_table: opp-table-cpr {
compatible = "operating-points-v2-qcom-level";
cpr_opp1: opp1 {
opp-level = <1>;
qcom,opp-fuse-level = <1>;
};
cpr_opp2: opp2 {
opp-level = <2>;
qcom,opp-fuse-level = <2>;
};
cpr_opp3: opp3 {
opp-level = <3>;
qcom,opp-fuse-level = <3>;
};
};
power-controller@b018000 {
compatible = "qcom,qcs404-cpr", "qcom,cpr";
reg = <0x0b018000 0x1000>;
interrupts = <0 15 IRQ_TYPE_EDGE_RISING>;
clocks = <&xo_board>;
clock-names = "ref";
vdd-apc-supply = <&pms405_s3>;
#power-domain-cells = <0>;
operating-points-v2 = <&cpr_opp_table>;
acc-syscon = <&tcsr>;
nvmem-cells = <&cpr_efuse_quot_offset1>,
<&cpr_efuse_quot_offset2>,
<&cpr_efuse_quot_offset3>,
<&cpr_efuse_init_voltage1>,
<&cpr_efuse_init_voltage2>,
<&cpr_efuse_init_voltage3>,
<&cpr_efuse_quot1>,
<&cpr_efuse_quot2>,
<&cpr_efuse_quot3>,
<&cpr_efuse_ring1>,
<&cpr_efuse_ring2>,
<&cpr_efuse_ring3>,
<&cpr_efuse_revision>;
nvmem-cell-names = "cpr_quotient_offset1",
"cpr_quotient_offset2",
"cpr_quotient_offset3",
"cpr_init_voltage1",
"cpr_init_voltage2",
"cpr_init_voltage3",
"cpr_quotient1",
"cpr_quotient2",
"cpr_quotient3",
"cpr_ring_osc1",
"cpr_ring_osc2",
"cpr_ring_osc3",
"cpr_fuse_revision";
};
......@@ -113,6 +113,16 @@ to: return warning/error, stop working or panic.
See Section 3. for an example of driver implementing this
callback, or Section 2.4 for further documentation on this API
Registration of EM using DT
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The EM can also be registered using OPP framework and information in DT
"operating-points-v2". Each OPP entry in DT can be extended with a property
"opp-microwatt" containing micro-Watts power value. This OPP DT property
allows a platform to register EM power values which are reflecting total power
(static + dynamic). These power values might be coming directly from
experiments and measurements.
Registration of 'simple' EM
~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
......@@ -10079,6 +10079,7 @@ INTEL UNCORE FREQUENCY CONTROL
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst
F: drivers/platform/x86/intel/uncore-frequency/
INTEL VENDOR SPECIFIC EXTENDED CAPABILITIES DRIVER
......@@ -16249,14 +16250,15 @@ M: Niklas Cassel <nks@flawful.org>
L: linux-pm@vger.kernel.org
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/power/avs/qcom,cpr.txt
F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml
F: drivers/soc/qcom/cpr.c
QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
M: Ilia Lin <ilia.lin@kernel.org>
L: linux-pm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
F: Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-nvmem.yaml
F: Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml
F: drivers/cpufreq/qcom-cpufreq-nvmem.c
QUALCOMM CRYPTO DRIVERS
......
......@@ -76,6 +76,22 @@ cpu0: cpu@0 {
clock-latency = <61036>; /* two CLK32 periods */
clocks = <&clks IMX7D_CLK_ARM>;
cpu-idle-states = <&cpu_sleep_wait>;
operating-points-v2 = <&cpu0_opp_table>;
#cooling-cells = <2>;
nvmem-cells = <&fuse_grade>;
nvmem-cell-names = "speed_grade";
};
};
cpu0_opp_table: opp-table {
compatible = "operating-points-v2";
opp-shared;
opp-792000000 {
opp-hz = /bits/ 64 <792000000>;
opp-microvolt = <1000000>;
clock-latency-ns = <150000>;
opp-supported-hw = <0xf>, <0xf>;
};
};
......
......@@ -303,52 +303,48 @@ static u64 cppc_get_dmi_max_khz(void)
/*
* If CPPC lowest_freq and nominal_freq registers are exposed then we can
* use them to convert perf to freq and vice versa
*
* If the perf/freq point lies between Nominal and Lowest, we can treat
* (Low perf, Low freq) and (Nom Perf, Nom freq) as 2D co-ordinates of a line
* and extrapolate the rest
* For perf/freq > Nominal, we use the ratio perf:freq at Nominal for conversion
* use them to convert perf to freq and vice versa. The conversion is
* extrapolated as an affine function passing by the 2 points:
* - (Low perf, Low freq)
* - (Nominal perf, Nominal perf)
*/
static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu_data,
unsigned int perf)
{
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
s64 retval, offset = 0;
static u64 max_khz;
u64 mul, div;
if (caps->lowest_freq && caps->nominal_freq) {
if (perf >= caps->nominal_perf) {
mul = caps->nominal_freq;
div = caps->nominal_perf;
} else {
mul = caps->nominal_freq - caps->lowest_freq;
div = caps->nominal_perf - caps->lowest_perf;
}
mul = caps->nominal_freq - caps->lowest_freq;
div = caps->nominal_perf - caps->lowest_perf;
offset = caps->nominal_freq - div64_u64(caps->nominal_perf * mul, div);
} else {
if (!max_khz)
max_khz = cppc_get_dmi_max_khz();
mul = max_khz;
div = caps->highest_perf;
}
return (u64)perf * mul / div;
retval = offset + div64_u64(perf * mul, div);
if (retval >= 0)
return retval;
return 0;
}
static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu_data,
unsigned int freq)
{
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
s64 retval, offset = 0;
static u64 max_khz;
u64 mul, div;
if (caps->lowest_freq && caps->nominal_freq) {
if (freq >= caps->nominal_freq) {
mul = caps->nominal_perf;
div = caps->nominal_freq;
} else {
mul = caps->lowest_perf;
div = caps->lowest_freq;
}
mul = caps->nominal_perf - caps->lowest_perf;
div = caps->nominal_freq - caps->lowest_freq;
offset = caps->nominal_perf - div64_u64(caps->nominal_freq * mul, div);
} else {
if (!max_khz)
max_khz = cppc_get_dmi_max_khz();
......@@ -356,7 +352,10 @@ static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu_data,
div = max_khz;
}
return (u64)freq * mul / div;
retval = offset + div64_u64(freq * mul, div);
if (retval >= 0)
return retval;
return 0;
}
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
......
......@@ -110,6 +110,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "fsl,imx7ulp", },
{ .compatible = "fsl,imx7d", },
{ .compatible = "fsl,imx7s", },
{ .compatible = "fsl,imx8mq", },
{ .compatible = "fsl,imx8mm", },
{ .compatible = "fsl,imx8mn", },
......@@ -138,9 +139,11 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "qcom,msm8996", },
{ .compatible = "qcom,qcs404", },
{ .compatible = "qcom,sa8155p" },
{ .compatible = "qcom,sa8540p" },
{ .compatible = "qcom,sc7180", },
{ .compatible = "qcom,sc7280", },
{ .compatible = "qcom,sc8180x", },
{ .compatible = "qcom,sc8280xp", },
{ .compatible = "qcom,sdm845", },
{ .compatible = "qcom,sm6350", },
{ .compatible = "qcom,sm8150", },
......
......@@ -28,6 +28,7 @@
struct qcom_cpufreq_soc_data {
u32 reg_enable;
u32 reg_dcvs_ctrl;
u32 reg_freq_lut;
u32 reg_volt_lut;
u32 reg_current_vote;
......@@ -50,6 +51,8 @@ struct qcom_cpufreq_data {
bool cancel_throttle;
struct delayed_work throttle_work;
struct cpufreq_policy *policy;
bool per_core_dcvs;
};
static unsigned long cpu_hw_rate, xo_rate;
......@@ -102,9 +105,14 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
struct qcom_cpufreq_data *data = policy->driver_data;
const struct qcom_cpufreq_soc_data *soc_data = data->soc_data;
unsigned long freq = policy->freq_table[index].frequency;
unsigned int i;
writel_relaxed(index, data->base + soc_data->reg_perf_state);
if (data->per_core_dcvs)
for (i = 1; i < cpumask_weight(policy->related_cpus); i++)
writel_relaxed(index, data->base + soc_data->reg_perf_state + i * 4);
if (icc_scaling_enabled)
qcom_cpufreq_set_bw(policy, freq);
......@@ -137,10 +145,15 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
struct qcom_cpufreq_data *data = policy->driver_data;
const struct qcom_cpufreq_soc_data *soc_data = data->soc_data;
unsigned int index;
unsigned int i;
index = policy->cached_resolved_idx;
writel_relaxed(index, data->base + soc_data->reg_perf_state);
if (data->per_core_dcvs)
for (i = 1; i < cpumask_weight(policy->related_cpus); i++)
writel_relaxed(index, data->base + soc_data->reg_perf_state + i * 4);
return policy->freq_table[index].frequency;
}
......@@ -342,6 +355,7 @@ static irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data)
static const struct qcom_cpufreq_soc_data qcom_soc_data = {
.reg_enable = 0x0,
.reg_dcvs_ctrl = 0xbc,
.reg_freq_lut = 0x110,
.reg_volt_lut = 0x114,
.reg_current_vote = 0x704,
......@@ -351,6 +365,7 @@ static const struct qcom_cpufreq_soc_data qcom_soc_data = {
static const struct qcom_cpufreq_soc_data epss_soc_data = {
.reg_enable = 0x0,
.reg_dcvs_ctrl = 0xb0,
.reg_freq_lut = 0x100,
.reg_volt_lut = 0x200,
.reg_perf_state = 0x320,
......@@ -481,8 +496,11 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
goto error;
}
if (readl_relaxed(base + data->soc_data->reg_dcvs_ctrl) & 0x1)
data->per_core_dcvs = true;
qcom_get_related_cpus(index, policy->cpus);
if (!cpumask_weight(policy->cpus)) {
if (cpumask_empty(policy->cpus)) {
dev_err(dev, "Domain-%d failed to get related CPUs\n", index);
ret = -ENOENT;
goto error;
......
......@@ -130,7 +130,7 @@ static void get_krait_bin_format_b(struct device *cpu_dev,
}
/* Check PVS_BLOW_STATUS */
pte_efuse = *(((u32 *)buf) + 4);
pte_efuse = *(((u32 *)buf) + 1);
pte_efuse &= BIT(21);
if (pte_efuse) {
dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs);
......
......@@ -154,7 +154,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
* table and opp-shared.
*/
ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->opp_shared_cpus);
if (ret || !cpumask_weight(priv->opp_shared_cpus)) {
if (ret || cpumask_empty(priv->opp_shared_cpus)) {
/*
* Either opp-table is not set or no opp-shared was found.
* Use the CPU mask from SCMI to designate CPUs sharing an OPP
......
......@@ -113,6 +113,31 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage);
/**
* dev_pm_opp_get_power() - Gets the power corresponding to an opp
* @opp: opp for which power has to be returned for
*
* Return: power in micro watt corresponding to the opp, else
* return 0
*
* This is useful only for devices with single power supply.
*/
unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp)
{
unsigned long opp_power = 0;
int i;
if (IS_ERR_OR_NULL(opp)) {
pr_err("%s: Invalid parameters\n", __func__);
return 0;
}
for (i = 0; i < opp->opp_table->regulator_count; i++)
opp_power += opp->supplies[i].u_watt;
return opp_power;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_power);
/**
* dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp
* @opp: opp for which frequency has to be returned for
......
......@@ -10,6 +10,7 @@
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/init.h>
#include <linux/limits.h>
#include <linux/slab.h>
......@@ -99,6 +100,9 @@ static void opp_debug_create_supplies(struct dev_pm_opp *opp,
debugfs_create_ulong("u_amp", S_IRUGO, d,
&opp->supplies[i].u_amp);
debugfs_create_ulong("u_watt", S_IRUGO, d,
&opp->supplies[i].u_watt);
}
}
......@@ -131,9 +135,13 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend);
debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate);
debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate);
debugfs_create_u32("level", S_IRUGO, d, &opp->level);
debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
&opp->clock_latency_ns);
opp->of_name = of_node_full_name(opp->np);
debugfs_create_str("of_name", S_IRUGO, d, (char **)&opp->of_name);
opp_debug_create_supplies(opp, opp_table, d);
opp_debug_create_bw(opp, opp_table, d);
......
......@@ -575,8 +575,9 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
struct opp_table *opp_table)
{
u32 *microvolt, *microamp = NULL;
int supplies = opp_table->regulator_count, vcount, icount, ret, i, j;
u32 *microvolt, *microamp = NULL, *microwatt = NULL;
int supplies = opp_table->regulator_count;
int vcount, icount, pcount, ret, i, j;
struct property *prop = NULL;
char name[NAME_MAX];
......@@ -688,6 +689,43 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
}
}
/* Search for "opp-microwatt" */
sprintf(name, "opp-microwatt");
prop = of_find_property(opp->np, name, NULL);
if (prop) {
pcount = of_property_count_u32_elems(opp->np, name);
if (pcount < 0) {
dev_err(dev, "%s: Invalid %s property (%d)\n", __func__,
name, pcount);
ret = pcount;
goto free_microamp;
}
if (pcount != supplies) {
dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)\n",
__func__, name, pcount, supplies);
ret = -EINVAL;
goto free_microamp;
}
microwatt = kmalloc_array(pcount, sizeof(*microwatt),
GFP_KERNEL);
if (!microwatt) {
ret = -EINVAL;
goto free_microamp;
}
ret = of_property_read_u32_array(opp->np, name, microwatt,
pcount);
if (ret) {
dev_err(dev, "%s: error parsing %s: %d\n", __func__,
name, ret);
ret = -EINVAL;
goto free_microwatt;
}
}
for (i = 0, j = 0; i < supplies; i++) {
opp->supplies[i].u_volt = microvolt[j++];
......@@ -701,8 +739,13 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
if (microamp)
opp->supplies[i].u_amp = microamp[i];
if (microwatt)
opp->supplies[i].u_watt = microwatt[i];
}
free_microwatt:
kfree(microwatt);
free_microamp:
kfree(microamp);
free_microvolt:
......@@ -1395,6 +1438,38 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node);
/*
* Callback function provided to the Energy Model framework upon registration.
* It provides the power used by @dev at @kHz if it is the frequency of an
* existing OPP, or at the frequency of the first OPP above @kHz otherwise
* (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
* frequency and @mW to the associated power.
*
* Returns 0 on success or a proper -EINVAL value in case of error.
*/
static int __maybe_unused
_get_dt_power(unsigned long *mW, unsigned long *kHz, struct device *dev)
{
struct dev_pm_opp *opp;
unsigned long opp_freq, opp_power;
/* Find the right frequency and related OPP */
opp_freq = *kHz * 1000;
opp = dev_pm_opp_find_freq_ceil(dev, &opp_freq);
if (IS_ERR(opp))
return -EINVAL;
opp_power = dev_pm_opp_get_power(opp);
dev_pm_opp_put(opp);
if (!opp_power)
return -EINVAL;
*kHz = opp_freq / 1000;
*mW = opp_power / 1000;
return 0;
}
/*
* Callback function provided to the Energy Model framework upon registration.
* This computes the power estimated by @dev at @kHz if it is the frequency
......@@ -1445,6 +1520,24 @@ static int __maybe_unused _get_power(unsigned long *mW, unsigned long *kHz,
return 0;
}
static bool _of_has_opp_microwatt_property(struct device *dev)
{
unsigned long power, freq = 0;
struct dev_pm_opp *opp;
/* Check if at least one OPP has needed property */
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp))
return false;
power = dev_pm_opp_get_power(opp);
dev_pm_opp_put(opp);
if (!power)
return false;
return true;
}
/**
* dev_pm_opp_of_register_em() - Attempt to register an Energy Model
* @dev : Device for which an Energy Model has to be registered
......@@ -1458,7 +1551,7 @@ static int __maybe_unused _get_power(unsigned long *mW, unsigned long *kHz,
*/
int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
{
struct em_data_callback em_cb = EM_DATA_CB(_get_power);
struct em_data_callback em_cb;
struct device_node *np;
int ret, nr_opp;
u32 cap;
......@@ -1474,6 +1567,12 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto failed;
}
/* First, try to find more precised Energy Model in DT */
if (_of_has_opp_microwatt_property(dev)) {
EM_SET_ACTIVE_POWER_CB(em_cb, _get_dt_power);
goto register_em;
}
np = of_node_get(dev->of_node);
if (!np) {
ret = -EINVAL;
......@@ -1495,6 +1594,9 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto failed;
}
EM_SET_ACTIVE_POWER_CB(em_cb, _get_power);
register_em:
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
if (ret)
goto failed;
......
......@@ -96,6 +96,7 @@ struct dev_pm_opp {
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
const char *of_name;
#endif
};
......
......@@ -116,6 +116,7 @@ struct em_data_callback {
struct device *dev);
};
#define EM_DATA_CB(_active_power_cb) { .active_power = &_active_power_cb }
#define EM_SET_ACTIVE_POWER_CB(em_cb, cb) ((em_cb).active_power = cb)
struct em_perf_domain *em_cpu_get(int cpu);
struct em_perf_domain *em_pd_get(struct device *dev);
......@@ -264,6 +265,7 @@ static inline int em_pd_nr_perf_states(struct em_perf_domain *pd)
#else
struct em_data_callback {};
#define EM_DATA_CB(_active_power_cb) { }
#define EM_SET_ACTIVE_POWER_CB(em_cb, cb) do { } while (0)
static inline
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
......
......@@ -32,14 +32,17 @@ enum dev_pm_opp_event {
* @u_volt_min: Minimum voltage in microvolts corresponding to this OPP
* @u_volt_max: Maximum voltage in microvolts corresponding to this OPP
* @u_amp: Maximum current drawn by the device in microamperes
* @u_watt: Power used by the device in microwatts
*
* This structure stores the voltage/current values for a single power supply.
* This structure stores the voltage/current/power values for a single power
* supply.
*/
struct dev_pm_opp_supply {
unsigned long u_volt;
unsigned long u_volt_min;
unsigned long u_volt_max;
unsigned long u_amp;
unsigned long u_watt;
};
/**
......@@ -94,6 +97,8 @@ void dev_pm_opp_put_opp_table(struct opp_table *opp_table);
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp);
unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp);
......@@ -186,6 +191,11 @@ static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
return 0;
}
static inline unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp)
{
return 0;
}
static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp)
{
return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment