Commit 79bc8bfa authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'pm-opp'

Merge OPP (Operating Performance Points) changes for 5.18-rc1.

* pm-opp:
  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
  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
parents 2353828f 0474bcc9
...@@ -172,7 +172,21 @@ properties: ...@@ -172,7 +172,21 @@ properties:
- const: qcom,apq8094 - const: qcom,apq8094
- items: - items:
- const: qcom,msm8996-mtp - enum:
- arrow,apq8096-db820c
- inforce,ifc6640
- const: qcom,apq8096-sbc
- const: qcom,apq8096
- items:
- enum:
- qcom,msm8996-mtp
- sony,dora-row
- sony,kagura-row
- sony,keyaki-row
- xiaomi,gemini
- xiaomi,scorpio
- const: qcom,msm8996
- items: - items:
- enum: - enum:
......
# 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>;
};
};
};
...@@ -93,6 +93,21 @@ patternProperties: ...@@ -93,6 +93,21 @@ patternProperties:
minItems: 1 minItems: 1
maxItems: 8 # Should be enough regulators 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: opp-level:
description: description:
A value representing the performance level of the device. A value representing the performance level of the device.
...@@ -203,6 +218,14 @@ patternProperties: ...@@ -203,6 +218,14 @@ patternProperties:
minItems: 1 minItems: 1
maxItems: 8 # Should be enough regulators 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: dependencies:
opp-avg-kBps: [ opp-peak-kBps ] 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. ...@@ -113,6 +113,16 @@ 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
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 Registration of 'simple' EM
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -15971,14 +15971,15 @@ M: Niklas Cassel <nks@flawful.org> ...@@ -15971,14 +15971,15 @@ M: Niklas Cassel <nks@flawful.org>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
L: linux-arm-msm@vger.kernel.org L: linux-arm-msm@vger.kernel.org
S: Maintained 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 F: drivers/soc/qcom/cpr.c
QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
M: Ilia Lin <ilia.lin@kernel.org> M: Ilia Lin <ilia.lin@kernel.org>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
S: Maintained 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 F: drivers/cpufreq/qcom-cpufreq-nvmem.c
QUALCOMM CRYPTO DRIVERS QUALCOMM CRYPTO DRIVERS
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
/ { / {
model = "Qualcomm Technologies, Inc. MSM 8996 MTP"; model = "Qualcomm Technologies, Inc. MSM 8996 MTP";
compatible = "qcom,msm8996-mtp"; compatible = "qcom,msm8996-mtp", "qcom,msm8996";
aliases { aliases {
serial0 = &blsp2_uart2; serial0 = &blsp2_uart2;
......
...@@ -134,7 +134,7 @@ CPU_SLEEP_0: cpu-sleep-0 { ...@@ -134,7 +134,7 @@ CPU_SLEEP_0: cpu-sleep-0 {
}; };
}; };
cluster0_opp: opp_table0 { cluster0_opp: opp-table-cluster0 {
compatible = "operating-points-v2-kryo-cpu"; compatible = "operating-points-v2-kryo-cpu";
nvmem-cells = <&speedbin_efuse>; nvmem-cells = <&speedbin_efuse>;
opp-shared; opp-shared;
...@@ -222,7 +222,7 @@ opp-1593600000 { ...@@ -222,7 +222,7 @@ opp-1593600000 {
}; };
}; };
cluster1_opp: opp_table1 { cluster1_opp: opp-table-cluster1 {
compatible = "operating-points-v2-kryo-cpu"; compatible = "operating-points-v2-kryo-cpu";
nvmem-cells = <&speedbin_efuse>; nvmem-cells = <&speedbin_efuse>;
opp-shared; opp-shared;
......
...@@ -110,7 +110,7 @@ CPU_SLEEP_0: cpu-sleep-0 { ...@@ -110,7 +110,7 @@ CPU_SLEEP_0: cpu-sleep-0 {
}; };
}; };
cpu_opp_table: cpu-opp-table { cpu_opp_table: opp-table-cpu {
compatible = "operating-points-v2-kryo-cpu"; compatible = "operating-points-v2-kryo-cpu";
opp-shared; opp-shared;
...@@ -128,7 +128,7 @@ opp-1401600000 { ...@@ -128,7 +128,7 @@ opp-1401600000 {
}; };
}; };
cpr_opp_table: cpr-opp-table { cpr_opp_table: opp-table-cpr {
compatible = "operating-points-v2-qcom-level"; compatible = "operating-points-v2-qcom-level";
cpr_opp1: opp1 { cpr_opp1: opp1 {
......
...@@ -113,6 +113,31 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *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); 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 * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp
* @opp: opp for which frequency has to be returned for * @opp: opp for which frequency has to be returned for
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -99,6 +100,9 @@ static void opp_debug_create_supplies(struct dev_pm_opp *opp, ...@@ -99,6 +100,9 @@ static void opp_debug_create_supplies(struct dev_pm_opp *opp,
debugfs_create_ulong("u_amp", S_IRUGO, d, debugfs_create_ulong("u_amp", S_IRUGO, d,
&opp->supplies[i].u_amp); &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) ...@@ -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_bool("suspend", S_IRUGO, d, &opp->suspend);
debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate); debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate);
debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate); 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, debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
&opp->clock_latency_ns); &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_supplies(opp, opp_table, d);
opp_debug_create_bw(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, ...@@ -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, static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
struct opp_table *opp_table) struct opp_table *opp_table)
{ {
u32 *microvolt, *microamp = NULL; u32 *microvolt, *microamp = NULL, *microwatt = NULL;
int supplies = opp_table->regulator_count, vcount, icount, ret, i, j; int supplies = opp_table->regulator_count;
int vcount, icount, pcount, ret, i, j;
struct property *prop = NULL; struct property *prop = NULL;
char name[NAME_MAX]; char name[NAME_MAX];
...@@ -688,6 +689,43 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -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++) { for (i = 0, j = 0; i < supplies; i++) {
opp->supplies[i].u_volt = microvolt[j++]; opp->supplies[i].u_volt = microvolt[j++];
...@@ -701,8 +739,13 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -701,8 +739,13 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
if (microamp) if (microamp)
opp->supplies[i].u_amp = microamp[i]; opp->supplies[i].u_amp = microamp[i];
if (microwatt)
opp->supplies[i].u_watt = microwatt[i];
} }
free_microwatt:
kfree(microwatt);
free_microamp: free_microamp:
kfree(microamp); kfree(microamp);
free_microvolt: free_microvolt:
...@@ -1395,6 +1438,38 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) ...@@ -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); 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. * Callback function provided to the Energy Model framework upon registration.
* 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
...@@ -1445,6 +1520,24 @@ static int __maybe_unused _get_power(unsigned long *mW, unsigned long *kHz, ...@@ -1445,6 +1520,24 @@ static int __maybe_unused _get_power(unsigned long *mW, unsigned long *kHz,
return 0; 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_pm_opp_of_register_em() - Attempt to register an Energy Model
* @dev : Device for which an Energy Model has to be registered * @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, ...@@ -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) 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; struct device_node *np;
int ret, nr_opp; int ret, nr_opp;
u32 cap; u32 cap;
...@@ -1474,6 +1567,12 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus) ...@@ -1474,6 +1567,12 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto failed; 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); np = of_node_get(dev->of_node);
if (!np) { if (!np) {
ret = -EINVAL; ret = -EINVAL;
...@@ -1495,6 +1594,9 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus) ...@@ -1495,6 +1594,9 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto failed; 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); ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
if (ret) if (ret)
goto failed; goto failed;
......
...@@ -96,6 +96,7 @@ struct dev_pm_opp { ...@@ -96,6 +96,7 @@ struct dev_pm_opp {
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *dentry; struct dentry *dentry;
const char *of_name;
#endif #endif
}; };
......
...@@ -116,6 +116,7 @@ struct em_data_callback { ...@@ -116,6 +116,7 @@ struct em_data_callback {
struct device *dev); struct device *dev);
}; };
#define EM_DATA_CB(_active_power_cb) { .active_power = &_active_power_cb } #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_cpu_get(int cpu);
struct em_perf_domain *em_pd_get(struct device *dev); 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) ...@@ -264,6 +265,7 @@ static inline int em_pd_nr_perf_states(struct em_perf_domain *pd)
#else #else
struct em_data_callback {}; struct em_data_callback {};
#define EM_DATA_CB(_active_power_cb) { } #define EM_DATA_CB(_active_power_cb) { }
#define EM_SET_ACTIVE_POWER_CB(em_cb, cb) do { } while (0)
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,
......
...@@ -32,14 +32,17 @@ enum dev_pm_opp_event { ...@@ -32,14 +32,17 @@ enum dev_pm_opp_event {
* @u_volt_min: Minimum voltage in microvolts corresponding to this OPP * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP
* @u_volt_max: Maximum 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_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 { struct dev_pm_opp_supply {
unsigned long u_volt; unsigned long u_volt;
unsigned long u_volt_min; unsigned long u_volt_min;
unsigned long u_volt_max; unsigned long u_volt_max;
unsigned long u_amp; unsigned long u_amp;
unsigned long u_watt;
}; };
/** /**
...@@ -94,6 +97,8 @@ void dev_pm_opp_put_opp_table(struct opp_table *opp_table); ...@@ -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_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 long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
unsigned int dev_pm_opp_get_level(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) ...@@ -186,6 +191,11 @@ static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
return 0; 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) static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp)
{ {
return 0; 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