ARM: SoC drivers for 6.0
The SoC driver updates contain changes to improve support for additional SoC variants, as well as cleanups an minor bugfixes in a number of existing drivers. Notable updates this time include: - Support for Qualcomm MSM8909 (Snapdragon 210) in various drivers - Updates for interconnect drivers on Qualcomm Snapdragon - A new driver support for NMI interrupts on Fujitsu A64fx - A rework of Broadcom BCMBCA Kconfig dependencies - Improved support for BCM2711 (Raspberry Pi 4) power management to allow the use of the V3D GPU - Cleanups to the NXP guts driver - Arm SCMI firmware driver updates to add tracing support, and use the firmware interfaces for system power control and for power capping. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmLo+0UACgkQmmx57+YA GNkkFw//eyeMxsJ/pqp0HeXTX7s2p0+39IQiak0hjFNe3XN12PIMRAHHtutKlt7F K0fKksokY9p+o1M86/5D4l7v7S6DcHQk2MdUD5AeMu/If7cfL0TI2mdIAVnoad4o p54ABR0q2Tr/Dr/2GK8kZPTnXkPPLd951mgCG/jwrlPgG3KjTgjhEWg86+F2s/B/ P8ryYakCYfsxPJGnZqyw63JuVet9Tnv87jySxabukNfSRR8RbJ+OVKXxaBBmvYkA +UuDEkcuPtlrEyUoNe+WtM07BdxP6tl/jRwZ4EenQtFDSLCQnapgHK3bVRbLs/WL naKJZgI7OOwU8fjRi90/zYoPBW6UQ9OoxcmshNwgFM5yBLJtVgGM+Fp8XNfPIvm0 BYvE7jf8cEtFDAOYWuB8jCdvBen8qzt5eeUpV+uOjHDxiWwfif15yUDxCE3GB7Ov vSPRpuTec/6Ry5hIbqcsrTcZRihnJbAJqDOHdlSVX3M+ohXcf5OMLd+IbD+oHgpo vSKvitkDnCKvdR6Uw1GSNAgeVm1ItMBlcL/8VsurOEUXR301pFNGdHEGuuxDu1Mm rEzk37ajYmiol5uBYIU8mdYrlK2+52sWd5/22zIwhMIEgQbuPbouYWPfUSP9bb+U 9NlvjGVxK5U73UqcU/56nn7W9uRt0ArzSzON53wnBW3WjkcgMLk= =0dZI -----END PGP SIGNATURE----- Merge tag 'arm-drivers-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull ARM SoC drivers from Arnd Bergmann: "The SoC driver updates contain changes to improve support for additional SoC variants, as well as cleanups an minor bugfixes in a number of existing drivers. Notable updates this time include: - Support for Qualcomm MSM8909 (Snapdragon 210) in various drivers - Updates for interconnect drivers on Qualcomm Snapdragon - A new driver support for NMI interrupts on Fujitsu A64fx - A rework of Broadcom BCMBCA Kconfig dependencies - Improved support for BCM2711 (Raspberry Pi 4) power management to allow the use of the V3D GPU - Cleanups to the NXP guts driver - Arm SCMI firmware driver updates to add tracing support, and use the firmware interfaces for system power control and for power capping" * tag 'arm-drivers-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (125 commits) soc: a64fx-diag: disable modular build dt-bindings: soc: qcom: qcom,smd-rpm: add power-controller dt-bindings: soc: qcom: aoss: document qcom,sm8450-aoss-qmp dt-bindings: soc: qcom,rpmh-rsc: simplify qcom,tcs-config ARM: mach-qcom: Add support for MSM8909 dt-bindings: arm: cpus: Document "qcom,msm8909-smp" enable-method soc: qcom: spm: Add CPU data for MSM8909 dt-bindings: soc: qcom: spm: Add MSM8909 CPU compatible soc: qcom: rpmpd: Add compatible for MSM8909 dt-bindings: power: qcom-rpmpd: Add MSM8909 power domains soc: qcom: smd-rpm: Add compatible for MSM8909 dt-bindings: soc: qcom: smd-rpm: Add MSM8909 soc: qcom: icc-bwmon: Remove unnecessary print function dev_err() soc: fujitsu: Add A64FX diagnostic interrupt driver soc: qcom: socinfo: Fix the id of SA8540P SoC soc: qcom: Make QCOM_RPMPD depend on PM tty: serial: bcm63xx: bcmbca: Replace ARCH_BCM_63XX with ARCH_BCMBCA spi: bcm63xx-hsspi: bcmbca: Replace ARCH_BCM_63XX with ARCH_BCMBCA clk: bcm: bcmbca: Replace ARCH_BCM_63XX with ARCH_BCMBCA hwrng: bcm2835: bcmbca: Replace ARCH_BCM_63XX with ARCH_BCMBCA ...
This commit is contained in:
commit
47b62edcd4
@ -221,6 +221,7 @@ properties:
|
||||
- qcom,kpss-acc-v1
|
||||
- qcom,kpss-acc-v2
|
||||
- qcom,msm8226-smp
|
||||
- qcom,msm8909-smp
|
||||
# Only valid on ARM 32-bit, see above for ARM v8 64-bit
|
||||
- qcom,msm8916-smp
|
||||
- renesas,apmu
|
||||
|
@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
title: QCOM device tree bindings
|
||||
|
||||
maintainers:
|
||||
- Stephen Boyd <sboyd@codeaurora.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description: |
|
||||
Some qcom based bootloaders identify the dtb blob based on a set of
|
||||
|
@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/arm/tegra/nvidia,tegra194-axi2apb.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: NVIDIA Tegra194 AXI2APB bridge
|
||||
|
||||
maintainers:
|
||||
- Sumit Gupta <sumitg@nvidia.com>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^axi2apb@([0-9a-f]+)$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- nvidia,tegra194-axi2apb
|
||||
|
||||
reg:
|
||||
maxItems: 6
|
||||
description: Physical base address and length of registers for all bridges
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
axi2apb: axi2apb@2390000 {
|
||||
compatible = "nvidia,tegra194-axi2apb";
|
||||
reg = <0x02390000 0x1000>,
|
||||
<0x023a0000 0x1000>,
|
||||
<0x023b0000 0x1000>,
|
||||
<0x023c0000 0x1000>,
|
||||
<0x023d0000 0x1000>,
|
||||
<0x023e0000 0x1000>;
|
||||
};
|
@ -0,0 +1,97 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/arm/tegra/nvidia,tegra194-cbb.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: NVIDIA Tegra194 CBB 1.0 bindings
|
||||
|
||||
maintainers:
|
||||
- Sumit Gupta <sumitg@nvidia.com>
|
||||
|
||||
description: |+
|
||||
The Control Backbone (CBB) is comprised of the physical path from an
|
||||
initiator to a target's register configuration space. CBB 1.0 has
|
||||
multiple hierarchical sub-NOCs (Network-on-Chip) and connects various
|
||||
initiators and targets using different bridges like AXIP2P, AXI2APB.
|
||||
|
||||
This driver handles errors due to illegal register accesses reported
|
||||
by the NOCs inside the CBB. NOCs reporting errors are cluster NOCs
|
||||
"AON-NOC, SCE-NOC, RCE-NOC, BPMP-NOC, CV-NOC" and "CBB Central NOC"
|
||||
which is the main NOC.
|
||||
|
||||
By default, the access issuing initiator is informed about the error
|
||||
using SError or Data Abort exception unless the ERD (Error Response
|
||||
Disable) is enabled/set for that initiator. If the ERD is enabled, then
|
||||
SError or Data Abort is masked and the error is reported with interrupt.
|
||||
|
||||
- For CCPLEX (CPU Complex) initiator, the driver sets ERD bit. So, the
|
||||
errors due to illegal accesses from CCPLEX are reported by interrupts.
|
||||
If ERD is not set, then error is reported by SError.
|
||||
- For other initiators, the ERD is disabled. So, the access issuing
|
||||
initiator is informed about the illegal access by Data Abort exception.
|
||||
In addition, an interrupt is also generated to CCPLEX. These initiators
|
||||
include all engines using Cortex-R5 (which is ARMv7 CPU cluster) and
|
||||
engines like TSEC (Security co-processor), NVDEC (NVIDIA Video Decoder
|
||||
engine) etc which can initiate transactions.
|
||||
|
||||
The driver prints relevant debug information like Error Code, Error
|
||||
Description, Master, Address, AXI ID, Cache, Protection, Security Group
|
||||
etc on receiving error notification.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^[a-z]+-noc@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- nvidia,tegra194-cbb-noc
|
||||
- nvidia,tegra194-aon-noc
|
||||
- nvidia,tegra194-bpmp-noc
|
||||
- nvidia,tegra194-rce-noc
|
||||
- nvidia,tegra194-sce-noc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
CCPLEX receives secure or nonsecure interrupt depending on error type.
|
||||
A secure interrupt is received for SEC(firewall) & SLV errors and a
|
||||
non-secure interrupt is received for TMO & DEC errors.
|
||||
items:
|
||||
- description: non-secure interrupt
|
||||
- description: secure interrupt
|
||||
|
||||
nvidia,axi2apb:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle'
|
||||
description:
|
||||
Specifies the node having all axi2apb bridges which need to be checked
|
||||
for any error logged in their status register.
|
||||
|
||||
nvidia,apbmisc:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle'
|
||||
description:
|
||||
Specifies the apbmisc node which need to be used for reading the ERD
|
||||
register.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- nvidia,apbmisc
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
cbb-noc@2300000 {
|
||||
compatible = "nvidia,tegra194-cbb-noc";
|
||||
reg = <0x02300000 0x1000>;
|
||||
interrupts = <GIC_SPI 230 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 231 IRQ_TYPE_LEVEL_HIGH>;
|
||||
nvidia,axi2apb = <&axi2apb>;
|
||||
nvidia,apbmisc = <&apbmisc>;
|
||||
};
|
@ -0,0 +1,74 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/arm/tegra/nvidia,tegra234-cbb.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: NVIDIA Tegra CBB 2.0 bindings
|
||||
|
||||
maintainers:
|
||||
- Sumit Gupta <sumitg@nvidia.com>
|
||||
|
||||
description: |+
|
||||
The Control Backbone (CBB) is comprised of the physical path from an
|
||||
initiator to a target's register configuration space. CBB 2.0 consists
|
||||
of multiple sub-blocks connected to each other to create a topology.
|
||||
The Tegra234 SoC has different fabrics based on CBB 2.0 architecture
|
||||
which include cluster fabrics BPMP, AON, PSC, SCE, RCE, DCE, FSI and
|
||||
"CBB central fabric".
|
||||
|
||||
In CBB 2.0, each initiator which can issue transactions connects to a
|
||||
Root Master Node (MN) before it connects to any other element of the
|
||||
fabric. Each Root MN contains a Error Monitor (EM) which detects and
|
||||
logs error. Interrupts from various EM blocks are collated by Error
|
||||
Notifier (EN) which is per fabric and presents a single interrupt from
|
||||
fabric to the SoC interrupt controller.
|
||||
|
||||
The driver handles errors from CBB due to illegal register accesses
|
||||
and prints debug information about failed transaction on receiving
|
||||
the interrupt from EN. Debug information includes Error Code, Error
|
||||
Description, MasterID, Fabric, SlaveID, Address, Cache, Protection,
|
||||
Security Group etc on receiving error notification.
|
||||
|
||||
If the Error Response Disable (ERD) is set/enabled for an initiator,
|
||||
then SError or Data abort exception error response is masked and an
|
||||
interrupt is used for reporting errors due to illegal accesses from
|
||||
that initiator. The value returned on read failures is '0xFFFFFFFF'
|
||||
for compatibility with PCIE.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^[a-z]+-fabric@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- nvidia,tegra234-aon-fabric
|
||||
- nvidia,tegra234-bpmp-fabric
|
||||
- nvidia,tegra234-cbb-fabric
|
||||
- nvidia,tegra234-dce-fabric
|
||||
- nvidia,tegra234-rce-fabric
|
||||
- nvidia,tegra234-sce-fabric
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: secure interrupt from error notifier
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
cbb-fabric@1300000 {
|
||||
compatible = "nvidia,tegra234-cbb-fabric";
|
||||
reg = <0x13a00000 0x400000>;
|
||||
interrupts = <GIC_SPI 231 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
@ -183,6 +183,12 @@ properties:
|
||||
required:
|
||||
- reg
|
||||
|
||||
protocol@18:
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
const: 0x18
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
patternProperties:
|
||||
@ -323,6 +329,10 @@ examples:
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
scmi_powercap: protocol@18 {
|
||||
reg = <0x18>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -23,10 +23,13 @@ Required properties:
|
||||
* "qcom,scm-msm8994"
|
||||
* "qcom,scm-msm8996"
|
||||
* "qcom,scm-msm8998"
|
||||
* "qcom,scm-qcs404"
|
||||
* "qcom,scm-sc7180"
|
||||
* "qcom,scm-sc7280"
|
||||
* "qcom,scm-sm6125"
|
||||
* "qcom,scm-sdm845"
|
||||
* "qcom,scm-sdx55"
|
||||
* "qcom,scm-sdx65"
|
||||
* "qcom,scm-sm6350"
|
||||
* "qcom,scm-sm8150"
|
||||
* "qcom,scm-sm8250"
|
||||
@ -43,6 +46,7 @@ Required properties:
|
||||
clock and "bus" for the bus clock per the requirements of the compatible.
|
||||
- qcom,dload-mode: phandle to the TCSR hardware block and offset of the
|
||||
download mode control register (optional)
|
||||
- interconnects: Specifies the bandwidth requirements of the SCM interface (optional)
|
||||
|
||||
Example for MSM8916:
|
||||
|
||||
|
@ -0,0 +1,86 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/interconnect/qcom,msm8998-bwmon.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Interconnect Bandwidth Monitor
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
|
||||
description: |
|
||||
Bandwidth Monitor measures current throughput on buses between various NoC
|
||||
fabrics and provides information when it crosses configured thresholds.
|
||||
|
||||
Certain SoCs might have more than one Bandwidth Monitors, for example on SDM845::
|
||||
- Measuring the bandwidth between CPUs and Last Level Cache Controller -
|
||||
called just BWMON,
|
||||
- Measuring the bandwidth between Last Level Cache Controller and memory
|
||||
(DDR) - called LLCC BWMON.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sdm845-bwmon
|
||||
- const: qcom,msm8998-bwmon
|
||||
- const: qcom,msm8998-bwmon # BWMON v4
|
||||
|
||||
interconnects:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
operating-points-v2: true
|
||||
opp-table: true
|
||||
|
||||
reg:
|
||||
# BWMON v4 (currently described) and BWMON v5 use one register address
|
||||
# space. BWMON v2 uses two register spaces - not yet described.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interconnects
|
||||
- interrupts
|
||||
- operating-points-v2
|
||||
- opp-table
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interconnect/qcom,sdm845.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
pmu@1436400 {
|
||||
compatible = "qcom,sdm845-bwmon", "qcom,msm8998-bwmon";
|
||||
reg = <0x01436400 0x600>;
|
||||
interrupts = <GIC_SPI 581 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interconnects = <&gladiator_noc MASTER_APPSS_PROC 3 &mem_noc SLAVE_LLCC 3>;
|
||||
|
||||
operating-points-v2 = <&cpu_bwmon_opp_table>;
|
||||
|
||||
cpu_bwmon_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
opp-0 {
|
||||
opp-peak-kBps = <4800000>;
|
||||
};
|
||||
opp-1 {
|
||||
opp-peak-kBps = <9216000>;
|
||||
};
|
||||
opp-2 {
|
||||
opp-peak-kBps = <15052800>;
|
||||
};
|
||||
opp-3 {
|
||||
opp-peak-kBps = <20889600>;
|
||||
};
|
||||
opp-4 {
|
||||
opp-peak-kBps = <25497600>;
|
||||
};
|
||||
};
|
||||
};
|
@ -32,6 +32,7 @@ properties:
|
||||
- mediatek,mt2701-smi-common
|
||||
- mediatek,mt2712-smi-common
|
||||
- mediatek,mt6779-smi-common
|
||||
- mediatek,mt6795-smi-common
|
||||
- mediatek,mt8167-smi-common
|
||||
- mediatek,mt8173-smi-common
|
||||
- mediatek,mt8183-smi-common
|
||||
|
@ -20,6 +20,7 @@ properties:
|
||||
- mediatek,mt2701-smi-larb
|
||||
- mediatek,mt2712-smi-larb
|
||||
- mediatek,mt6779-smi-larb
|
||||
- mediatek,mt6795-smi-larb
|
||||
- mediatek,mt8167-smi-larb
|
||||
- mediatek,mt8173-smi-larb
|
||||
- mediatek,mt8183-smi-larb
|
||||
|
@ -23,6 +23,7 @@ properties:
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt6795-power-controller
|
||||
- mediatek,mt8167-power-controller
|
||||
- mediatek,mt8173-power-controller
|
||||
- mediatek,mt8183-power-controller
|
||||
@ -62,6 +63,7 @@ patternProperties:
|
||||
reg:
|
||||
description: |
|
||||
Power domain index. Valid values are defined in:
|
||||
"include/dt-bindings/power/mt6795-power.h" - for MT8167 type power domain.
|
||||
"include/dt-bindings/power/mt8167-power.h" - for MT8167 type power domain.
|
||||
"include/dt-bindings/power/mt8173-power.h" - for MT8173 type power domain.
|
||||
"include/dt-bindings/power/mt8183-power.h" - for MT8183 type power domain.
|
||||
|
@ -18,6 +18,7 @@ properties:
|
||||
enum:
|
||||
- qcom,mdm9607-rpmpd
|
||||
- qcom,msm8226-rpmpd
|
||||
- qcom,msm8909-rpmpd
|
||||
- qcom,msm8916-rpmpd
|
||||
- qcom,msm8939-rpmpd
|
||||
- qcom,msm8953-rpmpd
|
||||
|
@ -20,6 +20,7 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt6779-devapc
|
||||
- mediatek,mt8186-devapc
|
||||
|
||||
reg:
|
||||
description: The base address of devapc register bank
|
||||
|
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/mediatek/mediatek,mutex.yaml#
|
||||
$id: http://devicetree.org/schemas/soc/mediatek/mediatek,mutex.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Mediatek mutex
|
||||
@ -55,6 +55,18 @@ properties:
|
||||
include/dt-bindings/gce/<chip>-gce.h of each chips.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
mediatek,gce-client-reg:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
items:
|
||||
- description: phandle of GCE
|
||||
- description: GCE subsys id
|
||||
- description: register offset
|
||||
- description: register size
|
||||
description: The register of client driver can be configured by gce with
|
||||
4 arguments defined in this property. Each GCE subsys id is mapping to
|
||||
a client defined in the header include/dt-bindings/gce/<chip>-gce.h.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
91
Documentation/devicetree/bindings/soc/mediatek/mtk-svs.yaml
Normal file
91
Documentation/devicetree/bindings/soc/mediatek/mtk-svs.yaml
Normal file
@ -0,0 +1,91 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/mediatek/mtk-svs.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek Smart Voltage Scaling (SVS) Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Roger Lu <roger.lu@mediatek.com>
|
||||
- Matthias Brugger <matthias.bgg@gmail.com>
|
||||
- Kevin Hilman <khilman@kernel.org>
|
||||
|
||||
description: |+
|
||||
The SVS engine is a piece of hardware which has several
|
||||
controllers(banks) for calculating suitable voltage to
|
||||
different power domains(CPU/GPU/CCI) according to
|
||||
chip process corner, temperatures and other factors. Then DVFS
|
||||
driver could apply SVS bank voltage to PMIC/Buck.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt8183-svs
|
||||
- mediatek,mt8192-svs
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: Address range of the MTK SVS controller.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: Main clock for MTK SVS controller to work.
|
||||
|
||||
clock-names:
|
||||
const: main
|
||||
|
||||
nvmem-cells:
|
||||
minItems: 1
|
||||
description:
|
||||
Phandle to the calibration data provided by a nvmem device.
|
||||
items:
|
||||
- description: SVS efuse for SVS controller
|
||||
- description: Thermal efuse for SVS controller
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: svs-calibration-data
|
||||
- const: t-calibration-data
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: svs_rst
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- nvmem-cells
|
||||
- nvmem-cell-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/mt8183-clk.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
svs@1100b000 {
|
||||
compatible = "mediatek,mt8183-svs";
|
||||
reg = <0 0x1100b000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <&infracfg CLK_INFRA_THERM>;
|
||||
clock-names = "main";
|
||||
nvmem-cells = <&svs_calibration>, <&thermal_calibration>;
|
||||
nvmem-cell-names = "svs-calibration-data", "t-calibration-data";
|
||||
};
|
||||
};
|
@ -33,6 +33,7 @@ properties:
|
||||
- qcom,sm8150-aoss-qmp
|
||||
- qcom,sm8250-aoss-qmp
|
||||
- qcom,sm8350-aoss-qmp
|
||||
- qcom,sm8450-aoss-qmp
|
||||
- const: qcom,aoss-qmp
|
||||
|
||||
reg:
|
||||
|
@ -65,33 +65,22 @@ properties:
|
||||
|
||||
qcom,tcs-config:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
items:
|
||||
- items:
|
||||
- description: TCS type
|
||||
enum: [ 0, 1, 2, 3 ]
|
||||
- description: Number of TCS
|
||||
- items:
|
||||
- description: TCS type
|
||||
enum: [ 0, 1, 2, 3 ]
|
||||
- description: Number of TCS
|
||||
- items:
|
||||
- description: TCS type
|
||||
enum: [ 0, 1, 2, 3]
|
||||
- description: Numbe r of TCS
|
||||
- items:
|
||||
- description: TCS type
|
||||
enum: [ 0, 1, 2, 3 ]
|
||||
- description: Number of TCS
|
||||
items:
|
||||
- description: |
|
||||
TCS type::
|
||||
- ACTIVE_TCS
|
||||
- SLEEP_TCS
|
||||
- WAKE_TCS
|
||||
- CONTROL_TCS
|
||||
enum: [ 0, 1, 2, 3 ]
|
||||
- description: Number of TCS
|
||||
description: |
|
||||
The tuple defining the configuration of TCS. Must have two cells which
|
||||
describe each TCS type. The order of the TCS must match the hardware
|
||||
configuration.
|
||||
Cell 1 (TCS Type):: TCS types to be specified::
|
||||
- ACTIVE_TCS
|
||||
- SLEEP_TCS
|
||||
- WAKE_TCS
|
||||
- CONTROL_TCS
|
||||
Cell 2 (Number of TCS):: <u32>
|
||||
|
||||
qcom,tcs-offset:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
@ -34,6 +34,7 @@ properties:
|
||||
- qcom,rpm-apq8084
|
||||
- qcom,rpm-ipq6018
|
||||
- qcom,rpm-msm8226
|
||||
- qcom,rpm-msm8909
|
||||
- qcom,rpm-msm8916
|
||||
- qcom,rpm-msm8936
|
||||
- qcom,rpm-msm8953
|
||||
@ -51,6 +52,9 @@ properties:
|
||||
$ref: /schemas/clock/qcom,rpmcc.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
power-controller:
|
||||
$ref: /schemas/power/qcom,rpmpd.yaml#
|
||||
|
||||
qcom,smd-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
description: Channel name used for the RPM communication
|
||||
|
@ -22,6 +22,7 @@ properties:
|
||||
- qcom,sdm660-silver-saw2-v4.1-l2
|
||||
- qcom,msm8998-gold-saw2-v4.1-l2
|
||||
- qcom,msm8998-silver-saw2-v4.1-l2
|
||||
- qcom,msm8909-saw2-v3.0-cpu
|
||||
- qcom,msm8916-saw2-v3.0-cpu
|
||||
- qcom,msm8226-saw2-v2.1-cpu
|
||||
- qcom,msm8974-saw2-v2.1-cpu
|
||||
|
@ -77,7 +77,6 @@ properties:
|
||||
Should reference the tx-enable and tx-rings-empty SMEM states.
|
||||
|
||||
qcom,smem-state-names:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
items:
|
||||
- const: tx-enable
|
||||
- const: tx-rings-empty
|
||||
|
@ -65,10 +65,11 @@ properties:
|
||||
- ti,am4376-pruss0 # for AM437x SoC family and PRUSS unit 0
|
||||
- ti,am4376-pruss1 # for AM437x SoC family and PRUSS unit 1
|
||||
- ti,am5728-pruss # for AM57xx SoC family
|
||||
- ti,k2g-pruss # for 66AK2G SoC family
|
||||
- ti,am625-pruss # for K3 AM62x SoC family
|
||||
- ti,am642-icssg # for K3 AM64x SoC family
|
||||
- ti,am654-icssg # for K3 AM65x SoC family
|
||||
- ti,j721e-icssg # for K3 J721E SoC family
|
||||
- ti,am642-icssg # for K3 AM64x SoC family
|
||||
- ti,k2g-pruss # for 66AK2G SoC family
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
12
MAINTAINERS
12
MAINTAINERS
@ -242,6 +242,11 @@ F: include/trace/events/9p.h
|
||||
F: include/uapi/linux/virtio_9p.h
|
||||
F: net/9p/
|
||||
|
||||
A64FX DIAG DRIVER
|
||||
M: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>
|
||||
S: Supported
|
||||
F: drivers/soc/fujitsu/a64fx-diag.c
|
||||
|
||||
A8293 MEDIA DRIVER
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -16674,6 +16679,13 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
|
||||
F: drivers/i2c/busses/i2c-qcom-cci.c
|
||||
|
||||
QUALCOMM INTERCONNECT BWMON DRIVER
|
||||
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml
|
||||
F: drivers/soc/qcom/icc-bwmon.c
|
||||
|
||||
QUALCOMM IOMMU
|
||||
M: Rob Clark <robdclark@gmail.com>
|
||||
L: iommu@lists.linux.dev
|
||||
|
@ -20,6 +20,10 @@ config ARCH_MSM8X60
|
||||
bool "Enable support for MSM8X60"
|
||||
select CLKSRC_QCOM
|
||||
|
||||
config ARCH_MSM8909
|
||||
bool "Enable support for MSM8909"
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
|
||||
config ARCH_MSM8916
|
||||
bool "Enable support for MSM8916"
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
|
@ -384,6 +384,7 @@ static const struct smp_operations qcom_smp_cortex_a7_ops __initconst = {
|
||||
#endif
|
||||
};
|
||||
CPU_METHOD_OF_DECLARE(qcom_smp_msm8226, "qcom,msm8226-smp", &qcom_smp_cortex_a7_ops);
|
||||
CPU_METHOD_OF_DECLARE(qcom_smp_msm8909, "qcom,msm8909-smp", &qcom_smp_cortex_a7_ops);
|
||||
CPU_METHOD_OF_DECLARE(qcom_smp_msm8916, "qcom,msm8916-smp", &qcom_smp_cortex_a7_ops);
|
||||
|
||||
static const struct smp_operations qcom_smp_kpssv1_ops __initconst = {
|
||||
|
@ -148,7 +148,7 @@ config SATA_AHCI_PLATFORM
|
||||
config AHCI_BRCM
|
||||
tristate "Broadcom AHCI SATA support"
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM_NSP || \
|
||||
ARCH_BCM_63XX || COMPILE_TEST
|
||||
ARCH_BCMBCA || COMPILE_TEST
|
||||
select SATA_HOST
|
||||
help
|
||||
This option enables support for the AHCI SATA3 controller found on
|
||||
|
@ -87,7 +87,7 @@ config HW_RANDOM_BA431
|
||||
config HW_RANDOM_BCM2835
|
||||
tristate "Broadcom BCM2835/BCM63xx Random Number Generator support"
|
||||
depends on ARCH_BCM2835 || ARCH_BCM_NSP || ARCH_BCM_5301X || \
|
||||
ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC || COMPILE_TEST
|
||||
ARCH_BCMBCA || BCM63XX || BMIPS_GENERIC || COMPILE_TEST
|
||||
default HW_RANDOM
|
||||
help
|
||||
This driver provides kernel-side support for the Random Number
|
||||
|
@ -22,9 +22,9 @@ config CLK_BCM2835
|
||||
|
||||
config CLK_BCM_63XX
|
||||
bool "Broadcom BCM63xx clock support"
|
||||
depends on ARCH_BCM_63XX || COMPILE_TEST
|
||||
depends on ARCH_BCMBCA || COMPILE_TEST
|
||||
select COMMON_CLK_IPROC
|
||||
default ARCH_BCM_63XX
|
||||
default ARCH_BCMBCA
|
||||
help
|
||||
Enable common clock framework support for Broadcom BCM63xx DSL SoCs
|
||||
based on the ARM architecture
|
||||
|
@ -149,4 +149,16 @@ config ARM_SCMI_POWER_DOMAIN
|
||||
will be called scmi_pm_domain. Note this may needed early in boot
|
||||
before rootfs may be available.
|
||||
|
||||
config ARM_SCMI_POWER_CONTROL
|
||||
tristate "SCMI system power control driver"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
help
|
||||
This enables System Power control logic which binds system shutdown or
|
||||
reboot actions to SCMI System Power notifications generated by SCP
|
||||
firmware.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called scmi_power_control. Note this may needed early in boot to catch
|
||||
early shutdown/reboot SCMI requests.
|
||||
|
||||
endmenu
|
||||
|
@ -7,11 +7,12 @@ scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += optee.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
|
||||
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
|
||||
$(scmi-transport-y)
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o
|
||||
|
||||
ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy)
|
||||
# The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/hashtable.h>
|
||||
@ -60,6 +61,11 @@ static atomic_t transfer_last_id;
|
||||
static DEFINE_IDR(scmi_requested_devices);
|
||||
static DEFINE_MUTEX(scmi_requested_devices_mtx);
|
||||
|
||||
/* Track globally the creation of SCMI SystemPower related devices */
|
||||
static bool scmi_syspower_registered;
|
||||
/* Protect access to scmi_syspower_registered */
|
||||
static DEFINE_MUTEX(scmi_syspower_mtx);
|
||||
|
||||
struct scmi_requested_dev {
|
||||
const struct scmi_device_id *id_table;
|
||||
struct list_head node;
|
||||
@ -660,6 +666,11 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
|
||||
smp_store_mb(xfer->priv, priv);
|
||||
info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size,
|
||||
xfer);
|
||||
|
||||
trace_scmi_msg_dump(xfer->hdr.protocol_id, xfer->hdr.id, "NOTI",
|
||||
xfer->hdr.seq, xfer->hdr.status,
|
||||
xfer->rx.buf, xfer->rx.len);
|
||||
|
||||
scmi_notify(cinfo->handle, xfer->hdr.protocol_id,
|
||||
xfer->hdr.id, xfer->rx.buf, xfer->rx.len, ts);
|
||||
|
||||
@ -694,6 +705,12 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
|
||||
smp_store_mb(xfer->priv, priv);
|
||||
info->desc->ops->fetch_response(cinfo, xfer);
|
||||
|
||||
trace_scmi_msg_dump(xfer->hdr.protocol_id, xfer->hdr.id,
|
||||
xfer->hdr.type == MSG_TYPE_DELAYED_RESP ?
|
||||
"DLYD" : "RESP",
|
||||
xfer->hdr.seq, xfer->hdr.status,
|
||||
xfer->rx.buf, xfer->rx.len);
|
||||
|
||||
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
|
||||
xfer->hdr.protocol_id, xfer->hdr.seq,
|
||||
xfer->hdr.type);
|
||||
@ -827,6 +844,12 @@ static int scmi_wait_for_message_response(struct scmi_chan_info *cinfo,
|
||||
xfer->state = SCMI_XFER_RESP_OK;
|
||||
}
|
||||
spin_unlock_irqrestore(&xfer->lock, flags);
|
||||
|
||||
/* Trace polled replies. */
|
||||
trace_scmi_msg_dump(xfer->hdr.protocol_id, xfer->hdr.id,
|
||||
"RESP",
|
||||
xfer->hdr.seq, xfer->hdr.status,
|
||||
xfer->rx.buf, xfer->rx.len);
|
||||
}
|
||||
} else {
|
||||
/* And we wait for the response. */
|
||||
@ -903,6 +926,10 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
trace_scmi_msg_dump(xfer->hdr.protocol_id, xfer->hdr.id, "CMND",
|
||||
xfer->hdr.seq, xfer->hdr.status,
|
||||
xfer->tx.buf, xfer->tx.len);
|
||||
|
||||
ret = scmi_wait_for_message_response(cinfo, xfer);
|
||||
if (!ret && xfer->hdr.status)
|
||||
ret = scmi_to_linux_errno(xfer->hdr.status);
|
||||
@ -1259,10 +1286,174 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct scmi_msg_get_fc_info {
|
||||
__le32 domain;
|
||||
__le32 message_id;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_desc_fc {
|
||||
__le32 attr;
|
||||
#define SUPPORTS_DOORBELL(x) ((x) & BIT(0))
|
||||
#define DOORBELL_REG_WIDTH(x) FIELD_GET(GENMASK(2, 1), (x))
|
||||
__le32 rate_limit;
|
||||
__le32 chan_addr_low;
|
||||
__le32 chan_addr_high;
|
||||
__le32 chan_size;
|
||||
__le32 db_addr_low;
|
||||
__le32 db_addr_high;
|
||||
__le32 db_set_lmask;
|
||||
__le32 db_set_hmask;
|
||||
__le32 db_preserve_lmask;
|
||||
__le32 db_preserve_hmask;
|
||||
};
|
||||
|
||||
static void
|
||||
scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
|
||||
u8 describe_id, u32 message_id, u32 valid_size,
|
||||
u32 domain, void __iomem **p_addr,
|
||||
struct scmi_fc_db_info **p_db)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
u64 phys_addr;
|
||||
u8 size;
|
||||
void __iomem *addr;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_fc_db_info *db = NULL;
|
||||
struct scmi_msg_get_fc_info *info;
|
||||
struct scmi_msg_resp_desc_fc *resp;
|
||||
const struct scmi_protocol_instance *pi = ph_to_pi(ph);
|
||||
|
||||
if (!p_addr) {
|
||||
ret = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, describe_id,
|
||||
sizeof(*info), sizeof(*resp), &t);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
info = t->tx.buf;
|
||||
info->domain = cpu_to_le32(domain);
|
||||
info->message_id = cpu_to_le32(message_id);
|
||||
|
||||
/*
|
||||
* Bail out on error leaving fc_info addresses zeroed; this includes
|
||||
* the case in which the requested domain/message_id does NOT support
|
||||
* fastchannels at all.
|
||||
*/
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (ret)
|
||||
goto err_xfer;
|
||||
|
||||
resp = t->rx.buf;
|
||||
flags = le32_to_cpu(resp->attr);
|
||||
size = le32_to_cpu(resp->chan_size);
|
||||
if (size != valid_size) {
|
||||
ret = -EINVAL;
|
||||
goto err_xfer;
|
||||
}
|
||||
|
||||
phys_addr = le32_to_cpu(resp->chan_addr_low);
|
||||
phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
|
||||
addr = devm_ioremap(ph->dev, phys_addr, size);
|
||||
if (!addr) {
|
||||
ret = -EADDRNOTAVAIL;
|
||||
goto err_xfer;
|
||||
}
|
||||
|
||||
*p_addr = addr;
|
||||
|
||||
if (p_db && SUPPORTS_DOORBELL(flags)) {
|
||||
db = devm_kzalloc(ph->dev, sizeof(*db), GFP_KERNEL);
|
||||
if (!db) {
|
||||
ret = -ENOMEM;
|
||||
goto err_db;
|
||||
}
|
||||
|
||||
size = 1 << DOORBELL_REG_WIDTH(flags);
|
||||
phys_addr = le32_to_cpu(resp->db_addr_low);
|
||||
phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32;
|
||||
addr = devm_ioremap(ph->dev, phys_addr, size);
|
||||
if (!addr) {
|
||||
ret = -EADDRNOTAVAIL;
|
||||
goto err_db_mem;
|
||||
}
|
||||
|
||||
db->addr = addr;
|
||||
db->width = size;
|
||||
db->set = le32_to_cpu(resp->db_set_lmask);
|
||||
db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32;
|
||||
db->mask = le32_to_cpu(resp->db_preserve_lmask);
|
||||
db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32;
|
||||
|
||||
*p_db = db;
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
dev_dbg(ph->dev,
|
||||
"Using valid FC for protocol %X [MSG_ID:%u / RES_ID:%u]\n",
|
||||
pi->proto->id, message_id, domain);
|
||||
|
||||
return;
|
||||
|
||||
err_db_mem:
|
||||
devm_kfree(ph->dev, db);
|
||||
|
||||
err_db:
|
||||
*p_addr = NULL;
|
||||
|
||||
err_xfer:
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
err_out:
|
||||
dev_warn(ph->dev,
|
||||
"Failed to get FC for protocol %X [MSG_ID:%u / RES_ID:%u] - ret:%d. Using regular messaging.\n",
|
||||
pi->proto->id, message_id, domain, ret);
|
||||
}
|
||||
|
||||
#define SCMI_PROTO_FC_RING_DB(w) \
|
||||
do { \
|
||||
u##w val = 0; \
|
||||
\
|
||||
if (db->mask) \
|
||||
val = ioread##w(db->addr) & db->mask; \
|
||||
iowrite##w((u##w)db->set | val, db->addr); \
|
||||
} while (0)
|
||||
|
||||
static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db)
|
||||
{
|
||||
if (!db || !db->addr)
|
||||
return;
|
||||
|
||||
if (db->width == 1)
|
||||
SCMI_PROTO_FC_RING_DB(8);
|
||||
else if (db->width == 2)
|
||||
SCMI_PROTO_FC_RING_DB(16);
|
||||
else if (db->width == 4)
|
||||
SCMI_PROTO_FC_RING_DB(32);
|
||||
else /* db->width == 8 */
|
||||
#ifdef CONFIG_64BIT
|
||||
SCMI_PROTO_FC_RING_DB(64);
|
||||
#else
|
||||
{
|
||||
u64 val = 0;
|
||||
|
||||
if (db->mask)
|
||||
val = ioread64_hi_lo(db->addr) & db->mask;
|
||||
iowrite64_hi_lo(db->set | val, db->addr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct scmi_proto_helpers_ops helpers_ops = {
|
||||
.extended_name_get = scmi_common_extended_name_get,
|
||||
.iter_response_init = scmi_iterator_init,
|
||||
.iter_response_run = scmi_iterator_run,
|
||||
.fastchannel_init = scmi_common_fastchannel_init,
|
||||
.fastchannel_db_ring = scmi_common_fastchannel_db_ring,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1497,6 +1688,30 @@ static void scmi_devm_release_protocol(struct device *dev, void *res)
|
||||
scmi_protocol_release(dres->handle, dres->protocol_id);
|
||||
}
|
||||
|
||||
static struct scmi_protocol_instance __must_check *
|
||||
scmi_devres_protocol_instance_get(struct scmi_device *sdev, u8 protocol_id)
|
||||
{
|
||||
struct scmi_protocol_instance *pi;
|
||||
struct scmi_protocol_devres *dres;
|
||||
|
||||
dres = devres_alloc(scmi_devm_release_protocol,
|
||||
sizeof(*dres), GFP_KERNEL);
|
||||
if (!dres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pi = scmi_get_protocol_instance(sdev->handle, protocol_id);
|
||||
if (IS_ERR(pi)) {
|
||||
devres_free(dres);
|
||||
return pi;
|
||||
}
|
||||
|
||||
dres->handle = sdev->handle;
|
||||
dres->protocol_id = protocol_id;
|
||||
devres_add(&sdev->dev, dres);
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_devm_protocol_get - Devres managed get protocol operations and handle
|
||||
* @sdev: A reference to an scmi_device whose embedded struct device is to
|
||||
@ -1520,32 +1735,47 @@ scmi_devm_protocol_get(struct scmi_device *sdev, u8 protocol_id,
|
||||
struct scmi_protocol_handle **ph)
|
||||
{
|
||||
struct scmi_protocol_instance *pi;
|
||||
struct scmi_protocol_devres *dres;
|
||||
struct scmi_handle *handle = sdev->handle;
|
||||
|
||||
if (!ph)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
dres = devres_alloc(scmi_devm_release_protocol,
|
||||
sizeof(*dres), GFP_KERNEL);
|
||||
if (!dres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pi = scmi_get_protocol_instance(handle, protocol_id);
|
||||
if (IS_ERR(pi)) {
|
||||
devres_free(dres);
|
||||
pi = scmi_devres_protocol_instance_get(sdev, protocol_id);
|
||||
if (IS_ERR(pi))
|
||||
return pi;
|
||||
}
|
||||
|
||||
dres->handle = handle;
|
||||
dres->protocol_id = protocol_id;
|
||||
devres_add(&sdev->dev, dres);
|
||||
|
||||
*ph = &pi->ph;
|
||||
|
||||
return pi->proto->ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_devm_protocol_acquire - Devres managed helper to get hold of a protocol
|
||||
* @sdev: A reference to an scmi_device whose embedded struct device is to
|
||||
* be used for devres accounting.
|
||||
* @protocol_id: The protocol being requested.
|
||||
*
|
||||
* Get hold of a protocol accounting for its usage, possibly triggering its
|
||||
* initialization but without getting access to its protocol specific operations
|
||||
* and handle.
|
||||
*
|
||||
* Being a devres based managed method, protocol hold will be automatically
|
||||
* released, and possibly de-initialized on last user, once the SCMI driver
|
||||
* owning the scmi_device is unbound from it.
|
||||
*
|
||||
* Return: 0 on SUCCESS
|
||||
*/
|
||||
static int __must_check scmi_devm_protocol_acquire(struct scmi_device *sdev,
|
||||
u8 protocol_id)
|
||||
{
|
||||
struct scmi_protocol_instance *pi;
|
||||
|
||||
pi = scmi_devres_protocol_instance_get(sdev, protocol_id);
|
||||
if (IS_ERR(pi))
|
||||
return PTR_ERR(pi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_devm_protocol_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct scmi_protocol_devres *dres = res;
|
||||
@ -1849,21 +2079,39 @@ scmi_get_protocol_device(struct device_node *np, struct scmi_info *info,
|
||||
if (sdev)
|
||||
return sdev;
|
||||
|
||||
mutex_lock(&scmi_syspower_mtx);
|
||||
if (prot_id == SCMI_PROTOCOL_SYSTEM && scmi_syspower_registered) {
|
||||
dev_warn(info->dev,
|
||||
"SCMI SystemPower protocol device must be unique !\n");
|
||||
mutex_unlock(&scmi_syspower_mtx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pr_debug("Creating SCMI device (%s) for protocol %x\n", name, prot_id);
|
||||
|
||||
sdev = scmi_device_create(np, info->dev, prot_id, name);
|
||||
if (!sdev) {
|
||||
dev_err(info->dev, "failed to create %d protocol device\n",
|
||||
prot_id);
|
||||
mutex_unlock(&scmi_syspower_mtx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (scmi_txrx_setup(info, &sdev->dev, prot_id)) {
|
||||
dev_err(&sdev->dev, "failed to setup transport\n");
|
||||
scmi_device_destroy(sdev);
|
||||
mutex_unlock(&scmi_syspower_mtx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (prot_id == SCMI_PROTOCOL_SYSTEM)
|
||||
scmi_syspower_registered = true;
|
||||
|
||||
mutex_unlock(&scmi_syspower_mtx);
|
||||
|
||||
return sdev;
|
||||
}
|
||||
|
||||
@ -2132,6 +2380,7 @@ static int scmi_probe(struct platform_device *pdev)
|
||||
handle = &info->handle;
|
||||
handle->dev = info->dev;
|
||||
handle->version = &info->version;
|
||||
handle->devm_protocol_acquire = scmi_devm_protocol_acquire;
|
||||
handle->devm_protocol_get = scmi_devm_protocol_get;
|
||||
handle->devm_protocol_put = scmi_devm_protocol_put;
|
||||
|
||||
@ -2401,6 +2650,7 @@ static int __init scmi_driver_init(void)
|
||||
scmi_sensors_register();
|
||||
scmi_voltage_register();
|
||||
scmi_system_register();
|
||||
scmi_powercap_register();
|
||||
|
||||
return platform_driver_register(&scmi_driver);
|
||||
}
|
||||
@ -2417,6 +2667,7 @@ static void __exit scmi_driver_exit(void)
|
||||
scmi_sensors_unregister();
|
||||
scmi_voltage_unregister();
|
||||
scmi_system_unregister();
|
||||
scmi_powercap_unregister();
|
||||
|
||||
scmi_bus_exit();
|
||||
|
||||
|
@ -10,13 +10,14 @@
|
||||
#include <linux/bits.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include <trace/events/scmi.h>
|
||||
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
@ -35,6 +36,12 @@ enum scmi_performance_protocol_cmd {
|
||||
PERF_DOMAIN_NAME_GET = 0xc,
|
||||
};
|
||||
|
||||
enum {
|
||||
PERF_FC_LEVEL,
|
||||
PERF_FC_LIMIT,
|
||||
PERF_FC_MAX,
|
||||
};
|
||||
|
||||
struct scmi_opp {
|
||||
u32 perf;
|
||||
u32 power;
|
||||
@ -115,43 +122,6 @@ struct scmi_msg_resp_perf_describe_levels {
|
||||
} opp[];
|
||||
};
|
||||
|
||||
struct scmi_perf_get_fc_info {
|
||||
__le32 domain;
|
||||
__le32 message_id;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_perf_desc_fc {
|
||||
__le32 attr;
|
||||
#define SUPPORTS_DOORBELL(x) ((x) & BIT(0))
|
||||
#define DOORBELL_REG_WIDTH(x) FIELD_GET(GENMASK(2, 1), (x))
|
||||
__le32 rate_limit;
|
||||
__le32 chan_addr_low;
|
||||
__le32 chan_addr_high;
|
||||
__le32 chan_size;
|
||||
__le32 db_addr_low;
|
||||
__le32 db_addr_high;
|
||||
__le32 db_set_lmask;
|
||||
__le32 db_set_hmask;
|
||||
__le32 db_preserve_lmask;
|
||||
__le32 db_preserve_hmask;
|
||||
};
|
||||
|
||||
struct scmi_fc_db_info {
|
||||
int width;
|
||||
u64 set;
|
||||
u64 mask;
|
||||
void __iomem *addr;
|
||||
};
|
||||
|
||||
struct scmi_fc_info {
|
||||
void __iomem *level_set_addr;
|
||||
void __iomem *limit_set_addr;
|
||||
void __iomem *level_get_addr;
|
||||
void __iomem *limit_get_addr;
|
||||
struct scmi_fc_db_info *level_set_db;
|
||||
struct scmi_fc_db_info *limit_set_db;
|
||||
};
|
||||
|
||||
struct perf_dom_info {
|
||||
bool set_limits;
|
||||
bool set_perf;
|
||||
@ -360,40 +330,6 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SCMI_PERF_FC_RING_DB(w) \
|
||||
do { \
|
||||
u##w val = 0; \
|
||||
\
|
||||
if (db->mask) \
|
||||
val = ioread##w(db->addr) & db->mask; \
|
||||
iowrite##w((u##w)db->set | val, db->addr); \
|
||||
} while (0)
|
||||
|
||||
static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db)
|
||||
{
|
||||
if (!db || !db->addr)
|
||||
return;
|
||||
|
||||
if (db->width == 1)
|
||||
SCMI_PERF_FC_RING_DB(8);
|
||||
else if (db->width == 2)
|
||||
SCMI_PERF_FC_RING_DB(16);
|
||||
else if (db->width == 4)
|
||||
SCMI_PERF_FC_RING_DB(32);
|
||||
else /* db->width == 8 */
|
||||
#ifdef CONFIG_64BIT
|
||||
SCMI_PERF_FC_RING_DB(64);
|
||||
#else
|
||||
{
|
||||
u64 val = 0;
|
||||
|
||||
if (db->mask)
|
||||
val = ioread64_hi_lo(db->addr) & db->mask;
|
||||
iowrite64_hi_lo(db->set | val, db->addr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int scmi_perf_mb_limits_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, u32 max_perf, u32 min_perf)
|
||||
{
|
||||
@ -426,10 +362,14 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf)
|
||||
return -EINVAL;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->limit_set_addr) {
|
||||
iowrite32(max_perf, dom->fc_info->limit_set_addr);
|
||||
iowrite32(min_perf, dom->fc_info->limit_set_addr + 4);
|
||||
scmi_perf_fc_ring_db(dom->fc_info->limit_set_db);
|
||||
if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].set_addr) {
|
||||
struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LIMIT];
|
||||
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_SET,
|
||||
domain, min_perf, max_perf);
|
||||
iowrite32(max_perf, fci->set_addr);
|
||||
iowrite32(min_perf, fci->set_addr + 4);
|
||||
ph->hops->fastchannel_db_ring(fci->set_db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -468,9 +408,13 @@ static int scmi_perf_limits_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_perf_info *pi = ph->get_priv(ph);
|
||||
struct perf_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->limit_get_addr) {
|
||||
*max_perf = ioread32(dom->fc_info->limit_get_addr);
|
||||
*min_perf = ioread32(dom->fc_info->limit_get_addr + 4);
|
||||
if (dom->fc_info && dom->fc_info[PERF_FC_LIMIT].get_addr) {
|
||||
struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LIMIT];
|
||||
|
||||
*max_perf = ioread32(fci->get_addr);
|
||||
*min_perf = ioread32(fci->get_addr + 4);
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LIMITS_GET,
|
||||
domain, *min_perf, *max_perf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -505,9 +449,13 @@ static int scmi_perf_level_set(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_perf_info *pi = ph->get_priv(ph);
|
||||
struct perf_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->level_set_addr) {
|
||||
iowrite32(level, dom->fc_info->level_set_addr);
|
||||
scmi_perf_fc_ring_db(dom->fc_info->level_set_db);
|
||||
if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr) {
|
||||
struct scmi_fc_info *fci = &dom->fc_info[PERF_FC_LEVEL];
|
||||
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_SET,
|
||||
domain, level, 0);
|
||||
iowrite32(level, fci->set_addr);
|
||||
ph->hops->fastchannel_db_ring(fci->set_db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -542,8 +490,10 @@ static int scmi_perf_level_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_perf_info *pi = ph->get_priv(ph);
|
||||
struct perf_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->level_get_addr) {
|
||||
*level = ioread32(dom->fc_info->level_get_addr);
|
||||
if (dom->fc_info && dom->fc_info[PERF_FC_LEVEL].get_addr) {
|
||||
*level = ioread32(dom->fc_info[PERF_FC_LEVEL].get_addr);
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_PERF, PERF_LEVEL_GET,
|
||||
domain, *level, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -572,100 +522,33 @@ static int scmi_perf_level_limits_notify(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size)
|
||||
{
|
||||
if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4)
|
||||
return true;
|
||||
if ((msg == PERF_LIMITS_GET || msg == PERF_LIMITS_SET) && size == 8)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
scmi_perf_domain_desc_fc(const struct scmi_protocol_handle *ph, u32 domain,
|
||||
u32 message_id, void __iomem **p_addr,
|
||||
struct scmi_fc_db_info **p_db)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
u64 phys_addr;
|
||||
u8 size;
|
||||
void __iomem *addr;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_fc_db_info *db;
|
||||
struct scmi_perf_get_fc_info *info;
|
||||
struct scmi_msg_resp_perf_desc_fc *resp;
|
||||
|
||||
if (!p_addr)
|
||||
return;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, PERF_DESCRIBE_FASTCHANNEL,
|
||||
sizeof(*info), sizeof(*resp), &t);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
info = t->tx.buf;
|
||||
info->domain = cpu_to_le32(domain);
|
||||
info->message_id = cpu_to_le32(message_id);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (ret)
|
||||
goto err_xfer;
|
||||
|
||||
resp = t->rx.buf;
|
||||
flags = le32_to_cpu(resp->attr);
|
||||
size = le32_to_cpu(resp->chan_size);
|
||||
if (!scmi_perf_fc_size_is_valid(message_id, size))
|
||||
goto err_xfer;
|
||||
|
||||
phys_addr = le32_to_cpu(resp->chan_addr_low);
|
||||
phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
|
||||
addr = devm_ioremap(ph->dev, phys_addr, size);
|
||||
if (!addr)
|
||||
goto err_xfer;
|
||||
*p_addr = addr;
|
||||
|
||||
if (p_db && SUPPORTS_DOORBELL(flags)) {
|
||||
db = devm_kzalloc(ph->dev, sizeof(*db), GFP_KERNEL);
|
||||
if (!db)
|
||||
goto err_xfer;
|
||||
|
||||
size = 1 << DOORBELL_REG_WIDTH(flags);
|
||||
phys_addr = le32_to_cpu(resp->db_addr_low);
|
||||
phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32;
|
||||
addr = devm_ioremap(ph->dev, phys_addr, size);
|
||||
if (!addr)
|
||||
goto err_xfer;
|
||||
|
||||
db->addr = addr;
|
||||
db->width = size;
|
||||
db->set = le32_to_cpu(resp->db_set_lmask);
|
||||
db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32;
|
||||
db->mask = le32_to_cpu(resp->db_preserve_lmask);
|
||||
db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32;
|
||||
*p_db = db;
|
||||
}
|
||||
err_xfer:
|
||||
ph->xops->xfer_put(ph, t);
|
||||
}
|
||||
|
||||
static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct scmi_fc_info **p_fc)
|
||||
{
|
||||
struct scmi_fc_info *fc;
|
||||
|
||||
fc = devm_kzalloc(ph->dev, sizeof(*fc), GFP_KERNEL);
|
||||
fc = devm_kcalloc(ph->dev, PERF_FC_MAX, sizeof(*fc), GFP_KERNEL);
|
||||
if (!fc)
|
||||
return;
|
||||
|
||||
scmi_perf_domain_desc_fc(ph, domain, PERF_LEVEL_SET,
|
||||
&fc->level_set_addr, &fc->level_set_db);
|
||||
scmi_perf_domain_desc_fc(ph, domain, PERF_LEVEL_GET,
|
||||
&fc->level_get_addr, NULL);
|
||||
scmi_perf_domain_desc_fc(ph, domain, PERF_LIMITS_SET,
|
||||
&fc->limit_set_addr, &fc->limit_set_db);
|
||||
scmi_perf_domain_desc_fc(ph, domain, PERF_LIMITS_GET,
|
||||
&fc->limit_get_addr, NULL);
|
||||
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
|
||||
PERF_LEVEL_SET, 4, domain,
|
||||
&fc[PERF_FC_LEVEL].set_addr,
|
||||
&fc[PERF_FC_LEVEL].set_db);
|
||||
|
||||
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
|
||||
PERF_LEVEL_GET, 4, domain,
|
||||
&fc[PERF_FC_LEVEL].get_addr, NULL);
|
||||
|
||||
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
|
||||
PERF_LIMITS_SET, 8, domain,
|
||||
&fc[PERF_FC_LIMIT].set_addr,
|
||||
&fc[PERF_FC_LIMIT].set_db);
|
||||
|
||||
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
|
||||
PERF_LIMITS_GET, 8, domain,
|
||||
&fc[PERF_FC_LIMIT].get_addr, NULL);
|
||||
|
||||
*p_fc = fc;
|
||||
}
|
||||
|
||||
@ -789,7 +672,7 @@ static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph,
|
||||
|
||||
dom = pi->dom_info + scmi_dev_domain_id(dev);
|
||||
|
||||
return dom->fc_info && dom->fc_info->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)
|
||||
|
866
drivers/firmware/arm_scmi/powercap.c
Normal file
866
drivers/firmware/arm_scmi/powercap.c
Normal file
@ -0,0 +1,866 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Powercap Protocol
|
||||
*
|
||||
* Copyright (C) 2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include <trace/events/scmi.h>
|
||||
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_powercap_protocol_cmd {
|
||||
POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
|
||||
POWERCAP_CAP_GET = 0x4,
|
||||
POWERCAP_CAP_SET = 0x5,
|
||||
POWERCAP_PAI_GET = 0x6,
|
||||
POWERCAP_PAI_SET = 0x7,
|
||||
POWERCAP_DOMAIN_NAME_GET = 0x8,
|
||||
POWERCAP_MEASUREMENTS_GET = 0x9,
|
||||
POWERCAP_CAP_NOTIFY = 0xa,
|
||||
POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
|
||||
POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
|
||||
};
|
||||
|
||||
enum {
|
||||
POWERCAP_FC_CAP,
|
||||
POWERCAP_FC_PAI,
|
||||
POWERCAP_FC_MAX,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_powercap_domain_attributes {
|
||||
__le32 attributes;
|
||||
#define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31))
|
||||
#define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30))
|
||||
#define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28))
|
||||
#define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27))
|
||||
#define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26))
|
||||
#define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25))
|
||||
#define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22))
|
||||
#define POWERCAP_POWER_UNIT(x) \
|
||||
(FIELD_GET(GENMASK(24, 23), (x)))
|
||||
#define SUPPORTS_POWER_UNITS_MW(x) \
|
||||
(POWERCAP_POWER_UNIT(x) == 0x2)
|
||||
#define SUPPORTS_POWER_UNITS_UW(x) \
|
||||
(POWERCAP_POWER_UNIT(x) == 0x1)
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
__le32 min_pai;
|
||||
__le32 max_pai;
|
||||
__le32 pai_step;
|
||||
__le32 min_power_cap;
|
||||
__le32 max_power_cap;
|
||||
__le32 power_cap_step;
|
||||
__le32 sustainable_power;
|
||||
__le32 accuracy;
|
||||
__le32 parent_id;
|
||||
};
|
||||
|
||||
struct scmi_msg_powercap_set_cap_or_pai {
|
||||
__le32 domain;
|
||||
__le32 flags;
|
||||
#define CAP_SET_ASYNC BIT(1)
|
||||
#define CAP_SET_IGNORE_DRESP BIT(0)
|
||||
__le32 value;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_powercap_cap_set_complete {
|
||||
__le32 domain;
|
||||
__le32 power_cap;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_powercap_meas_get {
|
||||
__le32 power;
|
||||
__le32 pai;
|
||||
};
|
||||
|
||||
struct scmi_msg_powercap_notify_cap {
|
||||
__le32 domain;
|
||||
__le32 notify_enable;
|
||||
};
|
||||
|
||||
struct scmi_msg_powercap_notify_thresh {
|
||||
__le32 domain;
|
||||
__le32 notify_enable;
|
||||
__le32 power_thresh_low;
|
||||
__le32 power_thresh_high;
|
||||
};
|
||||
|
||||
struct scmi_powercap_cap_changed_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 domain_id;
|
||||
__le32 power_cap;
|
||||
__le32 pai;
|
||||
};
|
||||
|
||||
struct scmi_powercap_meas_changed_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 domain_id;
|
||||
__le32 power;
|
||||
};
|
||||
|
||||
struct scmi_powercap_state {
|
||||
bool meas_notif_enabled;
|
||||
u64 thresholds;
|
||||
#define THRESH_LOW(p, id) \
|
||||
(lower_32_bits((p)->states[(id)].thresholds))
|
||||
#define THRESH_HIGH(p, id) \
|
||||
(upper_32_bits((p)->states[(id)].thresholds))
|
||||
};
|
||||
|
||||
struct powercap_info {
|
||||
u32 version;
|
||||
int num_domains;
|
||||
struct scmi_powercap_state *states;
|
||||
struct scmi_powercap_info *powercaps;
|
||||
};
|
||||
|
||||
static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
|
||||
POWERCAP_CAP_NOTIFY,
|
||||
POWERCAP_MEASUREMENTS_NOTIFY,
|
||||
};
|
||||
|
||||
static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, int message_id, bool enable);
|
||||
|
||||
static int
|
||||
scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct powercap_info *pi)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
|
||||
sizeof(u32), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
u32 attributes;
|
||||
|
||||
attributes = get_unaligned_le32(t->rx.buf);
|
||||
pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
|
||||
unsigned int step_val, bool configurable)
|
||||
{
|
||||
if (!min_val || !max_val)
|
||||
return -EPROTO;
|
||||
|
||||
if ((configurable && min_val == max_val) ||
|
||||
(!configurable && min_val != max_val))
|
||||
return -EPROTO;
|
||||
|
||||
if (min_val != max_val && !step_val)
|
||||
return -EPROTO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct powercap_info *pinfo, u32 domain)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
|
||||
struct scmi_msg_resp_powercap_domain_attributes *resp;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
|
||||
sizeof(domain), sizeof(*resp), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(domain, t->tx.buf);
|
||||
resp = t->rx.buf;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
flags = le32_to_cpu(resp->attributes);
|
||||
|
||||
dom_info->id = domain;
|
||||
dom_info->notify_powercap_cap_change =
|
||||
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
|
||||
dom_info->notify_powercap_measurement_change =
|
||||
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
|
||||
dom_info->async_powercap_cap_set =
|
||||
SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
|
||||
dom_info->powercap_cap_config =
|
||||
SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
|
||||
dom_info->powercap_monitoring =
|
||||
SUPPORTS_POWERCAP_MONITORING(flags);
|
||||
dom_info->powercap_pai_config =
|
||||
SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
|
||||
dom_info->powercap_scale_mw =
|
||||
SUPPORTS_POWER_UNITS_MW(flags);
|
||||
dom_info->powercap_scale_uw =
|
||||
SUPPORTS_POWER_UNITS_UW(flags);
|
||||
dom_info->fastchannels =
|
||||
SUPPORTS_POWERCAP_FASTCHANNELS(flags);
|
||||
|
||||
strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
|
||||
|
||||
dom_info->min_pai = le32_to_cpu(resp->min_pai);
|
||||
dom_info->max_pai = le32_to_cpu(resp->max_pai);
|
||||
dom_info->pai_step = le32_to_cpu(resp->pai_step);
|
||||
ret = scmi_powercap_validate(dom_info->min_pai,
|
||||
dom_info->max_pai,
|
||||
dom_info->pai_step,
|
||||
dom_info->powercap_pai_config);
|
||||
if (ret) {
|
||||
dev_err(ph->dev,
|
||||
"Platform reported inconsistent PAI config for domain %d - %s\n",
|
||||
dom_info->id, dom_info->name);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
|
||||
dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
|
||||
dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
|
||||
ret = scmi_powercap_validate(dom_info->min_power_cap,
|
||||
dom_info->max_power_cap,
|
||||
dom_info->power_cap_step,
|
||||
dom_info->powercap_cap_config);
|
||||
if (ret) {
|
||||
dev_err(ph->dev,
|
||||
"Platform reported inconsistent CAP config for domain %d - %s\n",
|
||||
dom_info->id, dom_info->name);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
dom_info->sustainable_power =
|
||||
le32_to_cpu(resp->sustainable_power);
|
||||
dom_info->accuracy = le32_to_cpu(resp->accuracy);
|
||||
|
||||
dom_info->parent_id = le32_to_cpu(resp->parent_id);
|
||||
if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
|
||||
(dom_info->parent_id >= pinfo->num_domains ||
|
||||
dom_info->parent_id == dom_info->id)) {
|
||||
dev_err(ph->dev,
|
||||
"Platform reported inconsistent parent ID for domain %d - %s\n",
|
||||
dom_info->id, dom_info->name);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
clean:
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
/*
|
||||
* If supported overwrite short name with the extended one;
|
||||
* on error just carry on and use already provided short name.
|
||||
*/
|
||||
if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
|
||||
ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
|
||||
domain, dom_info->name,
|
||||
SCMI_MAX_STR_SIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
return pi->num_domains;
|
||||
}
|
||||
|
||||
static const struct scmi_powercap_info *
|
||||
scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
|
||||
{
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (domain_id >= pi->num_domains)
|
||||
return NULL;
|
||||
|
||||
return pi->powercaps + domain_id;
|
||||
}
|
||||
|
||||
static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 *power_cap)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
|
||||
sizeof(u32), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(domain_id, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret)
|
||||
*power_cap = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 *power_cap)
|
||||
{
|
||||
struct scmi_powercap_info *dom;
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (!power_cap || domain_id >= pi->num_domains)
|
||||
return -EINVAL;
|
||||
|
||||
dom = pi->powercaps + domain_id;
|
||||
if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
|
||||
*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
|
||||
domain_id, *power_cap, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_powercap_xfer_cap_get(ph, domain_id, power_cap);
|
||||
}
|
||||
|
||||
static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
|
||||
const struct scmi_powercap_info *pc,
|
||||
u32 power_cap, bool ignore_dresp)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_powercap_set_cap_or_pai *msg;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
|
||||
sizeof(*msg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg = t->tx.buf;
|
||||
msg->domain = cpu_to_le32(pc->id);
|
||||
msg->flags =
|
||||
cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
|
||||
FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
|
||||
msg->value = cpu_to_le32(power_cap);
|
||||
|
||||
if (!pc->async_powercap_cap_set || ignore_dresp) {
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
} else {
|
||||
ret = ph->xops->do_xfer_with_response(ph, t);
|
||||
if (!ret) {
|
||||
struct scmi_msg_resp_powercap_cap_set_complete *resp;
|
||||
|
||||
resp = t->rx.buf;
|
||||
if (le32_to_cpu(resp->domain) == pc->id)
|
||||
dev_dbg(ph->dev,
|
||||
"Powercap ID %d CAP set async to %u\n",
|
||||
pc->id,
|
||||
get_unaligned_le32(&resp->power_cap));
|
||||
else
|
||||
ret = -EPROTO;
|
||||
}
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 power_cap,
|
||||
bool ignore_dresp)
|
||||
{
|
||||
const struct scmi_powercap_info *pc;
|
||||
|
||||
pc = scmi_powercap_dom_info_get(ph, domain_id);
|
||||
if (!pc || !pc->powercap_cap_config || !power_cap ||
|
||||
power_cap < pc->min_power_cap ||
|
||||
power_cap > pc->max_power_cap)
|
||||
return -EINVAL;
|
||||
|
||||
if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
|
||||
struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
|
||||
|
||||
iowrite32(power_cap, fci->set_addr);
|
||||
ph->hops->fastchannel_db_ring(fci->set_db);
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
|
||||
domain_id, power_cap, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_powercap_xfer_cap_set(ph, pc, power_cap, ignore_dresp);
|
||||
}
|
||||
|
||||
static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 *pai)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
|
||||
sizeof(u32), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(domain_id, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret)
|
||||
*pai = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 *pai)
|
||||
{
|
||||
struct scmi_powercap_info *dom;
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (!pai || domain_id >= pi->num_domains)
|
||||
return -EINVAL;
|
||||
|
||||
dom = pi->powercaps + domain_id;
|
||||
if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
|
||||
*pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
|
||||
domain_id, *pai, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
|
||||
}
|
||||
|
||||
static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 pai)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_powercap_set_cap_or_pai *msg;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
|
||||
sizeof(*msg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msg = t->tx.buf;
|
||||
msg->domain = cpu_to_le32(domain_id);
|
||||
msg->flags = cpu_to_le32(0);
|
||||
msg->value = cpu_to_le32(pai);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 pai)
|
||||
{
|
||||
const struct scmi_powercap_info *pc;
|
||||
|
||||
pc = scmi_powercap_dom_info_get(ph, domain_id);
|
||||
if (!pc || !pc->powercap_pai_config || !pai ||
|
||||
pai < pc->min_pai || pai > pc->max_pai)
|
||||
return -EINVAL;
|
||||
|
||||
if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
|
||||
struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
|
||||
|
||||
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
|
||||
domain_id, pai, 0);
|
||||
iowrite32(pai, fci->set_addr);
|
||||
ph->hops->fastchannel_db_ring(fci->set_db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
|
||||
}
|
||||
|
||||
static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 *average_power,
|
||||
u32 *pai)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_powercap_meas_get *resp;
|
||||
const struct scmi_powercap_info *pc;
|
||||
|
||||
pc = scmi_powercap_dom_info_get(ph, domain_id);
|
||||
if (!pc || !pc->powercap_monitoring || !pai || !average_power)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
|
||||
sizeof(u32), sizeof(*resp), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
resp = t->rx.buf;
|
||||
put_unaligned_le32(domain_id, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
*average_power = le32_to_cpu(resp->power);
|
||||
*pai = le32_to_cpu(resp->pai);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 *power_thresh_low,
|
||||
u32 *power_thresh_high)
|
||||
{
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (!power_thresh_low || !power_thresh_high ||
|
||||
domain_id >= pi->num_domains)
|
||||
return -EINVAL;
|
||||
|
||||
*power_thresh_low = THRESH_LOW(pi, domain_id);
|
||||
*power_thresh_high = THRESH_HIGH(pi, domain_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 power_thresh_low,
|
||||
u32 power_thresh_high)
|
||||
{
|
||||
int ret = 0;
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (domain_id >= pi->num_domains ||
|
||||
power_thresh_low > power_thresh_high)
|
||||
return -EINVAL;
|
||||
|
||||
/* Anything to do ? */
|
||||
if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
|
||||
THRESH_HIGH(pi, domain_id) == power_thresh_high)
|
||||
return ret;
|
||||
|
||||
pi->states[domain_id].thresholds =
|
||||
(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
|
||||
FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
|
||||
|
||||
/* Update thresholds if notification already enabled */
|
||||
if (pi->states[domain_id].meas_notif_enabled)
|
||||
ret = scmi_powercap_notify(ph, domain_id,
|
||||
POWERCAP_MEASUREMENTS_NOTIFY,
|
||||
true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_powercap_proto_ops powercap_proto_ops = {
|
||||
.num_domains_get = scmi_powercap_num_domains_get,
|
||||
.info_get = scmi_powercap_dom_info_get,
|
||||
.cap_get = scmi_powercap_cap_get,
|
||||
.cap_set = scmi_powercap_cap_set,
|
||||
.pai_get = scmi_powercap_pai_get,
|
||||
.pai_set = scmi_powercap_pai_set,
|
||||
.measurements_get = scmi_powercap_measurements_get,
|
||||
.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
|
||||
.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
|
||||
};
|
||||
|
||||
static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct scmi_fc_info **p_fc)
|
||||
{
|
||||
struct scmi_fc_info *fc;
|
||||
|
||||
fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
|
||||
if (!fc)
|
||||
return;
|
||||
|
||||
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
|
||||
POWERCAP_CAP_SET, 4, domain,
|
||||
&fc[POWERCAP_FC_CAP].set_addr,
|
||||
&fc[POWERCAP_FC_CAP].set_db);
|
||||
|
||||
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
|
||||
POWERCAP_CAP_GET, 4, domain,
|
||||
&fc[POWERCAP_FC_CAP].get_addr, NULL);
|
||||
|
||||
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
|
||||
POWERCAP_PAI_SET, 4, domain,
|
||||
&fc[POWERCAP_FC_PAI].set_addr,
|
||||
&fc[POWERCAP_FC_PAI].set_db);
|
||||
|
||||
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
|
||||
POWERCAP_PAI_GET, 4, domain,
|
||||
&fc[POWERCAP_FC_PAI].get_addr, NULL);
|
||||
|
||||
*p_fc = fc;
|
||||
}
|
||||
|
||||
static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, int message_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
switch (message_id) {
|
||||
case POWERCAP_CAP_NOTIFY:
|
||||
{
|
||||
struct scmi_msg_powercap_notify_cap *notify;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, message_id,
|
||||
sizeof(*notify), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
notify = t->tx.buf;
|
||||
notify->domain = cpu_to_le32(domain);
|
||||
notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
|
||||
break;
|
||||
}
|
||||
case POWERCAP_MEASUREMENTS_NOTIFY:
|
||||
{
|
||||
u32 low, high;
|
||||
struct scmi_msg_powercap_notify_thresh *notify;
|
||||
|
||||
/*
|
||||
* Note that we have to pick the most recently configured
|
||||
* thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
|
||||
* enable request and we fail, complaining, if no thresholds
|
||||
* were ever set, since this is an indication the API has been
|
||||
* used wrongly.
|
||||
*/
|
||||
ret = scmi_powercap_measurements_threshold_get(ph, domain,
|
||||
&low, &high);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable && !low && !high) {
|
||||
dev_err(ph->dev,
|
||||
"Invalid Measurements Notify thresholds: %u/%u\n",
|
||||
low, high);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, message_id,
|
||||
sizeof(*notify), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
notify = t->tx.buf;
|
||||
notify->domain = cpu_to_le32(domain);
|
||||
notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
|
||||
notify->power_thresh_low = cpu_to_le32(low);
|
||||
notify->power_thresh_high = cpu_to_le32(high);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret, cmd_id;
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
|
||||
return -EINVAL;
|
||||
|
||||
cmd_id = evt_2_cmd[evt_id];
|
||||
ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
|
||||
if (ret)
|
||||
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
|
||||
evt_id, src_id, ret);
|
||||
else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
|
||||
/*
|
||||
* On success save the current notification enabled state, so
|
||||
* as to be able to properly update the notification thresholds
|
||||
* when they are modified on a domain for which measurement
|
||||
* notifications were currently enabled.
|
||||
*
|
||||
* This is needed because the SCMI Notification core machinery
|
||||
* and API does not support passing per-notification custom
|
||||
* arguments at callback registration time.
|
||||
*
|
||||
* Note that this can be done here with a simple flag since the
|
||||
* SCMI core Notifications code takes care of keeping proper
|
||||
* per-domain enables refcounting, so that this helper function
|
||||
* will be called only once (for enables) when the first user
|
||||
* registers a callback on this domain and once more (disable)
|
||||
* when the last user de-registers its callback.
|
||||
*/
|
||||
pi->states[src_id].meas_notif_enabled = enable;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *
|
||||
scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
void *rep = NULL;
|
||||
|
||||
switch (evt_id) {
|
||||
case SCMI_EVENT_POWERCAP_CAP_CHANGED:
|
||||
{
|
||||
const struct scmi_powercap_cap_changed_notify_payld *p = payld;
|
||||
struct scmi_powercap_cap_changed_report *r = report;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
break;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
r->power_cap = le32_to_cpu(p->power_cap);
|
||||
r->pai = le32_to_cpu(p->pai);
|
||||
*src_id = r->domain_id;
|
||||
rep = r;
|
||||
break;
|
||||
}
|
||||
case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
|
||||
{
|
||||
const struct scmi_powercap_meas_changed_notify_payld *p = payld;
|
||||
struct scmi_powercap_meas_changed_report *r = report;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
break;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
r->power = le32_to_cpu(p->power);
|
||||
*src_id = r->domain_id;
|
||||
rep = r;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (!pi)
|
||||
return -EINVAL;
|
||||
|
||||
return pi->num_domains;
|
||||
}
|
||||
|
||||
static const struct scmi_event powercap_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
|
||||
.max_payld_sz =
|
||||
sizeof(struct scmi_powercap_cap_changed_notify_payld),
|
||||
.max_report_sz =
|
||||
sizeof(struct scmi_powercap_cap_changed_report),
|
||||
},
|
||||
{
|
||||
.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
|
||||
.max_payld_sz =
|
||||
sizeof(struct scmi_powercap_meas_changed_notify_payld),
|
||||
.max_report_sz =
|
||||
sizeof(struct scmi_powercap_meas_changed_report),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops powercap_event_ops = {
|
||||
.get_num_sources = scmi_powercap_get_num_sources,
|
||||
.set_notify_enabled = scmi_powercap_set_notify_enabled,
|
||||
.fill_custom_report = scmi_powercap_fill_custom_report,
|
||||
};
|
||||
|
||||
static const struct scmi_protocol_events powercap_protocol_events = {
|
||||
.queue_sz = SCMI_PROTO_QUEUE_SZ,
|
||||
.ops = &powercap_event_ops,
|
||||
.evts = powercap_events,
|
||||
.num_events = ARRAY_SIZE(powercap_events),
|
||||
};
|
||||
|
||||
static int
|
||||
scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct powercap_info *pinfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Powercap Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scmi_powercap_attributes_get(ph, pinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
|
||||
sizeof(*pinfo->powercaps),
|
||||
GFP_KERNEL);
|
||||
if (!pinfo->powercaps)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Note that any failure in retrieving any domain attribute leads to
|
||||
* the whole Powercap protocol initialization failure: this way the
|
||||
* reported Powercap domains are all assured, when accessed, to be well
|
||||
* formed and correlated by sane parent-child relationship (if any).
|
||||
*/
|
||||
for (domain = 0; domain < pinfo->num_domains; domain++) {
|
||||
ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pinfo->powercaps[domain].fastchannels)
|
||||
scmi_powercap_domain_init_fc(ph, domain,
|
||||
&pinfo->powercaps[domain].fc_info);
|
||||
}
|
||||
|
||||
pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
|
||||
sizeof(*pinfo->states), GFP_KERNEL);
|
||||
if (!pinfo->states)
|
||||
return -ENOMEM;
|
||||
|
||||
pinfo->version = version;
|
||||
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_powercap = {
|
||||
.id = SCMI_PROTOCOL_POWERCAP,
|
||||
.owner = THIS_MODULE,
|
||||
.instance_init = &scmi_powercap_protocol_init,
|
||||
.ops = &powercap_proto_ops,
|
||||
.events = &powercap_protocol_events,
|
||||
};
|
||||
|
||||
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
|
@ -215,6 +215,19 @@ struct scmi_iterator_ops {
|
||||
struct scmi_iterator_state *st, void *priv);
|
||||
};
|
||||
|
||||
struct scmi_fc_db_info {
|
||||
int width;
|
||||
u64 set;
|
||||
u64 mask;
|
||||
void __iomem *addr;
|
||||
};
|
||||
|
||||
struct scmi_fc_info {
|
||||
void __iomem *set_addr;
|
||||
void __iomem *get_addr;
|
||||
struct scmi_fc_db_info *set_db;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_proto_helpers_ops - References to common protocol helpers
|
||||
* @extended_name_get: A common helper function to retrieve extended naming
|
||||
@ -230,6 +243,9 @@ struct scmi_iterator_ops {
|
||||
* provided in @ops.
|
||||
* @iter_response_run: A common helper to trigger the run of a previously
|
||||
* initialized iterator.
|
||||
* @fastchannel_init: A common helper used to initialize FC descriptors by
|
||||
* gathering FC descriptions from the SCMI platform server.
|
||||
* @fastchannel_db_ring: A common helper to ring a FC doorbell.
|
||||
*/
|
||||
struct scmi_proto_helpers_ops {
|
||||
int (*extended_name_get)(const struct scmi_protocol_handle *ph,
|
||||
@ -239,6 +255,12 @@ struct scmi_proto_helpers_ops {
|
||||
unsigned int max_resources, u8 msg_id,
|
||||
size_t tx_size, void *priv);
|
||||
int (*iter_response_run)(void *iter);
|
||||
void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
|
||||
u8 describe_id, u32 message_id,
|
||||
u32 valid_size, u32 domain,
|
||||
void __iomem **p_addr,
|
||||
struct scmi_fc_db_info **p_db);
|
||||
void (*fastchannel_db_ring)(struct scmi_fc_db_info *db);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -315,5 +337,6 @@ DECLARE_SCMI_REGISTER_UNREGISTER(reset);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(system);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(powercap);
|
||||
|
||||
#endif /* _SCMI_PROTOCOLS_H */
|
||||
|
362
drivers/firmware/arm_scmi/scmi_power_control.c
Normal file
362
drivers/firmware/arm_scmi/scmi_power_control.c
Normal file
@ -0,0 +1,362 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SCMI Generic SystemPower Control driver.
|
||||
*
|
||||
* Copyright (C) 2020-2022 ARM Ltd.
|
||||
*/
|
||||
/*
|
||||
* In order to handle platform originated SCMI SystemPower requests (like
|
||||
* shutdowns or cold/warm resets) we register an SCMI Notification notifier
|
||||
* block to react when such SCMI SystemPower events are emitted by platform.
|
||||
*
|
||||
* Once such a notification is received we act accordingly to perform the
|
||||
* required system transition depending on the kind of request.
|
||||
*
|
||||
* Graceful requests are routed to userspace through the same API methods
|
||||
* (orderly_poweroff/reboot()) used by ACPI when handling ACPI Shutdown bus
|
||||
* events.
|
||||
*
|
||||
* Direct forceful requests are not supported since are not meant to be sent
|
||||
* by the SCMI platform to an OSPM like Linux.
|
||||
*
|
||||
* Additionally, graceful request notifications can carry an optional timeout
|
||||
* field stating the maximum amount of time allowed by the platform for
|
||||
* completion after which they are converted to forceful ones: the assumption
|
||||
* here is that even graceful requests can be upper-bound by a maximum final
|
||||
* timeout strictly enforced by the platform itself which can ultimately cut
|
||||
* the power off at will anytime; in order to avoid such extreme scenario, we
|
||||
* track progress of graceful requests through the means of a reboot notifier
|
||||
* converting timed-out graceful requests to forceful ones, so at least we
|
||||
* try to perform a clean sync and shutdown/restart before the power is cut.
|
||||
*
|
||||
* Given the peculiar nature of SCMI SystemPower protocol, that is being in
|
||||
* charge of triggering system wide shutdown/reboot events, there should be
|
||||
* only one SCMI platform actively emitting SystemPower events.
|
||||
* For this reason the SCMI core takes care to enforce the creation of one
|
||||
* single unique device associated to the SCMI System Power protocol; no matter
|
||||
* how many SCMI platforms are defined on the system, only one can be designated
|
||||
* to support System Power: as a consequence this driver will never be probed
|
||||
* more than once.
|
||||
*
|
||||
* For similar reasons as soon as the first valid SystemPower is received by
|
||||
* this driver and the shutdown/reboot is started, any further notification
|
||||
* possibly emitted by the platform will be ignored.
|
||||
*/
|
||||
|
||||
#include <linux/math.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time64.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#ifndef MODULE
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
enum scmi_syspower_state {
|
||||
SCMI_SYSPOWER_IDLE,
|
||||
SCMI_SYSPOWER_IN_PROGRESS,
|
||||
SCMI_SYSPOWER_REBOOTING
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_syspower_conf - Common configuration
|
||||
*
|
||||
* @dev: A reference device
|
||||
* @state: Current SystemPower state
|
||||
* @state_mtx: @state related mutex
|
||||
* @required_transition: The requested transition as decribed in the received
|
||||
* SCMI SystemPower notification
|
||||
* @userspace_nb: The notifier_block registered against the SCMI SystemPower
|
||||
* notification to start the needed userspace interactions.
|
||||
* @reboot_nb: A notifier_block optionally used to track reboot progress
|
||||
* @forceful_work: A worker used to trigger a forceful transition once a
|
||||
* graceful has timed out.
|
||||
*/
|
||||
struct scmi_syspower_conf {
|
||||
struct device *dev;
|
||||
enum scmi_syspower_state state;
|
||||
/* Protect access to state */
|
||||
struct mutex state_mtx;
|
||||
enum scmi_system_events required_transition;
|
||||
|
||||
struct notifier_block userspace_nb;
|
||||
struct notifier_block reboot_nb;
|
||||
|
||||
struct delayed_work forceful_work;
|
||||
};
|
||||
|
||||
#define userspace_nb_to_sconf(x) \
|
||||
container_of(x, struct scmi_syspower_conf, userspace_nb)
|
||||
|
||||
#define reboot_nb_to_sconf(x) \
|
||||
container_of(x, struct scmi_syspower_conf, reboot_nb)
|
||||
|
||||
#define dwork_to_sconf(x) \
|
||||
container_of(x, struct scmi_syspower_conf, forceful_work)
|
||||
|
||||
/**
|
||||
* scmi_reboot_notifier - A reboot notifier to catch an ongoing successful
|
||||
* system transition
|
||||
* @nb: Reference to the related notifier block
|
||||
* @reason: The reason for the ongoing reboot
|
||||
* @__unused: The cmd being executed on a restart request (unused)
|
||||
*
|
||||
* When an ongoing system transition is detected, compatible with the one
|
||||
* requested by SCMI, cancel the delayed work.
|
||||
*
|
||||
* Return: NOTIFY_OK in any case
|
||||
*/
|
||||
static int scmi_reboot_notifier(struct notifier_block *nb,
|
||||
unsigned long reason, void *__unused)
|
||||
{
|
||||
struct scmi_syspower_conf *sc = reboot_nb_to_sconf(nb);
|
||||
|
||||
mutex_lock(&sc->state_mtx);
|
||||
switch (reason) {
|
||||
case SYS_HALT:
|
||||
case SYS_POWER_OFF:
|
||||
if (sc->required_transition == SCMI_SYSTEM_SHUTDOWN)
|
||||
sc->state = SCMI_SYSPOWER_REBOOTING;
|
||||
break;
|
||||
case SYS_RESTART:
|
||||
if (sc->required_transition == SCMI_SYSTEM_COLDRESET ||
|
||||
sc->required_transition == SCMI_SYSTEM_WARMRESET)
|
||||
sc->state = SCMI_SYSPOWER_REBOOTING;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (sc->state == SCMI_SYSPOWER_REBOOTING) {
|
||||
dev_dbg(sc->dev, "Reboot in progress...cancel delayed work.\n");
|
||||
cancel_delayed_work_sync(&sc->forceful_work);
|
||||
}
|
||||
mutex_unlock(&sc->state_mtx);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_request_forceful_transition - Request forceful SystemPower transition
|
||||
* @sc: A reference to the configuration data
|
||||
*
|
||||
* Initiates the required SystemPower transition without involving userspace:
|
||||
* just trigger the action at the kernel level after issuing an emergency
|
||||
* sync. (if possible at all)
|
||||
*/
|
||||
static inline void
|
||||
scmi_request_forceful_transition(struct scmi_syspower_conf *sc)
|
||||
{
|
||||
dev_dbg(sc->dev, "Serving forceful request:%d\n",
|
||||
sc->required_transition);
|
||||
|
||||
#ifndef MODULE
|
||||
emergency_sync();
|
||||
#endif
|
||||
switch (sc->required_transition) {
|
||||
case SCMI_SYSTEM_SHUTDOWN:
|
||||
kernel_power_off();
|
||||
break;
|
||||
case SCMI_SYSTEM_COLDRESET:
|
||||
case SCMI_SYSTEM_WARMRESET:
|
||||
kernel_restart(NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void scmi_forceful_work_func(struct work_struct *work)
|
||||
{
|
||||
struct scmi_syspower_conf *sc;
|
||||
struct delayed_work *dwork;
|
||||
|
||||
if (system_state > SYSTEM_RUNNING)
|
||||
return;
|
||||
|
||||
dwork = to_delayed_work(work);
|
||||
sc = dwork_to_sconf(dwork);
|
||||
|
||||
dev_dbg(sc->dev, "Graceful request timed out...forcing !\n");
|
||||
mutex_lock(&sc->state_mtx);
|
||||
/* avoid deadlock by unregistering reboot notifier first */
|
||||
unregister_reboot_notifier(&sc->reboot_nb);
|
||||
if (sc->state == SCMI_SYSPOWER_IN_PROGRESS)
|
||||
scmi_request_forceful_transition(sc);
|
||||
mutex_unlock(&sc->state_mtx);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_request_graceful_transition - Request graceful SystemPower transition
|
||||
* @sc: A reference to the configuration data
|
||||
* @timeout_ms: The desired timeout to wait for the shutdown to complete before
|
||||
* system is forcibly shutdown.
|
||||
*
|
||||
* Initiates the required SystemPower transition, requesting userspace
|
||||
* co-operation: it uses the same orderly_ methods used by ACPI Shutdown event
|
||||
* processing.
|
||||
*
|
||||
* Takes care also to register a reboot notifier and to schedule a delayed work
|
||||
* in order to detect if userspace actions are taking too long and in such a
|
||||
* case to trigger a forceful transition.
|
||||
*/
|
||||
static void scmi_request_graceful_transition(struct scmi_syspower_conf *sc,
|
||||
unsigned int timeout_ms)
|
||||
{
|
||||
unsigned int adj_timeout_ms = 0;
|
||||
|
||||
if (timeout_ms) {
|
||||
int ret;
|
||||
|
||||
sc->reboot_nb.notifier_call = &scmi_reboot_notifier;
|
||||
ret = register_reboot_notifier(&sc->reboot_nb);
|
||||
if (!ret) {
|
||||
/* Wait only up to 75% of the advertised timeout */
|
||||
adj_timeout_ms = mult_frac(timeout_ms, 3, 4);
|
||||
INIT_DELAYED_WORK(&sc->forceful_work,
|
||||
scmi_forceful_work_func);
|
||||
schedule_delayed_work(&sc->forceful_work,
|
||||
msecs_to_jiffies(adj_timeout_ms));
|
||||
} else {
|
||||
/* Carry on best effort even without a reboot notifier */
|
||||
dev_warn(sc->dev,
|
||||
"Cannot register reboot notifier !\n");
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(sc->dev,
|
||||
"Serving graceful req:%d (timeout_ms:%u adj_timeout_ms:%u)\n",
|
||||
sc->required_transition, timeout_ms, adj_timeout_ms);
|
||||
|
||||
switch (sc->required_transition) {
|
||||
case SCMI_SYSTEM_SHUTDOWN:
|
||||
/*
|
||||
* When triggered early at boot-time the 'orderly' call will
|
||||
* partially fail due to the lack of userspace itself, but
|
||||
* the force=true argument will start anyway a successful
|
||||
* forced shutdown.
|
||||
*/
|
||||
orderly_poweroff(true);
|
||||
break;
|
||||
case SCMI_SYSTEM_COLDRESET:
|
||||
case SCMI_SYSTEM_WARMRESET:
|
||||
orderly_reboot();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_userspace_notifier - Notifier callback to act on SystemPower
|
||||
* Notifications
|
||||
* @nb: Reference to the related notifier block
|
||||
* @event: The SystemPower notification event id
|
||||
* @data: The SystemPower event report
|
||||
*
|
||||
* This callback is in charge of decoding the received SystemPower report
|
||||
* and act accordingly triggering a graceful or forceful system transition.
|
||||
*
|
||||
* Note that once a valid SCMI SystemPower event starts being served, any
|
||||
* other following SystemPower notification received from the same SCMI
|
||||
* instance (handle) will be ignored.
|
||||
*
|
||||
* Return: NOTIFY_OK once a valid SystemPower event has been successfully
|
||||
* processed.
|
||||
*/
|
||||
static int scmi_userspace_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct scmi_system_power_state_notifier_report *er = data;
|
||||
struct scmi_syspower_conf *sc = userspace_nb_to_sconf(nb);
|
||||
|
||||
if (er->system_state >= SCMI_SYSTEM_POWERUP) {
|
||||
dev_err(sc->dev, "Ignoring unsupported system_state: 0x%X\n",
|
||||
er->system_state);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
if (!SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(er->flags)) {
|
||||
dev_err(sc->dev, "Ignoring forceful notification.\n");
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bail out if system is already shutting down or an SCMI SystemPower
|
||||
* requested is already being served.
|
||||
*/
|
||||
if (system_state > SYSTEM_RUNNING)
|
||||
return NOTIFY_DONE;
|
||||
mutex_lock(&sc->state_mtx);
|
||||
if (sc->state != SCMI_SYSPOWER_IDLE) {
|
||||
dev_dbg(sc->dev,
|
||||
"Transition already in progress...ignore.\n");
|
||||
mutex_unlock(&sc->state_mtx);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
sc->state = SCMI_SYSPOWER_IN_PROGRESS;
|
||||
mutex_unlock(&sc->state_mtx);
|
||||
|
||||
sc->required_transition = er->system_state;
|
||||
|
||||
/* Leaving a trace in logs of who triggered the shutdown/reboot. */
|
||||
dev_info(sc->dev, "Serving shutdown/reboot request: %d\n",
|
||||
sc->required_transition);
|
||||
|
||||
scmi_request_graceful_transition(sc, er->timeout);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int scmi_syspower_probe(struct scmi_device *sdev)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_syspower_conf *sc;
|
||||
struct scmi_handle *handle = sdev->handle;
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
ret = handle->devm_protocol_acquire(sdev, SCMI_PROTOCOL_SYSTEM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sc = devm_kzalloc(&sdev->dev, sizeof(*sc), GFP_KERNEL);
|
||||
if (!sc)
|
||||
return -ENOMEM;
|
||||
|
||||
sc->state = SCMI_SYSPOWER_IDLE;
|
||||
mutex_init(&sc->state_mtx);
|
||||
sc->required_transition = SCMI_SYSTEM_MAX;
|
||||
sc->userspace_nb.notifier_call = &scmi_userspace_notifier;
|
||||
sc->dev = &sdev->dev;
|
||||
|
||||
return handle->notify_ops->devm_event_notifier_register(sdev,
|
||||
SCMI_PROTOCOL_SYSTEM,
|
||||
SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
|
||||
NULL, &sc->userspace_nb);
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_SYSTEM, "syspower" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_system_power_driver = {
|
||||
.name = "scmi-system-power",
|
||||
.probe = scmi_syspower_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_system_power_driver);
|
||||
|
||||
MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
|
||||
MODULE_DESCRIPTION("ARM SCMI SystemPower Control driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -27,10 +27,12 @@ struct scmi_system_power_state_notifier_payld {
|
||||
__le32 agent_id;
|
||||
__le32 flags;
|
||||
__le32 system_state;
|
||||
__le32 timeout;
|
||||
};
|
||||
|
||||
struct scmi_system_info {
|
||||
u32 version;
|
||||
bool graceful_timeout_supported;
|
||||
};
|
||||
|
||||
static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
|
||||
@ -72,17 +74,27 @@ scmi_system_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
size_t expected_sz;
|
||||
const struct scmi_system_power_state_notifier_payld *p = payld;
|
||||
struct scmi_system_power_state_notifier_report *r = report;
|
||||
struct scmi_system_info *pinfo = ph->get_priv(ph);
|
||||
|
||||
expected_sz = pinfo->graceful_timeout_supported ?
|
||||
sizeof(*p) : sizeof(*p) - sizeof(__le32);
|
||||
if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER ||
|
||||
sizeof(*p) != payld_sz)
|
||||
payld_sz != expected_sz)
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->flags = le32_to_cpu(p->flags);
|
||||
r->system_state = le32_to_cpu(p->system_state);
|
||||
if (pinfo->graceful_timeout_supported &&
|
||||
r->system_state == SCMI_SYSTEM_SHUTDOWN &&
|
||||
SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(r->flags))
|
||||
r->timeout = le32_to_cpu(p->timeout);
|
||||
else
|
||||
r->timeout = 0x00;
|
||||
*src_id = 0;
|
||||
|
||||
return r;
|
||||
@ -129,6 +141,9 @@ static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
return -ENOMEM;
|
||||
|
||||
pinfo->version = version;
|
||||
if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
|
||||
pinfo->graceful_timeout_supported = true;
|
||||
|
||||
return ph->set_priv(ph, pinfo);
|
||||
}
|
||||
|
||||
|
@ -815,7 +815,7 @@ static int scpi_init_versions(struct scpi_drvinfo *info)
|
||||
info->firmware_version = le32_to_cpu(caps.platform_version);
|
||||
}
|
||||
/* Ignore error if not implemented */
|
||||
if (scpi_info->is_legacy && ret == -EOPNOTSUPP)
|
||||
if (info->is_legacy && ret == -EOPNOTSUPP)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
@ -913,13 +913,14 @@ static int scpi_probe(struct platform_device *pdev)
|
||||
struct resource res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct scpi_drvinfo *scpi_drvinfo;
|
||||
|
||||
scpi_info = devm_kzalloc(dev, sizeof(*scpi_info), GFP_KERNEL);
|
||||
if (!scpi_info)
|
||||
scpi_drvinfo = devm_kzalloc(dev, sizeof(*scpi_drvinfo), GFP_KERNEL);
|
||||
if (!scpi_drvinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_match_device(legacy_scpi_of_match, &pdev->dev))
|
||||
scpi_info->is_legacy = true;
|
||||
scpi_drvinfo->is_legacy = true;
|
||||
|
||||
count = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
|
||||
if (count < 0) {
|
||||
@ -927,19 +928,19 @@ static int scpi_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan),
|
||||
GFP_KERNEL);
|
||||
if (!scpi_info->channels)
|
||||
scpi_drvinfo->channels =
|
||||
devm_kcalloc(dev, count, sizeof(struct scpi_chan), GFP_KERNEL);
|
||||
if (!scpi_drvinfo->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_add_action(dev, scpi_free_channels, scpi_info);
|
||||
ret = devm_add_action(dev, scpi_free_channels, scpi_drvinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (; scpi_info->num_chans < count; scpi_info->num_chans++) {
|
||||
for (; scpi_drvinfo->num_chans < count; scpi_drvinfo->num_chans++) {
|
||||
resource_size_t size;
|
||||
int idx = scpi_info->num_chans;
|
||||
struct scpi_chan *pchan = scpi_info->channels + idx;
|
||||
int idx = scpi_drvinfo->num_chans;
|
||||
struct scpi_chan *pchan = scpi_drvinfo->channels + idx;
|
||||
struct mbox_client *cl = &pchan->cl;
|
||||
struct device_node *shmem = of_parse_phandle(np, "shmem", idx);
|
||||
|
||||
@ -986,45 +987,53 @@ static int scpi_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
scpi_info->commands = scpi_std_commands;
|
||||
scpi_drvinfo->commands = scpi_std_commands;
|
||||
|
||||
platform_set_drvdata(pdev, scpi_info);
|
||||
platform_set_drvdata(pdev, scpi_drvinfo);
|
||||
|
||||
if (scpi_info->is_legacy) {
|
||||
if (scpi_drvinfo->is_legacy) {
|
||||
/* Replace with legacy variants */
|
||||
scpi_ops.clk_set_val = legacy_scpi_clk_set_val;
|
||||
scpi_info->commands = scpi_legacy_commands;
|
||||
scpi_drvinfo->commands = scpi_legacy_commands;
|
||||
|
||||
/* Fill priority bitmap */
|
||||
for (idx = 0; idx < ARRAY_SIZE(legacy_hpriority_cmds); idx++)
|
||||
set_bit(legacy_hpriority_cmds[idx],
|
||||
scpi_info->cmd_priority);
|
||||
scpi_drvinfo->cmd_priority);
|
||||
}
|
||||
|
||||
ret = scpi_init_versions(scpi_info);
|
||||
scpi_info = scpi_drvinfo;
|
||||
|
||||
ret = scpi_init_versions(scpi_drvinfo);
|
||||
if (ret) {
|
||||
dev_err(dev, "incorrect or no SCP firmware found\n");
|
||||
scpi_info = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (scpi_info->is_legacy && !scpi_info->protocol_version &&
|
||||
!scpi_info->firmware_version)
|
||||
if (scpi_drvinfo->is_legacy && !scpi_drvinfo->protocol_version &&
|
||||
!scpi_drvinfo->firmware_version)
|
||||
dev_info(dev, "SCP Protocol legacy pre-1.0 firmware\n");
|
||||
else
|
||||
dev_info(dev, "SCP Protocol %lu.%lu Firmware %lu.%lu.%lu version\n",
|
||||
FIELD_GET(PROTO_REV_MAJOR_MASK,
|
||||
scpi_info->protocol_version),
|
||||
scpi_drvinfo->protocol_version),
|
||||
FIELD_GET(PROTO_REV_MINOR_MASK,
|
||||
scpi_info->protocol_version),
|
||||
scpi_drvinfo->protocol_version),
|
||||
FIELD_GET(FW_REV_MAJOR_MASK,
|
||||
scpi_info->firmware_version),
|
||||
scpi_drvinfo->firmware_version),
|
||||
FIELD_GET(FW_REV_MINOR_MASK,
|
||||
scpi_info->firmware_version),
|
||||
scpi_drvinfo->firmware_version),
|
||||
FIELD_GET(FW_REV_PATCH_MASK,
|
||||
scpi_info->firmware_version));
|
||||
scpi_info->scpi_ops = &scpi_ops;
|
||||
scpi_drvinfo->firmware_version));
|
||||
|
||||
return devm_of_platform_populate(dev);
|
||||
scpi_drvinfo->scpi_ops = &scpi_ops;
|
||||
|
||||
ret = devm_of_platform_populate(dev);
|
||||
if (ret)
|
||||
scpi_info = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id scpi_of_match[] = {
|
||||
|
@ -120,6 +120,9 @@ static void __scm_legacy_do(const struct arm_smccc_args *smc,
|
||||
/**
|
||||
* scm_legacy_call() - Sends a command to the SCM and waits for the command to
|
||||
* finish processing.
|
||||
* @dev: device
|
||||
* @desc: descriptor structure containing arguments and return values
|
||||
* @res: results from SMC call
|
||||
*
|
||||
* A note on cache maintenance:
|
||||
* Note that any buffers that are expected to be accessed by the secure world
|
||||
@ -211,6 +214,7 @@ out:
|
||||
/**
|
||||
* scm_legacy_call_atomic() - Send an atomic SCM command with up to 5 arguments
|
||||
* and 3 return values
|
||||
* @unused: device, legacy argument, not used, can be NULL
|
||||
* @desc: SCM call descriptor containing arguments
|
||||
* @res: SCM call return values
|
||||
*
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
@ -31,8 +32,13 @@ struct qcom_scm {
|
||||
struct clk *core_clk;
|
||||
struct clk *iface_clk;
|
||||
struct clk *bus_clk;
|
||||
struct icc_path *path;
|
||||
struct reset_controller_dev reset;
|
||||
|
||||
/* control access to the interconnect path */
|
||||
struct mutex scm_bw_lock;
|
||||
int scm_vote_count;
|
||||
|
||||
u64 dload_mode_addr;
|
||||
};
|
||||
|
||||
@ -99,6 +105,42 @@ static void qcom_scm_clk_disable(void)
|
||||
clk_disable_unprepare(__scm->bus_clk);
|
||||
}
|
||||
|
||||
static int qcom_scm_bw_enable(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!__scm->path)
|
||||
return 0;
|
||||
|
||||
if (IS_ERR(__scm->path))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&__scm->scm_bw_lock);
|
||||
if (!__scm->scm_vote_count) {
|
||||
ret = icc_set_bw(__scm->path, 0, UINT_MAX);
|
||||
if (ret < 0) {
|
||||
dev_err(__scm->dev, "failed to set bandwidth request\n");
|
||||
goto err_bw;
|
||||
}
|
||||
}
|
||||
__scm->scm_vote_count++;
|
||||
err_bw:
|
||||
mutex_unlock(&__scm->scm_bw_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_scm_bw_disable(void)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(__scm->path))
|
||||
return;
|
||||
|
||||
mutex_lock(&__scm->scm_bw_lock);
|
||||
if (__scm->scm_vote_count-- == 1)
|
||||
icc_set_bw(__scm->path, 0, 0);
|
||||
mutex_unlock(&__scm->scm_bw_lock);
|
||||
}
|
||||
|
||||
enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
|
||||
static DEFINE_SPINLOCK(scm_query_lock);
|
||||
|
||||
@ -444,10 +486,15 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = qcom_scm_bw_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
desc.args[1] = mdata_phys;
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
|
||||
qcom_scm_bw_disable();
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
out:
|
||||
@ -507,7 +554,12 @@ int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_bw_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
qcom_scm_bw_disable();
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret ? : res.result[0];
|
||||
@ -537,7 +589,12 @@ int qcom_scm_pas_auth_and_reset(u32 peripheral)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_bw_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
qcom_scm_bw_disable();
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret ? : res.result[0];
|
||||
@ -566,8 +623,13 @@ int qcom_scm_pas_shutdown(u32 peripheral)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_bw_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
|
||||
qcom_scm_bw_disable();
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret ? : res.result[0];
|
||||
@ -1277,8 +1339,15 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&scm->scm_bw_lock);
|
||||
|
||||
clks = (unsigned long)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
scm->path = devm_of_icc_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(scm->path))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(scm->path),
|
||||
"failed to acquire interconnect path\n");
|
||||
|
||||
scm->core_clk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(scm->core_clk)) {
|
||||
if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
|
||||
@ -1337,7 +1406,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
||||
|
||||
/*
|
||||
* If requested enable "download mode", from this point on warmboot
|
||||
* will cause the the boot stages to enter download mode, unless
|
||||
* will cause the boot stages to enter download mode, unless
|
||||
* disabled below by a clean shutdown/reboot.
|
||||
*/
|
||||
if (download_mode)
|
||||
|
@ -474,7 +474,7 @@ static int bpmp_populate_debugfs_inband(struct tegra_bpmp *bpmp,
|
||||
mode |= attrs & DEBUGFS_S_IWUSR ? 0200 : 0;
|
||||
dentry = debugfs_create_file(name, mode, parent, bpmp,
|
||||
&bpmp_debug_fops);
|
||||
if (!dentry) {
|
||||
if (IS_ERR(dentry)) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
@ -725,7 +725,7 @@ static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
|
||||
|
||||
if (t & DEBUGFS_S_ISDIR) {
|
||||
dentry = debugfs_create_dir(name, parent);
|
||||
if (!dentry)
|
||||
if (IS_ERR(dentry))
|
||||
return -ENOMEM;
|
||||
err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1);
|
||||
if (err < 0)
|
||||
@ -738,7 +738,7 @@ static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
|
||||
dentry = debugfs_create_file(name, mode,
|
||||
parent, bpmp,
|
||||
&debugfs_fops);
|
||||
if (!dentry)
|
||||
if (IS_ERR(dentry))
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
@ -788,11 +788,11 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
|
||||
return 0;
|
||||
|
||||
root = debugfs_create_dir("bpmp", NULL);
|
||||
if (!root)
|
||||
if (IS_ERR(root))
|
||||
return -ENOMEM;
|
||||
|
||||
bpmp->debugfs_mirror = debugfs_create_dir("debug", root);
|
||||
if (!bpmp->debugfs_mirror) {
|
||||
if (IS_ERR(bpmp->debugfs_mirror)) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
|
||||
int err;
|
||||
|
||||
if (data && size > 0)
|
||||
memcpy(data, channel->ib->data, size);
|
||||
memcpy_fromio(data, channel->ib->data, size);
|
||||
|
||||
err = tegra_bpmp_ack_response(channel);
|
||||
if (err < 0)
|
||||
@ -245,7 +245,7 @@ static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
|
||||
channel->ob->flags = flags;
|
||||
|
||||
if (data && size > 0)
|
||||
memcpy(channel->ob->data, data, size);
|
||||
memcpy_toio(channel->ob->data, data, size);
|
||||
|
||||
return tegra_bpmp_post_request(channel);
|
||||
}
|
||||
@ -420,7 +420,7 @@ void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code,
|
||||
channel->ob->code = code;
|
||||
|
||||
if (data && size > 0)
|
||||
memcpy(channel->ob->data, data, size);
|
||||
memcpy_toio(channel->ob->data, data, size);
|
||||
|
||||
err = tegra_bpmp_post_response(channel);
|
||||
if (WARN_ON(err < 0))
|
||||
|
@ -486,7 +486,7 @@ config I2C_BCM_KONA
|
||||
|
||||
config I2C_BRCMSTB
|
||||
tristate "BRCM Settop/DSL I2C controller"
|
||||
depends on ARCH_BCM2835 || ARCH_BCM4908 || ARCH_BCM_63XX || \
|
||||
depends on ARCH_BCM2835 || ARCH_BCM4908 || ARCH_BCMBCA || \
|
||||
ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
|
||||
default y
|
||||
help
|
||||
|
@ -21,11 +21,13 @@
|
||||
/* SMI COMMON */
|
||||
#define SMI_L1LEN 0x100
|
||||
|
||||
#define SMI_L1_ARB 0x200
|
||||
#define SMI_BUS_SEL 0x220
|
||||
#define SMI_BUS_LARB_SHIFT(larbid) ((larbid) << 1)
|
||||
/* All are MMU0 defaultly. Only specialize mmu1 here. */
|
||||
#define F_MMU1_LARB(larbid) (0x1 << SMI_BUS_LARB_SHIFT(larbid))
|
||||
|
||||
#define SMI_READ_FIFO_TH 0x230
|
||||
#define SMI_M4U_TH 0x234
|
||||
#define SMI_FIFO_TH1 0x238
|
||||
#define SMI_FIFO_TH2 0x23c
|
||||
@ -360,6 +362,7 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
||||
{.compatible = "mediatek,mt2701-smi-larb", .data = &mtk_smi_larb_mt2701},
|
||||
{.compatible = "mediatek,mt2712-smi-larb", .data = &mtk_smi_larb_mt2712},
|
||||
{.compatible = "mediatek,mt6779-smi-larb", .data = &mtk_smi_larb_mt6779},
|
||||
{.compatible = "mediatek,mt6795-smi-larb", .data = &mtk_smi_larb_mt8173},
|
||||
{.compatible = "mediatek,mt8167-smi-larb", .data = &mtk_smi_larb_mt8167},
|
||||
{.compatible = "mediatek,mt8173-smi-larb", .data = &mtk_smi_larb_mt8173},
|
||||
{.compatible = "mediatek,mt8183-smi-larb", .data = &mtk_smi_larb_mt8183},
|
||||
@ -544,6 +547,13 @@ static struct platform_driver mtk_smi_larb_driver = {
|
||||
}
|
||||
};
|
||||
|
||||
static const struct mtk_smi_reg_pair mtk_smi_common_mt6795_init[SMI_COMMON_INIT_REGS_NR] = {
|
||||
{SMI_L1_ARB, 0x1b},
|
||||
{SMI_M4U_TH, 0xce810c85},
|
||||
{SMI_FIFO_TH1, 0x43214c8},
|
||||
{SMI_READ_FIFO_TH, 0x191f},
|
||||
};
|
||||
|
||||
static const struct mtk_smi_reg_pair mtk_smi_common_mt8195_init[SMI_COMMON_INIT_REGS_NR] = {
|
||||
{SMI_L1LEN, 0xb},
|
||||
{SMI_M4U_TH, 0xe100e10},
|
||||
@ -568,6 +578,12 @@ static const struct mtk_smi_common_plat mtk_smi_common_mt6779 = {
|
||||
F_MMU1_LARB(5) | F_MMU1_LARB(6) | F_MMU1_LARB(7),
|
||||
};
|
||||
|
||||
static const struct mtk_smi_common_plat mtk_smi_common_mt6795 = {
|
||||
.type = MTK_SMI_GEN2,
|
||||
.bus_sel = F_MMU1_LARB(0),
|
||||
.init = mtk_smi_common_mt6795_init,
|
||||
};
|
||||
|
||||
static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
|
||||
.type = MTK_SMI_GEN2,
|
||||
.has_gals = true,
|
||||
@ -612,6 +628,7 @@ static const struct of_device_id mtk_smi_common_of_ids[] = {
|
||||
{.compatible = "mediatek,mt2701-smi-common", .data = &mtk_smi_common_gen1},
|
||||
{.compatible = "mediatek,mt2712-smi-common", .data = &mtk_smi_common_gen2},
|
||||
{.compatible = "mediatek,mt6779-smi-common", .data = &mtk_smi_common_mt6779},
|
||||
{.compatible = "mediatek,mt6795-smi-common", .data = &mtk_smi_common_mt6795},
|
||||
{.compatible = "mediatek,mt8167-smi-common", .data = &mtk_smi_common_gen2},
|
||||
{.compatible = "mediatek,mt8173-smi-common", .data = &mtk_smi_common_gen2},
|
||||
{.compatible = "mediatek,mt8183-smi-common", .data = &mtk_smi_common_mt8183},
|
||||
|
@ -11,6 +11,76 @@
|
||||
|
||||
static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
{
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBEARD,
|
||||
.name = "mgbeard",
|
||||
.sid = TEGRA234_SID_MGBE,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x2c0,
|
||||
.security = 0x2c4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBEBRD,
|
||||
.name = "mgbebrd",
|
||||
.sid = TEGRA234_SID_MGBE_VF1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x2c8,
|
||||
.security = 0x2cc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBECRD,
|
||||
.name = "mgbecrd",
|
||||
.sid = TEGRA234_SID_MGBE_VF2,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x2d0,
|
||||
.security = 0x2d4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBEDRD,
|
||||
.name = "mgbedrd",
|
||||
.sid = TEGRA234_SID_MGBE_VF3,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x2d8,
|
||||
.security = 0x2dc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBEAWR,
|
||||
.name = "mgbeawr",
|
||||
.sid = TEGRA234_SID_MGBE,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x2e0,
|
||||
.security = 0x2e4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBEBWR,
|
||||
.name = "mgbebwr",
|
||||
.sid = TEGRA234_SID_MGBE_VF1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x2f8,
|
||||
.security = 0x2fc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBECWR,
|
||||
.name = "mgbecwr",
|
||||
.sid = TEGRA234_SID_MGBE_VF2,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x308,
|
||||
.security = 0x30c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_SDMMCRAB,
|
||||
.name = "sdmmcrab",
|
||||
.sid = TEGRA234_SID_SDMMC4,
|
||||
@ -20,6 +90,16 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
.security = 0x31c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_MGBEDWR,
|
||||
.name = "mgbedwr",
|
||||
.sid = TEGRA234_SID_MGBE_VF3,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x328,
|
||||
.security = 0x32c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_SDMMCWAB,
|
||||
.name = "sdmmcwab",
|
||||
|
@ -25,9 +25,52 @@ static const struct mfd_cell bcm2835_power_devs[] = {
|
||||
{ .name = "bcm2835-power" },
|
||||
};
|
||||
|
||||
static int bcm2835_pm_get_pdata(struct platform_device *pdev,
|
||||
struct bcm2835_pm *pm)
|
||||
{
|
||||
if (of_find_property(pm->dev->of_node, "reg-names", NULL)) {
|
||||
struct resource *res;
|
||||
|
||||
pm->base = devm_platform_ioremap_resource_byname(pdev, "pm");
|
||||
if (IS_ERR(pm->base))
|
||||
return PTR_ERR(pm->base);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "asb");
|
||||
if (res) {
|
||||
pm->asb = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pm->asb))
|
||||
pm->asb = NULL;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"rpivid_asb");
|
||||
if (res) {
|
||||
pm->rpivid_asb = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pm->rpivid_asb))
|
||||
pm->rpivid_asb = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If no 'reg-names' property is found we can assume we're using old DTB. */
|
||||
pm->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(pm->base))
|
||||
return PTR_ERR(pm->base);
|
||||
|
||||
pm->asb = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(pm->asb))
|
||||
pm->asb = NULL;
|
||||
|
||||
pm->rpivid_asb = devm_platform_ioremap_resource(pdev, 2);
|
||||
if (IS_ERR(pm->rpivid_asb))
|
||||
pm->rpivid_asb = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm2835_pm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bcm2835_pm *pm;
|
||||
int ret;
|
||||
@ -39,10 +82,9 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
|
||||
|
||||
pm->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pm->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pm->base))
|
||||
return PTR_ERR(pm->base);
|
||||
ret = bcm2835_pm_get_pdata(pdev, pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_mfd_add_devices(dev, -1,
|
||||
bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs),
|
||||
@ -50,30 +92,22 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We'll use the presence of the AXI ASB regs in the
|
||||
/*
|
||||
* We'll use the presence of the AXI ASB regs in the
|
||||
* bcm2835-pm binding as the key for whether we can reference
|
||||
* the full PM register range and support power domains.
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
pm->asb = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pm->asb))
|
||||
return PTR_ERR(pm->asb);
|
||||
|
||||
ret = devm_mfd_add_devices(dev, -1,
|
||||
bcm2835_power_devs,
|
||||
ARRAY_SIZE(bcm2835_power_devs),
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pm->asb)
|
||||
return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
|
||||
ARRAY_SIZE(bcm2835_power_devs),
|
||||
NULL, 0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm2835_pm_of_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-pm-wdt", },
|
||||
{ .compatible = "brcm,bcm2835-pm", },
|
||||
{ .compatible = "brcm,bcm2711-pm", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
|
||||
|
@ -83,7 +83,7 @@ config PHY_NS2_USB_DRD
|
||||
config PHY_BRCM_SATA
|
||||
tristate "Broadcom SATA PHY driver"
|
||||
depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || \
|
||||
ARCH_BCM_63XX || COMPILE_TEST
|
||||
ARCH_BCMBCA || COMPILE_TEST
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
default ARCH_BCM_IPROC
|
||||
|
@ -9,6 +9,7 @@ source "drivers/soc/atmel/Kconfig"
|
||||
source "drivers/soc/bcm/Kconfig"
|
||||
source "drivers/soc/canaan/Kconfig"
|
||||
source "drivers/soc/fsl/Kconfig"
|
||||
source "drivers/soc/fujitsu/Kconfig"
|
||||
source "drivers/soc/imx/Kconfig"
|
||||
source "drivers/soc/ixp4xx/Kconfig"
|
||||
source "drivers/soc/litex/Kconfig"
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_SOC_CANAAN) += canaan/
|
||||
obj-$(CONFIG_ARCH_DOVE) += dove/
|
||||
obj-$(CONFIG_MACH_DOVE) += dove/
|
||||
obj-y += fsl/
|
||||
obj-y += fujitsu/
|
||||
obj-$(CONFIG_ARCH_GEMINI) += gemini/
|
||||
obj-y += imx/
|
||||
obj-y += ixp4xx/
|
||||
|
@ -126,6 +126,7 @@ static int __init meson_mx_socinfo_init(void)
|
||||
np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids);
|
||||
if (np) {
|
||||
analog_top_regmap = syscon_node_to_regmap(np);
|
||||
of_node_put(np);
|
||||
if (IS_ERR(analog_top_regmap))
|
||||
return PTR_ERR(analog_top_regmap);
|
||||
|
||||
|
@ -152,8 +152,10 @@ static int meson_secure_pwrc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
|
||||
if (!pwrc)
|
||||
if (!pwrc) {
|
||||
of_node_put(sm_np);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pwrc->fw = meson_sm_get(sm_np);
|
||||
of_node_put(sm_np);
|
||||
|
@ -126,8 +126,7 @@
|
||||
|
||||
#define ASB_AXI_BRDG_ID 0x20
|
||||
|
||||
#define ASB_READ(reg) readl(power->asb + (reg))
|
||||
#define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg))
|
||||
#define BCM2835_BRDG_ID 0x62726467
|
||||
|
||||
struct bcm2835_power_domain {
|
||||
struct generic_pm_domain base;
|
||||
@ -142,24 +141,41 @@ struct bcm2835_power {
|
||||
void __iomem *base;
|
||||
/* AXI Async bridge registers. */
|
||||
void __iomem *asb;
|
||||
/* RPiVid bridge registers. */
|
||||
void __iomem *rpivid_asb;
|
||||
|
||||
struct genpd_onecell_data pd_xlate;
|
||||
struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT];
|
||||
struct reset_controller_dev reset;
|
||||
};
|
||||
|
||||
static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
|
||||
static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable)
|
||||
{
|
||||
void __iomem *base = power->asb;
|
||||
u64 start;
|
||||
u32 val;
|
||||
|
||||
if (!reg)
|
||||
switch (reg) {
|
||||
case 0:
|
||||
return 0;
|
||||
case ASB_V3D_S_CTRL:
|
||||
case ASB_V3D_M_CTRL:
|
||||
if (power->rpivid_asb)
|
||||
base = power->rpivid_asb;
|
||||
break;
|
||||
}
|
||||
|
||||
start = ktime_get_ns();
|
||||
|
||||
/* Enable the module's async AXI bridges. */
|
||||
ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP);
|
||||
while (ASB_READ(reg) & ASB_ACK) {
|
||||
if (enable) {
|
||||
val = readl(base + reg) & ~ASB_REQ_STOP;
|
||||
} else {
|
||||
val = readl(base + reg) | ASB_REQ_STOP;
|
||||
}
|
||||
writel(PM_PASSWORD | val, base + reg);
|
||||
|
||||
while (readl(base + reg) & ASB_ACK) {
|
||||
cpu_relax();
|
||||
if (ktime_get_ns() - start >= 1000)
|
||||
return -ETIMEDOUT;
|
||||
@ -168,30 +184,24 @@ static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg)
|
||||
{
|
||||
return bcm2835_asb_control(power, reg, true);
|
||||
}
|
||||
|
||||
static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg)
|
||||
{
|
||||
u64 start;
|
||||
|
||||
if (!reg)
|
||||
return 0;
|
||||
|
||||
start = ktime_get_ns();
|
||||
|
||||
/* Enable the module's async AXI bridges. */
|
||||
ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP);
|
||||
while (!(ASB_READ(reg) & ASB_ACK)) {
|
||||
cpu_relax();
|
||||
if (ktime_get_ns() - start >= 1000)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return bcm2835_asb_control(power, reg, false);
|
||||
}
|
||||
|
||||
static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg)
|
||||
{
|
||||
struct bcm2835_power *power = pd->power;
|
||||
|
||||
/* We don't run this on BCM2711 */
|
||||
if (power->rpivid_asb)
|
||||
return 0;
|
||||
|
||||
/* Enable functional isolation */
|
||||
PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC);
|
||||
|
||||
@ -213,6 +223,10 @@ static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg)
|
||||
int inrush;
|
||||
bool powok;
|
||||
|
||||
/* We don't run this on BCM2711 */
|
||||
if (power->rpivid_asb)
|
||||
return 0;
|
||||
|
||||
/* If it was already powered on by the fw, leave it that way. */
|
||||
if (PM_READ(pm_reg) & PM_POWUP)
|
||||
return 0;
|
||||
@ -626,13 +640,23 @@ static int bcm2835_power_probe(struct platform_device *pdev)
|
||||
power->dev = dev;
|
||||
power->base = pm->base;
|
||||
power->asb = pm->asb;
|
||||
power->rpivid_asb = pm->rpivid_asb;
|
||||
|
||||
id = ASB_READ(ASB_AXI_BRDG_ID);
|
||||
if (id != 0x62726467 /* "BRDG" */) {
|
||||
id = readl(power->asb + ASB_AXI_BRDG_ID);
|
||||
if (id != BCM2835_BRDG_ID /* "BRDG" */) {
|
||||
dev_err(dev, "ASB register ID returned 0x%08x\n", id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (power->rpivid_asb) {
|
||||
id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID);
|
||||
if (id != BCM2835_BRDG_ID /* "BRDG" */) {
|
||||
dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n",
|
||||
id);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
power->pd_xlate.domains = devm_kcalloc(dev,
|
||||
ARRAY_SIZE(power_domain_names),
|
||||
sizeof(*power->pd_xlate.domains),
|
||||
|
@ -340,12 +340,12 @@ static int __init brcmstb_biuctrl_init(void)
|
||||
|
||||
ret = setup_hifcpubiuctrl_regs(np);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out_put;
|
||||
|
||||
ret = mcp_write_pairing_set();
|
||||
if (ret) {
|
||||
pr_err("MCP: Unable to disable write pairing!\n");
|
||||
return ret;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
a72_b53_rac_enable_all(np);
|
||||
@ -353,6 +353,9 @@ static int __init brcmstb_biuctrl_init(void)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
register_syscore_ops(&brcmstb_cpu_credit_syscore_ops);
|
||||
#endif
|
||||
return 0;
|
||||
ret = 0;
|
||||
out_put:
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
early_initcall(brcmstb_biuctrl_init);
|
||||
|
@ -721,7 +721,7 @@ static int brcmstb_pm_probe(struct platform_device *pdev)
|
||||
ctrl.phy_a_standby_ctrl_offs = ddr_phy_data->phy_a_standby_ctrl_offs;
|
||||
ctrl.phy_b_standby_ctrl_offs = ddr_phy_data->phy_b_standby_ctrl_offs;
|
||||
/*
|
||||
* Slightly grosss to use the phy ver to get a memc,
|
||||
* Slightly gross to use the phy ver to get a memc,
|
||||
* offset but that is the only versioned things so far
|
||||
* we can test for.
|
||||
*/
|
||||
|
@ -14,21 +14,16 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fsl/guts.h>
|
||||
|
||||
struct guts {
|
||||
struct ccsr_guts __iomem *regs;
|
||||
bool little_endian;
|
||||
};
|
||||
|
||||
struct fsl_soc_die_attr {
|
||||
char *die;
|
||||
u32 svr;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
static struct guts *guts;
|
||||
static struct soc_device_attribute soc_dev_attr;
|
||||
static struct soc_device *soc_dev;
|
||||
|
||||
struct fsl_soc_data {
|
||||
const char *sfp_compat;
|
||||
u32 uid_offset;
|
||||
};
|
||||
|
||||
/* SoC die attribute definition for QorIQ platform */
|
||||
static const struct fsl_soc_die_attr fsl_soc_die[] = {
|
||||
@ -120,88 +115,36 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u32 fsl_guts_get_svr(void)
|
||||
static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset)
|
||||
{
|
||||
u32 svr = 0;
|
||||
struct device_node *np;
|
||||
void __iomem *sfp_base;
|
||||
u64 uid;
|
||||
|
||||
if (!guts || !guts->regs)
|
||||
return svr;
|
||||
np = of_find_compatible_node(NULL, NULL, compat);
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
if (guts->little_endian)
|
||||
svr = ioread32(&guts->regs->svr);
|
||||
else
|
||||
svr = ioread32be(&guts->regs->svr);
|
||||
|
||||
return svr;
|
||||
}
|
||||
|
||||
static int fsl_guts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *root, *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct fsl_soc_die_attr *soc_die;
|
||||
const char *machine;
|
||||
u32 svr;
|
||||
|
||||
/* Initialize guts */
|
||||
guts = devm_kzalloc(dev, sizeof(*guts), GFP_KERNEL);
|
||||
if (!guts)
|
||||
return -ENOMEM;
|
||||
|
||||
guts->little_endian = of_property_read_bool(np, "little-endian");
|
||||
|
||||
guts->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(guts->regs))
|
||||
return PTR_ERR(guts->regs);
|
||||
|
||||
/* Register soc device */
|
||||
root = of_find_node_by_path("/");
|
||||
if (of_property_read_string(root, "model", &machine))
|
||||
of_property_read_string_index(root, "compatible", 0, &machine);
|
||||
if (machine) {
|
||||
soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL);
|
||||
if (!soc_dev_attr.machine) {
|
||||
of_node_put(root);
|
||||
return -ENOMEM;
|
||||
}
|
||||
sfp_base = of_iomap(np, 0);
|
||||
if (!sfp_base) {
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
}
|
||||
of_node_put(root);
|
||||
|
||||
svr = fsl_guts_get_svr();
|
||||
soc_die = fsl_soc_die_match(svr, fsl_soc_die);
|
||||
if (soc_die) {
|
||||
soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"QorIQ %s", soc_die->die);
|
||||
} else {
|
||||
soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "QorIQ");
|
||||
}
|
||||
if (!soc_dev_attr.family)
|
||||
return -ENOMEM;
|
||||
soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"svr:0x%08x", svr);
|
||||
if (!soc_dev_attr.soc_id)
|
||||
return -ENOMEM;
|
||||
soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d",
|
||||
(svr >> 4) & 0xf, svr & 0xf);
|
||||
if (!soc_dev_attr.revision)
|
||||
return -ENOMEM;
|
||||
uid = ioread32(sfp_base + offset);
|
||||
uid <<= 32;
|
||||
uid |= ioread32(sfp_base + offset + 4);
|
||||
|
||||
soc_dev = soc_device_register(&soc_dev_attr);
|
||||
if (IS_ERR(soc_dev))
|
||||
return PTR_ERR(soc_dev);
|
||||
iounmap(sfp_base);
|
||||
of_node_put(np);
|
||||
|
||||
pr_info("Machine: %s\n", soc_dev_attr.machine);
|
||||
pr_info("SoC family: %s\n", soc_dev_attr.family);
|
||||
pr_info("SoC ID: %s, Revision: %s\n",
|
||||
soc_dev_attr.soc_id, soc_dev_attr.revision);
|
||||
return 0;
|
||||
return uid;
|
||||
}
|
||||
|
||||
static int fsl_guts_remove(struct platform_device *dev)
|
||||
{
|
||||
soc_device_unregister(soc_dev);
|
||||
return 0;
|
||||
}
|
||||
static const struct fsl_soc_data ls1028a_data = {
|
||||
.sfp_compat = "fsl,ls1028a-sfp",
|
||||
.uid_offset = 0x21c,
|
||||
};
|
||||
|
||||
/*
|
||||
* Table for matching compatible strings, for device tree
|
||||
@ -231,28 +174,106 @@ static const struct of_device_id fsl_guts_of_match[] = {
|
||||
{ .compatible = "fsl,ls1012a-dcfg", },
|
||||
{ .compatible = "fsl,ls1046a-dcfg", },
|
||||
{ .compatible = "fsl,lx2160a-dcfg", },
|
||||
{ .compatible = "fsl,ls1028a-dcfg", },
|
||||
{ .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_guts_of_match);
|
||||
|
||||
static struct platform_driver fsl_guts_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-guts",
|
||||
.of_match_table = fsl_guts_of_match,
|
||||
},
|
||||
.probe = fsl_guts_probe,
|
||||
.remove = fsl_guts_remove,
|
||||
};
|
||||
|
||||
static int __init fsl_guts_init(void)
|
||||
{
|
||||
return platform_driver_register(&fsl_guts_driver);
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
static struct soc_device *soc_dev;
|
||||
const struct fsl_soc_die_attr *soc_die;
|
||||
const struct fsl_soc_data *soc_data;
|
||||
const struct of_device_id *match;
|
||||
struct ccsr_guts __iomem *regs;
|
||||
const char *machine = NULL;
|
||||
struct device_node *np;
|
||||
bool little_endian;
|
||||
u64 soc_uid = 0;
|
||||
u32 svr;
|
||||
int ret;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match);
|
||||
if (!np)
|
||||
return 0;
|
||||
soc_data = match->data;
|
||||
|
||||
regs = of_iomap(np, 0);
|
||||
if (!regs) {
|
||||
of_node_put(np);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
little_endian = of_property_read_bool(np, "little-endian");
|
||||
if (little_endian)
|
||||
svr = ioread32(®s->svr);
|
||||
else
|
||||
svr = ioread32be(®s->svr);
|
||||
iounmap(regs);
|
||||
of_node_put(np);
|
||||
|
||||
/* Register soc device */
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_string(of_root, "model", &machine))
|
||||
of_property_read_string_index(of_root, "compatible", 0, &machine);
|
||||
if (machine) {
|
||||
soc_dev_attr->machine = kstrdup(machine, GFP_KERNEL);
|
||||
if (!soc_dev_attr->machine)
|
||||
goto err_nomem;
|
||||
}
|
||||
|
||||
soc_die = fsl_soc_die_match(svr, fsl_soc_die);
|
||||
if (soc_die) {
|
||||
soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ %s",
|
||||
soc_die->die);
|
||||
} else {
|
||||
soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ");
|
||||
}
|
||||
if (!soc_dev_attr->family)
|
||||
goto err_nomem;
|
||||
|
||||
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", svr);
|
||||
if (!soc_dev_attr->soc_id)
|
||||
goto err_nomem;
|
||||
|
||||
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d",
|
||||
(svr >> 4) & 0xf, svr & 0xf);
|
||||
if (!soc_dev_attr->revision)
|
||||
goto err_nomem;
|
||||
|
||||
if (soc_data)
|
||||
soc_uid = fsl_guts_get_soc_uid(soc_data->sfp_compat,
|
||||
soc_data->uid_offset);
|
||||
if (soc_uid)
|
||||
soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX",
|
||||
soc_uid);
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
ret = PTR_ERR(soc_dev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pr_info("Machine: %s\n", soc_dev_attr->machine);
|
||||
pr_info("SoC family: %s\n", soc_dev_attr->family);
|
||||
pr_info("SoC ID: %s, Revision: %s\n",
|
||||
soc_dev_attr->soc_id, soc_dev_attr->revision);
|
||||
|
||||
return 0;
|
||||
|
||||
err_nomem:
|
||||
ret = -ENOMEM;
|
||||
err:
|
||||
kfree(soc_dev_attr->machine);
|
||||
kfree(soc_dev_attr->family);
|
||||
kfree(soc_dev_attr->soc_id);
|
||||
kfree(soc_dev_attr->revision);
|
||||
kfree(soc_dev_attr->serial_number);
|
||||
kfree(soc_dev_attr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
core_initcall(fsl_guts_init);
|
||||
|
||||
static void __exit fsl_guts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&fsl_guts_driver);
|
||||
}
|
||||
module_exit(fsl_guts_exit);
|
||||
|
16
drivers/soc/fujitsu/Kconfig
Normal file
16
drivers/soc/fujitsu/Kconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menu "fujitsu SoC drivers"
|
||||
|
||||
config A64FX_DIAG
|
||||
bool "A64FX diag driver"
|
||||
depends on ARM64
|
||||
depends on ACPI
|
||||
help
|
||||
Say Y here if you want to enable diag interrupt on Fujitsu A64FX.
|
||||
This driver enables BMC's diagnostic requests and enables
|
||||
A64FX-specific interrupts. This allows administrators to obtain
|
||||
kernel dumps via diagnostic requests using ipmitool, etc.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endmenu
|
3
drivers/soc/fujitsu/Makefile
Normal file
3
drivers/soc/fujitsu/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_A64FX_DIAG) += a64fx-diag.o
|
154
drivers/soc/fujitsu/a64fx-diag.c
Normal file
154
drivers/soc/fujitsu/a64fx-diag.c
Normal file
@ -0,0 +1,154 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* A64FX diag driver.
|
||||
* Copyright (c) 2022 Fujitsu Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define A64FX_DIAG_IRQ 1
|
||||
#define BMC_DIAG_INTERRUPT_ENABLE 0x40
|
||||
#define BMC_DIAG_INTERRUPT_STATUS 0x44
|
||||
#define BMC_DIAG_INTERRUPT_MASK BIT(31)
|
||||
|
||||
struct a64fx_diag_priv {
|
||||
void __iomem *mmsc_reg_base;
|
||||
int irq;
|
||||
bool has_nmi;
|
||||
};
|
||||
|
||||
static irqreturn_t a64fx_diag_handler_nmi(int irq, void *dev_id)
|
||||
{
|
||||
nmi_panic(NULL, "a64fx_diag: interrupt received\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t a64fx_diag_handler_irq(int irq, void *dev_id)
|
||||
{
|
||||
panic("a64fx_diag: interrupt received\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void a64fx_diag_interrupt_clear(struct a64fx_diag_priv *priv)
|
||||
{
|
||||
void __iomem *diag_status_reg_addr;
|
||||
u32 mmsc;
|
||||
|
||||
diag_status_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_STATUS;
|
||||
mmsc = readl(diag_status_reg_addr);
|
||||
if (mmsc & BMC_DIAG_INTERRUPT_MASK)
|
||||
writel(BMC_DIAG_INTERRUPT_MASK, diag_status_reg_addr);
|
||||
}
|
||||
|
||||
static void a64fx_diag_interrupt_enable(struct a64fx_diag_priv *priv)
|
||||
{
|
||||
void __iomem *diag_enable_reg_addr;
|
||||
u32 mmsc;
|
||||
|
||||
diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE;
|
||||
mmsc = readl(diag_enable_reg_addr);
|
||||
if (!(mmsc & BMC_DIAG_INTERRUPT_MASK)) {
|
||||
mmsc |= BMC_DIAG_INTERRUPT_MASK;
|
||||
writel(mmsc, diag_enable_reg_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void a64fx_diag_interrupt_disable(struct a64fx_diag_priv *priv)
|
||||
{
|
||||
void __iomem *diag_enable_reg_addr;
|
||||
u32 mmsc;
|
||||
|
||||
diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE;
|
||||
mmsc = readl(diag_enable_reg_addr);
|
||||
if (mmsc & BMC_DIAG_INTERRUPT_MASK) {
|
||||
mmsc &= ~BMC_DIAG_INTERRUPT_MASK;
|
||||
writel(mmsc, diag_enable_reg_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int a64fx_diag_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct a64fx_diag_priv *priv;
|
||||
unsigned long irq_flags;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->mmsc_reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->mmsc_reg_base))
|
||||
return PTR_ERR(priv->mmsc_reg_base);
|
||||
|
||||
priv->irq = platform_get_irq(pdev, A64FX_DIAG_IRQ);
|
||||
if (priv->irq < 0)
|
||||
return priv->irq;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN |
|
||||
IRQF_NO_THREAD;
|
||||
ret = request_nmi(priv->irq, &a64fx_diag_handler_nmi, irq_flags,
|
||||
"a64fx_diag_nmi", NULL);
|
||||
if (ret) {
|
||||
ret = request_irq(priv->irq, &a64fx_diag_handler_irq,
|
||||
irq_flags, "a64fx_diag_irq", NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot register IRQ %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
enable_irq(priv->irq);
|
||||
} else {
|
||||
enable_nmi(priv->irq);
|
||||
priv->has_nmi = true;
|
||||
}
|
||||
|
||||
a64fx_diag_interrupt_clear(priv);
|
||||
a64fx_diag_interrupt_enable(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a64fx_diag_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct a64fx_diag_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
a64fx_diag_interrupt_disable(priv);
|
||||
a64fx_diag_interrupt_clear(priv);
|
||||
|
||||
if (priv->has_nmi)
|
||||
free_nmi(priv->irq, NULL);
|
||||
else
|
||||
free_irq(priv->irq, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id a64fx_diag_acpi_match[] = {
|
||||
{ "FUJI2007", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match);
|
||||
|
||||
|
||||
static struct platform_driver a64fx_diag_driver = {
|
||||
.driver = {
|
||||
.name = "a64fx_diag_driver",
|
||||
.acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match),
|
||||
},
|
||||
.probe = a64fx_diag_probe,
|
||||
.remove = a64fx_diag_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(a64fx_diag_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>");
|
||||
MODULE_DESCRIPTION("A64FX diag driver");
|
@ -328,7 +328,9 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
|
||||
if (!IS_ERR(domain->regulator)) {
|
||||
ret = regulator_enable(domain->regulator);
|
||||
if (ret) {
|
||||
dev_err(domain->dev, "failed to enable regulator\n");
|
||||
dev_err(domain->dev,
|
||||
"failed to enable regulator: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
goto out_put_pm;
|
||||
}
|
||||
}
|
||||
@ -467,7 +469,9 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
|
||||
if (!IS_ERR(domain->regulator)) {
|
||||
ret = regulator_disable(domain->regulator);
|
||||
if (ret) {
|
||||
dev_err(domain->dev, "failed to disable regulator\n");
|
||||
dev_err(domain->dev,
|
||||
"failed to disable regulator: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
|
||||
bc->bus_power_dev = genpd_dev_pm_attach_by_name(dev, "bus");
|
||||
if (IS_ERR(bc->bus_power_dev))
|
||||
return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
|
||||
"failed to attach power domain\n");
|
||||
"failed to attach power domain \"bus\"\n");
|
||||
|
||||
for (i = 0; i < bc_data->num_domains; i++) {
|
||||
const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i];
|
||||
@ -238,7 +238,8 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
|
||||
dev_pm_domain_attach_by_name(dev, data->gpc_name);
|
||||
if (IS_ERR(domain->power_dev)) {
|
||||
dev_err_probe(dev, PTR_ERR(domain->power_dev),
|
||||
"failed to attach power domain\n");
|
||||
"failed to attach power domain \"%s\"\n",
|
||||
data->gpc_name);
|
||||
ret = PTR_ERR(domain->power_dev);
|
||||
goto cleanup_pds;
|
||||
}
|
||||
@ -251,7 +252,9 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
|
||||
|
||||
ret = pm_genpd_init(&domain->genpd, NULL, true);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "failed to init power domain\n");
|
||||
dev_err_probe(dev, ret,
|
||||
"failed to init power domain \"%s\"\n",
|
||||
data->gpc_name);
|
||||
dev_pm_domain_detach(domain->power_dev, true);
|
||||
goto cleanup_pds;
|
||||
}
|
||||
|
@ -73,4 +73,14 @@ config MTK_MMSYS
|
||||
Say yes here to add support for the MediaTek Multimedia
|
||||
Subsystem (MMSYS).
|
||||
|
||||
config MTK_SVS
|
||||
tristate "MediaTek Smart Voltage Scaling(SVS)"
|
||||
depends on MTK_EFUSE && NVMEM
|
||||
help
|
||||
The Smart Voltage Scaling(SVS) engine is a piece of hardware
|
||||
which has several controllers(banks) for calculating suitable
|
||||
voltage to different power domains(CPU/GPU/CCI) according to
|
||||
chip process corner, temperatures and other factors. Then DVFS
|
||||
driver could apply SVS bank voltage to PMIC/Buck.
|
||||
|
||||
endmenu
|
||||
|
@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
|
||||
obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o
|
||||
obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
|
||||
obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
|
||||
obj-$(CONFIG_MTK_SVS) += mtk-svs.o
|
||||
|
112
drivers/soc/mediatek/mt6795-pm-domains.h
Normal file
112
drivers/soc/mediatek/mt6795-pm-domains.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __SOC_MEDIATEK_MT6795_PM_DOMAINS_H
|
||||
#define __SOC_MEDIATEK_MT6795_PM_DOMAINS_H
|
||||
|
||||
#include "mtk-pm-domains.h"
|
||||
#include <dt-bindings/power/mt6795-power.h>
|
||||
|
||||
/*
|
||||
* MT6795 power domain support
|
||||
*/
|
||||
|
||||
static const struct scpsys_domain_data scpsys_domain_data_mt6795[] = {
|
||||
[MT6795_POWER_DOMAIN_VDEC] = {
|
||||
.name = "vdec",
|
||||
.sta_mask = PWR_STATUS_VDEC,
|
||||
.ctl_offs = SPM_VDE_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(12, 12),
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_VENC] = {
|
||||
.name = "venc",
|
||||
.sta_mask = PWR_STATUS_VENC,
|
||||
.ctl_offs = SPM_VEN_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(15, 12),
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_ISP] = {
|
||||
.name = "isp",
|
||||
.sta_mask = PWR_STATUS_ISP,
|
||||
.ctl_offs = SPM_ISP_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(13, 12),
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_MM] = {
|
||||
.name = "mm",
|
||||
.sta_mask = PWR_STATUS_DISP,
|
||||
.ctl_offs = SPM_DIS_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(12, 12),
|
||||
.bp_infracfg = {
|
||||
BUS_PROT_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MM_M0 |
|
||||
MT8173_TOP_AXI_PROT_EN_MM_M1),
|
||||
},
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_MJC] = {
|
||||
.name = "mjc",
|
||||
.sta_mask = BIT(20),
|
||||
.ctl_offs = 0x298,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(15, 12),
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_AUDIO] = {
|
||||
.name = "audio",
|
||||
.sta_mask = PWR_STATUS_AUDIO,
|
||||
.ctl_offs = SPM_AUDIO_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(15, 12),
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_MFG_ASYNC] = {
|
||||
.name = "mfg_async",
|
||||
.sta_mask = PWR_STATUS_MFG_ASYNC,
|
||||
.ctl_offs = SPM_MFG_ASYNC_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = 0,
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_MFG_2D] = {
|
||||
.name = "mfg_2d",
|
||||
.sta_mask = PWR_STATUS_MFG_2D,
|
||||
.ctl_offs = SPM_MFG_2D_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(11, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(13, 12),
|
||||
},
|
||||
[MT6795_POWER_DOMAIN_MFG] = {
|
||||
.name = "mfg",
|
||||
.sta_mask = PWR_STATUS_MFG,
|
||||
.ctl_offs = SPM_MFG_PWR_CON,
|
||||
.pwr_sta_offs = SPM_PWR_STATUS,
|
||||
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
|
||||
.sram_pdn_bits = GENMASK(13, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(21, 16),
|
||||
.bp_infracfg = {
|
||||
BUS_PROT_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MFG_S |
|
||||
MT8173_TOP_AXI_PROT_EN_MFG_M0 |
|
||||
MT8173_TOP_AXI_PROT_EN_MFG_M1 |
|
||||
MT8173_TOP_AXI_PROT_EN_MFG_SNOOP_OUT),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scpsys_soc_data mt6795_scpsys_data = {
|
||||
.domains_data = scpsys_domain_data_mt6795,
|
||||
.num_domains = ARRAY_SIZE(scpsys_domain_data_mt6795),
|
||||
};
|
||||
|
||||
#endif /* __SOC_MEDIATEK_MT6795_PM_DOMAINS_H */
|
@ -41,6 +41,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = {
|
||||
.pwr_sta2nd_offs = 0x0184,
|
||||
.sram_pdn_bits = 0,
|
||||
.sram_pdn_ack_bits = 0,
|
||||
.caps = MTK_SCPD_DOMAIN_SUPPLY,
|
||||
},
|
||||
[MT8183_POWER_DOMAIN_MFG] = {
|
||||
.name = "mfg",
|
||||
|
@ -51,7 +51,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8186[] = {
|
||||
MT8186_TOP_AXI_PROT_EN_1_CLR,
|
||||
MT8186_TOP_AXI_PROT_EN_1_STA),
|
||||
},
|
||||
.caps = MTK_SCPD_KEEP_DEFAULT_OFF,
|
||||
.caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY,
|
||||
},
|
||||
[MT8186_POWER_DOMAIN_MFG2] = {
|
||||
.name = "mfg2",
|
||||
|
@ -58,6 +58,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = {
|
||||
.pwr_sta2nd_offs = 0x0170,
|
||||
.sram_pdn_bits = GENMASK(8, 8),
|
||||
.sram_pdn_ack_bits = GENMASK(12, 12),
|
||||
.caps = MTK_SCPD_DOMAIN_SUPPLY,
|
||||
},
|
||||
[MT8192_POWER_DOMAIN_MFG1] = {
|
||||
.name = "mfg1",
|
||||
@ -85,6 +86,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = {
|
||||
MT8192_TOP_AXI_PROT_EN_2_CLR,
|
||||
MT8192_TOP_AXI_PROT_EN_2_STA1),
|
||||
},
|
||||
.caps = MTK_SCPD_DOMAIN_SUPPLY,
|
||||
},
|
||||
[MT8192_POWER_DOMAIN_MFG2] = {
|
||||
.name = "mfg2",
|
||||
|
@ -67,7 +67,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = {
|
||||
.ctl_offs = 0x334,
|
||||
.pwr_sta_offs = 0x174,
|
||||
.pwr_sta2nd_offs = 0x178,
|
||||
.caps = MTK_SCPD_ACTIVE_WAKEUP,
|
||||
.caps = MTK_SCPD_ACTIVE_WAKEUP | MTK_SCPD_ALWAYS_ON,
|
||||
},
|
||||
[MT8195_POWER_DOMAIN_CSI_RX_TOP] = {
|
||||
.name = "csi_rx_top",
|
||||
@ -162,7 +162,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = {
|
||||
MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR,
|
||||
MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1),
|
||||
},
|
||||
.caps = MTK_SCPD_KEEP_DEFAULT_OFF,
|
||||
.caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY,
|
||||
},
|
||||
[MT8195_POWER_DOMAIN_MFG2] = {
|
||||
.name = "mfg2",
|
||||
|
@ -10,6 +10,9 @@
|
||||
#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN 0xf60
|
||||
#define MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0xf64
|
||||
#define MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN 0xf68
|
||||
#define MT8365_DISP_REG_CONFIG_DISP_RDMA1_SOUT_SEL 0xfd0
|
||||
#define MT8365_DISP_REG_CONFIG_DISP_DPI0_SEL_IN 0xfd8
|
||||
#define MT8365_DISP_REG_CONFIG_DISP_LVDS_SYS_CFG_00 0xfdc
|
||||
|
||||
#define MT8365_RDMA0_SOUT_COLOR0 0x1
|
||||
#define MT8365_DITHER_MOUT_EN_DSI0 0x1
|
||||
@ -18,6 +21,10 @@
|
||||
#define MT8365_RDMA0_RSZ0_SEL_IN_RDMA0 0x0
|
||||
#define MT8365_DISP_COLOR_SEL_IN_COLOR0 0x0
|
||||
#define MT8365_OVL0_MOUT_PATH0_SEL BIT(0)
|
||||
#define MT8365_RDMA1_SOUT_DPI0 0x1
|
||||
#define MT8365_DPI0_SEL_IN_RDMA1 0x0
|
||||
#define MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK 0x1
|
||||
#define MT8365_DPI0_SEL_IN_RDMA1 0x0
|
||||
|
||||
static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = {
|
||||
{
|
||||
@ -55,6 +62,21 @@ static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = {
|
||||
MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN,
|
||||
MT8365_RDMA0_RSZ0_SEL_IN_RDMA0, MT8365_RDMA0_RSZ0_SEL_IN_RDMA0
|
||||
},
|
||||
{
|
||||
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
|
||||
MT8365_DISP_REG_CONFIG_DISP_LVDS_SYS_CFG_00,
|
||||
MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK, MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK
|
||||
},
|
||||
{
|
||||
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
|
||||
MT8365_DISP_REG_CONFIG_DISP_DPI0_SEL_IN,
|
||||
MT8365_DPI0_SEL_IN_RDMA1, MT8365_DPI0_SEL_IN_RDMA1
|
||||
},
|
||||
{
|
||||
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
|
||||
MT8365_DISP_REG_CONFIG_DISP_RDMA1_SOUT_SEL,
|
||||
MT8365_RDMA1_SOUT_DPI0, MT8365_RDMA1_SOUT_DPI0
|
||||
},
|
||||
};
|
||||
|
||||
#endif /* __SOC_MEDIATEK_MT8365_MMSYS_H */
|
||||
|
@ -31,10 +31,7 @@ struct mtk_devapc_vio_dbgs {
|
||||
u32 vio_dbg1;
|
||||
};
|
||||
|
||||
struct mtk_devapc_data {
|
||||
/* numbers of violation index */
|
||||
u32 vio_idx_num;
|
||||
|
||||
struct mtk_devapc_regs_ofs {
|
||||
/* reg offset */
|
||||
u32 vio_mask_offset;
|
||||
u32 vio_sta_offset;
|
||||
@ -46,6 +43,12 @@ struct mtk_devapc_data {
|
||||
u32 vio_shift_con_offset;
|
||||
};
|
||||
|
||||
struct mtk_devapc_data {
|
||||
/* numbers of violation index */
|
||||
u32 vio_idx_num;
|
||||
const struct mtk_devapc_regs_ofs *regs_ofs;
|
||||
};
|
||||
|
||||
struct mtk_devapc_context {
|
||||
struct device *dev;
|
||||
void __iomem *infra_base;
|
||||
@ -58,7 +61,7 @@ static void clear_vio_status(struct mtk_devapc_context *ctx)
|
||||
void __iomem *reg;
|
||||
int i;
|
||||
|
||||
reg = ctx->infra_base + ctx->data->vio_sta_offset;
|
||||
reg = ctx->infra_base + ctx->data->regs_ofs->vio_sta_offset;
|
||||
|
||||
for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
|
||||
writel(GENMASK(31, 0), reg + 4 * i);
|
||||
@ -73,7 +76,7 @@ static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask)
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
reg = ctx->infra_base + ctx->data->vio_mask_offset;
|
||||
reg = ctx->infra_base + ctx->data->regs_ofs->vio_mask_offset;
|
||||
|
||||
if (mask)
|
||||
val = GENMASK(31, 0);
|
||||
@ -116,11 +119,11 @@ static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
|
||||
u32 val;
|
||||
|
||||
pd_vio_shift_sta_reg = ctx->infra_base +
|
||||
ctx->data->vio_shift_sta_offset;
|
||||
ctx->data->regs_ofs->vio_shift_sta_offset;
|
||||
pd_vio_shift_sel_reg = ctx->infra_base +
|
||||
ctx->data->vio_shift_sel_offset;
|
||||
ctx->data->regs_ofs->vio_shift_sel_offset;
|
||||
pd_vio_shift_con_reg = ctx->infra_base +
|
||||
ctx->data->vio_shift_con_offset;
|
||||
ctx->data->regs_ofs->vio_shift_con_offset;
|
||||
|
||||
/* Find the minimum shift group which has violation */
|
||||
val = readl(pd_vio_shift_sta_reg);
|
||||
@ -161,8 +164,8 @@ static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
|
||||
void __iomem *vio_dbg0_reg;
|
||||
void __iomem *vio_dbg1_reg;
|
||||
|
||||
vio_dbg0_reg = ctx->infra_base + ctx->data->vio_dbg0_offset;
|
||||
vio_dbg1_reg = ctx->infra_base + ctx->data->vio_dbg1_offset;
|
||||
vio_dbg0_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg0_offset;
|
||||
vio_dbg1_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg1_offset;
|
||||
|
||||
vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg);
|
||||
vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg);
|
||||
@ -200,7 +203,7 @@ static irqreturn_t devapc_violation_irq(int irq_number, void *data)
|
||||
*/
|
||||
static void start_devapc(struct mtk_devapc_context *ctx)
|
||||
{
|
||||
writel(BIT(31), ctx->infra_base + ctx->data->apc_con_offset);
|
||||
writel(BIT(31), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
|
||||
|
||||
mask_module_irq(ctx, false);
|
||||
}
|
||||
@ -212,11 +215,10 @@ static void stop_devapc(struct mtk_devapc_context *ctx)
|
||||
{
|
||||
mask_module_irq(ctx, true);
|
||||
|
||||
writel(BIT(2), ctx->infra_base + ctx->data->apc_con_offset);
|
||||
writel(BIT(2), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
|
||||
}
|
||||
|
||||
static const struct mtk_devapc_data devapc_mt6779 = {
|
||||
.vio_idx_num = 511,
|
||||
static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt6779 = {
|
||||
.vio_mask_offset = 0x0,
|
||||
.vio_sta_offset = 0x400,
|
||||
.vio_dbg0_offset = 0x900,
|
||||
@ -227,10 +229,23 @@ static const struct mtk_devapc_data devapc_mt6779 = {
|
||||
.vio_shift_con_offset = 0xF20,
|
||||
};
|
||||
|
||||
static const struct mtk_devapc_data devapc_mt6779 = {
|
||||
.vio_idx_num = 511,
|
||||
.regs_ofs = &devapc_regs_ofs_mt6779,
|
||||
};
|
||||
|
||||
static const struct mtk_devapc_data devapc_mt8186 = {
|
||||
.vio_idx_num = 519,
|
||||
.regs_ofs = &devapc_regs_ofs_mt6779,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_devapc_dt_match[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt6779-devapc",
|
||||
.data = &devapc_mt6779,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8186-devapc",
|
||||
.data = &devapc_mt8186,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
|
@ -7,10 +7,12 @@
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/soc/mediatek/mtk-mmsys.h>
|
||||
#include <linux/soc/mediatek/mtk-mutex.h>
|
||||
#include <linux/soc/mediatek/mtk-cmdq.h>
|
||||
|
||||
#define MT2701_MUTEX0_MOD0 0x2c
|
||||
#define MT2701_MUTEX0_SOF0 0x30
|
||||
@ -80,6 +82,15 @@
|
||||
#define MT8183_MUTEX_MOD_DISP_GAMMA0 16
|
||||
#define MT8183_MUTEX_MOD_DISP_DITHER0 17
|
||||
|
||||
#define MT8183_MUTEX_MOD_MDP_RDMA0 2
|
||||
#define MT8183_MUTEX_MOD_MDP_RSZ0 4
|
||||
#define MT8183_MUTEX_MOD_MDP_RSZ1 5
|
||||
#define MT8183_MUTEX_MOD_MDP_TDSHP0 6
|
||||
#define MT8183_MUTEX_MOD_MDP_WROT0 7
|
||||
#define MT8183_MUTEX_MOD_MDP_WDMA 8
|
||||
#define MT8183_MUTEX_MOD_MDP_AAL0 23
|
||||
#define MT8183_MUTEX_MOD_MDP_CCORR0 24
|
||||
|
||||
#define MT8173_MUTEX_MOD_DISP_OVL0 11
|
||||
#define MT8173_MUTEX_MOD_DISP_OVL1 12
|
||||
#define MT8173_MUTEX_MOD_DISP_RDMA0 13
|
||||
@ -110,6 +121,20 @@
|
||||
#define MT8195_MUTEX_MOD_DISP_DP_INTF0 21
|
||||
#define MT8195_MUTEX_MOD_DISP_PWM0 27
|
||||
|
||||
#define MT8365_MUTEX_MOD_DISP_OVL0 7
|
||||
#define MT8365_MUTEX_MOD_DISP_OVL0_2L 8
|
||||
#define MT8365_MUTEX_MOD_DISP_RDMA0 9
|
||||
#define MT8365_MUTEX_MOD_DISP_RDMA1 10
|
||||
#define MT8365_MUTEX_MOD_DISP_WDMA0 11
|
||||
#define MT8365_MUTEX_MOD_DISP_COLOR0 12
|
||||
#define MT8365_MUTEX_MOD_DISP_CCORR 13
|
||||
#define MT8365_MUTEX_MOD_DISP_AAL 14
|
||||
#define MT8365_MUTEX_MOD_DISP_GAMMA 15
|
||||
#define MT8365_MUTEX_MOD_DISP_DITHER 16
|
||||
#define MT8365_MUTEX_MOD_DISP_DSI0 17
|
||||
#define MT8365_MUTEX_MOD_DISP_PWM0 20
|
||||
#define MT8365_MUTEX_MOD_DISP_DPI0 22
|
||||
|
||||
#define MT2712_MUTEX_MOD_DISP_PWM2 10
|
||||
#define MT2712_MUTEX_MOD_DISP_OVL0 11
|
||||
#define MT2712_MUTEX_MOD_DISP_OVL1 12
|
||||
@ -185,6 +210,7 @@ struct mtk_mutex_data {
|
||||
const unsigned int *mutex_sof;
|
||||
const unsigned int mutex_mod_reg;
|
||||
const unsigned int mutex_sof_reg;
|
||||
const unsigned int *mutex_table_mod;
|
||||
const bool no_clk;
|
||||
};
|
||||
|
||||
@ -194,6 +220,8 @@ struct mtk_mutex_ctx {
|
||||
void __iomem *regs;
|
||||
struct mtk_mutex mutex[10];
|
||||
const struct mtk_mutex_data *data;
|
||||
phys_addr_t addr;
|
||||
struct cmdq_client_reg cmdq_reg;
|
||||
};
|
||||
|
||||
static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = {
|
||||
@ -272,6 +300,17 @@ static const unsigned int mt8183_mutex_mod[DDP_COMPONENT_ID_MAX] = {
|
||||
[DDP_COMPONENT_WDMA0] = MT8183_MUTEX_MOD_DISP_WDMA0,
|
||||
};
|
||||
|
||||
static const unsigned int mt8183_mutex_table_mod[MUTEX_MOD_IDX_MAX] = {
|
||||
[MUTEX_MOD_IDX_MDP_RDMA0] = MT8183_MUTEX_MOD_MDP_RDMA0,
|
||||
[MUTEX_MOD_IDX_MDP_RSZ0] = MT8183_MUTEX_MOD_MDP_RSZ0,
|
||||
[MUTEX_MOD_IDX_MDP_RSZ1] = MT8183_MUTEX_MOD_MDP_RSZ1,
|
||||
[MUTEX_MOD_IDX_MDP_TDSHP0] = MT8183_MUTEX_MOD_MDP_TDSHP0,
|
||||
[MUTEX_MOD_IDX_MDP_WROT0] = MT8183_MUTEX_MOD_MDP_WROT0,
|
||||
[MUTEX_MOD_IDX_MDP_WDMA] = MT8183_MUTEX_MOD_MDP_WDMA,
|
||||
[MUTEX_MOD_IDX_MDP_AAL0] = MT8183_MUTEX_MOD_MDP_AAL0,
|
||||
[MUTEX_MOD_IDX_MDP_CCORR0] = MT8183_MUTEX_MOD_MDP_CCORR0,
|
||||
};
|
||||
|
||||
static const unsigned int mt8186_mutex_mod[DDP_COMPONENT_ID_MAX] = {
|
||||
[DDP_COMPONENT_AAL0] = MT8186_MUTEX_MOD_DISP_AAL0,
|
||||
[DDP_COMPONENT_CCORR] = MT8186_MUTEX_MOD_DISP_CCORR0,
|
||||
@ -315,6 +354,22 @@ static const unsigned int mt8195_mutex_mod[DDP_COMPONENT_ID_MAX] = {
|
||||
[DDP_COMPONENT_DP_INTF0] = MT8195_MUTEX_MOD_DISP_DP_INTF0,
|
||||
};
|
||||
|
||||
static const unsigned int mt8365_mutex_mod[DDP_COMPONENT_ID_MAX] = {
|
||||
[DDP_COMPONENT_AAL0] = MT8365_MUTEX_MOD_DISP_AAL,
|
||||
[DDP_COMPONENT_CCORR] = MT8365_MUTEX_MOD_DISP_CCORR,
|
||||
[DDP_COMPONENT_COLOR0] = MT8365_MUTEX_MOD_DISP_COLOR0,
|
||||
[DDP_COMPONENT_DITHER0] = MT8365_MUTEX_MOD_DISP_DITHER,
|
||||
[DDP_COMPONENT_DPI0] = MT8365_MUTEX_MOD_DISP_DPI0,
|
||||
[DDP_COMPONENT_DSI0] = MT8365_MUTEX_MOD_DISP_DSI0,
|
||||
[DDP_COMPONENT_GAMMA] = MT8365_MUTEX_MOD_DISP_GAMMA,
|
||||
[DDP_COMPONENT_OVL0] = MT8365_MUTEX_MOD_DISP_OVL0,
|
||||
[DDP_COMPONENT_OVL_2L0] = MT8365_MUTEX_MOD_DISP_OVL0_2L,
|
||||
[DDP_COMPONENT_PWM0] = MT8365_MUTEX_MOD_DISP_PWM0,
|
||||
[DDP_COMPONENT_RDMA0] = MT8365_MUTEX_MOD_DISP_RDMA0,
|
||||
[DDP_COMPONENT_RDMA1] = MT8365_MUTEX_MOD_DISP_RDMA1,
|
||||
[DDP_COMPONENT_WDMA0] = MT8365_MUTEX_MOD_DISP_WDMA0,
|
||||
};
|
||||
|
||||
static const unsigned int mt2712_mutex_sof[DDP_MUTEX_SOF_MAX] = {
|
||||
[MUTEX_SOF_SINGLE_MODE] = MUTEX_SOF_SINGLE_MODE,
|
||||
[MUTEX_SOF_DSI0] = MUTEX_SOF_DSI0,
|
||||
@ -399,6 +454,7 @@ static const struct mtk_mutex_data mt8183_mutex_driver_data = {
|
||||
.mutex_sof = mt8183_mutex_sof,
|
||||
.mutex_mod_reg = MT8183_MUTEX0_MOD0,
|
||||
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
|
||||
.mutex_table_mod = mt8183_mutex_table_mod,
|
||||
.no_clk = true,
|
||||
};
|
||||
|
||||
@ -423,6 +479,14 @@ static const struct mtk_mutex_data mt8195_mutex_driver_data = {
|
||||
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
|
||||
};
|
||||
|
||||
static const struct mtk_mutex_data mt8365_mutex_driver_data = {
|
||||
.mutex_mod = mt8365_mutex_mod,
|
||||
.mutex_sof = mt8183_mutex_sof,
|
||||
.mutex_mod_reg = MT8183_MUTEX0_MOD0,
|
||||
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
|
||||
.no_clk = true,
|
||||
};
|
||||
|
||||
struct mtk_mutex *mtk_mutex_get(struct device *dev)
|
||||
{
|
||||
struct mtk_mutex_ctx *mtx = dev_get_drvdata(dev);
|
||||
@ -572,6 +636,30 @@ void mtk_mutex_enable(struct mtk_mutex *mutex)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtk_mutex_enable);
|
||||
|
||||
int mtk_mutex_enable_by_cmdq(struct mtk_mutex *mutex, void *pkt)
|
||||
{
|
||||
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
|
||||
mutex[mutex->id]);
|
||||
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
|
||||
struct cmdq_pkt *cmdq_pkt = (struct cmdq_pkt *)pkt;
|
||||
|
||||
WARN_ON(&mtx->mutex[mutex->id] != mutex);
|
||||
|
||||
if (!mtx->cmdq_reg.size) {
|
||||
dev_err(mtx->dev, "mediatek,gce-client-reg hasn't been set");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmdq_pkt_write(cmdq_pkt, mtx->cmdq_reg.subsys,
|
||||
mtx->addr + DISP_REG_MUTEX_EN(mutex->id), 1);
|
||||
return 0;
|
||||
#else
|
||||
dev_err(mtx->dev, "Not support for enable MUTEX by CMDQ");
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtk_mutex_enable_by_cmdq);
|
||||
|
||||
void mtk_mutex_disable(struct mtk_mutex *mutex)
|
||||
{
|
||||
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
|
||||
@ -606,12 +694,67 @@ void mtk_mutex_release(struct mtk_mutex *mutex)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtk_mutex_release);
|
||||
|
||||
int mtk_mutex_write_mod(struct mtk_mutex *mutex,
|
||||
enum mtk_mutex_mod_index idx, bool clear)
|
||||
{
|
||||
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
|
||||
mutex[mutex->id]);
|
||||
unsigned int reg;
|
||||
unsigned int offset;
|
||||
|
||||
WARN_ON(&mtx->mutex[mutex->id] != mutex);
|
||||
|
||||
if (idx < MUTEX_MOD_IDX_MDP_RDMA0 ||
|
||||
idx >= MUTEX_MOD_IDX_MAX) {
|
||||
dev_err(mtx->dev, "Not supported MOD table index : %d", idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset = DISP_REG_MUTEX_MOD(mtx->data->mutex_mod_reg,
|
||||
mutex->id);
|
||||
reg = readl_relaxed(mtx->regs + offset);
|
||||
|
||||
if (clear)
|
||||
reg &= ~BIT(mtx->data->mutex_table_mod[idx]);
|
||||
else
|
||||
reg |= BIT(mtx->data->mutex_table_mod[idx]);
|
||||
|
||||
writel_relaxed(reg, mtx->regs + offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtk_mutex_write_mod);
|
||||
|
||||
int mtk_mutex_write_sof(struct mtk_mutex *mutex,
|
||||
enum mtk_mutex_sof_index idx)
|
||||
{
|
||||
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
|
||||
mutex[mutex->id]);
|
||||
|
||||
WARN_ON(&mtx->mutex[mutex->id] != mutex);
|
||||
|
||||
if (idx < MUTEX_SOF_IDX_SINGLE_MODE ||
|
||||
idx >= MUTEX_SOF_IDX_MAX) {
|
||||
dev_err(mtx->dev, "Not supported SOF index : %d", idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel_relaxed(idx, mtx->regs +
|
||||
DISP_REG_MUTEX_SOF(mtx->data->mutex_sof_reg, mutex->id));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtk_mutex_write_sof);
|
||||
|
||||
static int mtk_mutex_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mtk_mutex_ctx *mtx;
|
||||
struct resource *regs;
|
||||
int i;
|
||||
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
|
||||
int ret;
|
||||
#endif
|
||||
|
||||
mtx = devm_kzalloc(dev, sizeof(*mtx), GFP_KERNEL);
|
||||
if (!mtx)
|
||||
@ -631,12 +774,18 @@ static int mtk_mutex_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mtx->regs = devm_ioremap_resource(dev, regs);
|
||||
mtx->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s);
|
||||
if (IS_ERR(mtx->regs)) {
|
||||
dev_err(dev, "Failed to map mutex registers\n");
|
||||
return PTR_ERR(mtx->regs);
|
||||
}
|
||||
mtx->addr = regs->start;
|
||||
|
||||
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
|
||||
ret = cmdq_dev_get_client_reg(dev, &mtx->cmdq_reg, 0);
|
||||
if (ret)
|
||||
dev_dbg(dev, "No mediatek,gce-client-reg!\n");
|
||||
#endif
|
||||
|
||||
platform_set_drvdata(pdev, mtx);
|
||||
|
||||
@ -665,6 +814,8 @@ static const struct of_device_id mutex_driver_dt_match[] = {
|
||||
.data = &mt8192_mutex_driver_data},
|
||||
{ .compatible = "mediatek,mt8195-disp-mutex",
|
||||
.data = &mt8195_mutex_driver_data},
|
||||
{ .compatible = "mediatek,mt8365-disp-mutex",
|
||||
.data = &mt8365_mutex_driver_data},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mutex_driver_dt_match);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/soc/mediatek/infracfg.h>
|
||||
|
||||
#include "mt6795-pm-domains.h"
|
||||
#include "mt8167-pm-domains.h"
|
||||
#include "mt8173-pm-domains.h"
|
||||
#include "mt8183-pm-domains.h"
|
||||
@ -428,6 +429,9 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
|
||||
dev_err(scpsys->dev, "%pOF: failed to power on domain: %d\n", node, ret);
|
||||
goto err_put_subsys_clocks;
|
||||
}
|
||||
|
||||
if (MTK_SCPD_CAPS(pd, MTK_SCPD_ALWAYS_ON))
|
||||
pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
|
||||
}
|
||||
|
||||
if (scpsys->domains[id]) {
|
||||
@ -555,6 +559,10 @@ static void scpsys_domain_cleanup(struct scpsys *scpsys)
|
||||
}
|
||||
|
||||
static const struct of_device_id scpsys_of_match[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt6795-power-controller",
|
||||
.data = &mt6795_scpsys_data,
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt8167-power-controller",
|
||||
.data = &mt8167_scpsys_data,
|
||||
|
@ -8,6 +8,8 @@
|
||||
#define MTK_SCPD_SRAM_ISO BIT(2)
|
||||
#define MTK_SCPD_KEEP_DEFAULT_OFF BIT(3)
|
||||
#define MTK_SCPD_DOMAIN_SUPPLY BIT(4)
|
||||
/* can't set MTK_SCPD_KEEP_DEFAULT_OFF at the same time */
|
||||
#define MTK_SCPD_ALWAYS_ON BIT(5)
|
||||
#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x))
|
||||
|
||||
#define SPM_VDE_PWR_CON 0x0210
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define PWRAP_POLL_DELAY_US 10
|
||||
#define PWRAP_POLL_TIMEOUT_US 10000
|
||||
|
||||
#define PWRAP_MT8135_BRIDGE_IORD_ARB_EN 0x4
|
||||
#define PWRAP_MT8135_BRIDGE_WACS3_EN 0x10
|
||||
#define PWRAP_MT8135_BRIDGE_INIT_DONE3 0x14
|
||||
@ -1140,12 +1143,9 @@ enum pwrap_type {
|
||||
};
|
||||
|
||||
struct pmic_wrapper;
|
||||
struct pwrap_slv_type {
|
||||
const u32 *dew_regs;
|
||||
enum pmic_type type;
|
||||
|
||||
struct pwrap_slv_regops {
|
||||
const struct regmap_config *regmap;
|
||||
/* Flags indicating the capability for the target slave */
|
||||
u32 caps;
|
||||
/*
|
||||
* pwrap operations are highly associated with the PMIC types,
|
||||
* so the pointers added increases flexibility allowing determination
|
||||
@ -1155,6 +1155,14 @@ struct pwrap_slv_type {
|
||||
int (*pwrap_write)(struct pmic_wrapper *wrp, u32 adr, u32 wdata);
|
||||
};
|
||||
|
||||
struct pwrap_slv_type {
|
||||
const u32 *dew_regs;
|
||||
enum pmic_type type;
|
||||
const struct pwrap_slv_regops *regops;
|
||||
/* Flags indicating the capability for the target slave */
|
||||
u32 caps;
|
||||
};
|
||||
|
||||
struct pmic_wrapper {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
@ -1241,27 +1249,14 @@ static bool pwrap_is_fsm_idle_and_sync_idle(struct pmic_wrapper *wrp)
|
||||
(val & PWRAP_STATE_SYNC_IDLE0);
|
||||
}
|
||||
|
||||
static int pwrap_wait_for_state(struct pmic_wrapper *wrp,
|
||||
bool (*fp)(struct pmic_wrapper *))
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + usecs_to_jiffies(10000);
|
||||
|
||||
do {
|
||||
if (time_after(jiffies, timeout))
|
||||
return fp(wrp) ? 0 : -ETIMEDOUT;
|
||||
if (fp(wrp))
|
||||
return 0;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
|
||||
{
|
||||
bool tmp;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
pwrap_leave_fsm_vldclr(wrp);
|
||||
return ret;
|
||||
@ -1273,7 +1268,8 @@ static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
|
||||
val = (adr >> 1) << 16;
|
||||
pwrap_writel(wrp, val, PWRAP_WACS2_CMD);
|
||||
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_vldclr, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1290,11 +1286,14 @@ static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
|
||||
|
||||
static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
|
||||
{
|
||||
bool tmp;
|
||||
int ret, msb;
|
||||
|
||||
*rdata = 0;
|
||||
for (msb = 0; msb < 2; msb++) {
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
|
||||
if (ret) {
|
||||
pwrap_leave_fsm_vldclr(wrp);
|
||||
return ret;
|
||||
@ -1303,7 +1302,8 @@ static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
|
||||
pwrap_writel(wrp, ((msb << 30) | (adr << 16)),
|
||||
PWRAP_WACS2_CMD);
|
||||
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_vldclr, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1318,14 +1318,16 @@ static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
|
||||
|
||||
static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
|
||||
{
|
||||
return wrp->slave->pwrap_read(wrp, adr, rdata);
|
||||
return wrp->slave->regops->pwrap_read(wrp, adr, rdata);
|
||||
}
|
||||
|
||||
static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
|
||||
{
|
||||
bool tmp;
|
||||
int ret;
|
||||
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
pwrap_leave_fsm_vldclr(wrp);
|
||||
return ret;
|
||||
@ -1344,10 +1346,12 @@ static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
|
||||
|
||||
static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
|
||||
{
|
||||
bool tmp;
|
||||
int ret, msb, rdata;
|
||||
|
||||
for (msb = 0; msb < 2; msb++) {
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
pwrap_leave_fsm_vldclr(wrp);
|
||||
return ret;
|
||||
@ -1373,7 +1377,7 @@ static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
|
||||
|
||||
static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
|
||||
{
|
||||
return wrp->slave->pwrap_write(wrp, adr, wdata);
|
||||
return wrp->slave->regops->pwrap_write(wrp, adr, wdata);
|
||||
}
|
||||
|
||||
static int pwrap_regmap_read(void *context, u32 adr, u32 *rdata)
|
||||
@ -1388,6 +1392,7 @@ static int pwrap_regmap_write(void *context, u32 adr, u32 wdata)
|
||||
|
||||
static int pwrap_reset_spislave(struct pmic_wrapper *wrp)
|
||||
{
|
||||
bool tmp;
|
||||
int ret, i;
|
||||
|
||||
pwrap_writel(wrp, 0, PWRAP_HIPRIO_ARB_EN);
|
||||
@ -1407,7 +1412,8 @@ static int pwrap_reset_spislave(struct pmic_wrapper *wrp)
|
||||
pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_OUTS,
|
||||
PWRAP_MAN_CMD);
|
||||
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_sync_idle);
|
||||
ret = readx_poll_timeout(pwrap_is_sync_idle, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
|
||||
return ret;
|
||||
@ -1458,14 +1464,15 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp)
|
||||
static int pwrap_init_dual_io(struct pmic_wrapper *wrp)
|
||||
{
|
||||
int ret;
|
||||
bool tmp;
|
||||
u32 rdata;
|
||||
|
||||
/* Enable dual IO mode */
|
||||
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1);
|
||||
|
||||
/* Check IDLE & INIT_DONE in advance */
|
||||
ret = pwrap_wait_for_state(wrp,
|
||||
pwrap_is_fsm_idle_and_sync_idle);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_idle_and_sync_idle, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
|
||||
return ret;
|
||||
@ -1570,6 +1577,7 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp)
|
||||
static int pwrap_init_cipher(struct pmic_wrapper *wrp)
|
||||
{
|
||||
int ret;
|
||||
bool tmp;
|
||||
u32 rdata = 0;
|
||||
|
||||
pwrap_writel(wrp, 0x1, PWRAP_CIPHER_SWRST);
|
||||
@ -1624,14 +1632,16 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
|
||||
}
|
||||
|
||||
/* wait for cipher data ready@AP */
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_cipher_ready);
|
||||
ret = readx_poll_timeout(pwrap_is_cipher_ready, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(wrp->dev, "cipher data ready@AP fail, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wait for cipher data ready@PMIC */
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_pmic_cipher_ready);
|
||||
ret = readx_poll_timeout(pwrap_is_pmic_cipher_ready, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(wrp->dev,
|
||||
"timeout waiting for cipher data ready@PMIC\n");
|
||||
@ -1640,7 +1650,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
|
||||
|
||||
/* wait for cipher mode idle */
|
||||
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_MODE], 0x1);
|
||||
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle);
|
||||
ret = readx_poll_timeout(pwrap_is_fsm_idle_and_sync_idle, wrp, tmp, tmp,
|
||||
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(wrp->dev, "cipher mode idle fail, ret=%d\n", ret);
|
||||
return ret;
|
||||
@ -1885,99 +1896,82 @@ static const struct regmap_config pwrap_regmap_config32 = {
|
||||
.max_register = 0xffff,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_regops pwrap_regops16 = {
|
||||
.pwrap_read = pwrap_read16,
|
||||
.pwrap_write = pwrap_write16,
|
||||
.regmap = &pwrap_regmap_config16,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_regops pwrap_regops32 = {
|
||||
.pwrap_read = pwrap_read32,
|
||||
.pwrap_write = pwrap_write32,
|
||||
.regmap = &pwrap_regmap_config32,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_type pmic_mt6323 = {
|
||||
.dew_regs = mt6323_regs,
|
||||
.type = PMIC_MT6323,
|
||||
.regmap = &pwrap_regmap_config16,
|
||||
.regops = &pwrap_regops16,
|
||||
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO |
|
||||
PWRAP_SLV_CAP_SECURITY,
|
||||
.pwrap_read = pwrap_read16,
|
||||
.pwrap_write = pwrap_write16,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_type pmic_mt6351 = {
|
||||
.dew_regs = mt6351_regs,
|
||||
.type = PMIC_MT6351,
|
||||
.regmap = &pwrap_regmap_config16,
|
||||
.regops = &pwrap_regops16,
|
||||
.caps = 0,
|
||||
.pwrap_read = pwrap_read16,
|
||||
.pwrap_write = pwrap_write16,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_type pmic_mt6357 = {
|
||||
.dew_regs = mt6357_regs,
|
||||
.type = PMIC_MT6357,
|
||||
.regmap = &pwrap_regmap_config16,
|
||||
.regops = &pwrap_regops16,
|
||||
.caps = 0,
|
||||
.pwrap_read = pwrap_read16,
|
||||
.pwrap_write = pwrap_write16,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_type pmic_mt6358 = {
|
||||
.dew_regs = mt6358_regs,
|
||||
.type = PMIC_MT6358,
|
||||
.regmap = &pwrap_regmap_config16,
|
||||
.regops = &pwrap_regops16,
|
||||
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO,
|
||||
.pwrap_read = pwrap_read16,
|
||||
.pwrap_write = pwrap_write16,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_type pmic_mt6359 = {
|
||||
.dew_regs = mt6359_regs,
|
||||
.type = PMIC_MT6359,
|
||||
.regmap = &pwrap_regmap_config16,
|
||||
.regops = &pwrap_regops16,
|
||||
.caps = PWRAP_SLV_CAP_DUALIO,
|
||||
.pwrap_read = pwrap_read16,
|
||||
.pwrap_write = pwrap_write16,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_type pmic_mt6380 = {
|
||||
.dew_regs = NULL,
|
||||
.type = PMIC_MT6380,
|
||||
.regmap = &pwrap_regmap_config32,
|
||||
.regops = &pwrap_regops32,
|
||||
.caps = 0,
|
||||
.pwrap_read = pwrap_read32,
|
||||
.pwrap_write = pwrap_write32,
|
||||
};
|
||||
|
||||
static const struct pwrap_slv_type pmic_mt6397 = {
|
||||
.dew_regs = mt6397_regs,
|
||||
.type = PMIC_MT6397,
|
||||
.regmap = &pwrap_regmap_config16,
|
||||
.regops = &pwrap_regops16,
|
||||
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO |
|
||||
PWRAP_SLV_CAP_SECURITY,
|
||||
.pwrap_read = pwrap_read16,
|
||||
.pwrap_write = pwrap_write16,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_slave_match_tbl[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt6323",
|
||||
.data = &pmic_mt6323,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6351",
|
||||
.data = &pmic_mt6351,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6357",
|
||||
.data = &pmic_mt6357,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6358",
|
||||
.data = &pmic_mt6358,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6359",
|
||||
.data = &pmic_mt6359,
|
||||
}, {
|
||||
/* The MT6380 PMIC only implements a regulator, so we bind it
|
||||
* directly instead of using a MFD.
|
||||
*/
|
||||
.compatible = "mediatek,mt6380-regulator",
|
||||
.data = &pmic_mt6380,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6397",
|
||||
.data = &pmic_mt6397,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
{ .compatible = "mediatek,mt6323", .data = &pmic_mt6323 },
|
||||
{ .compatible = "mediatek,mt6351", .data = &pmic_mt6351 },
|
||||
{ .compatible = "mediatek,mt6357", .data = &pmic_mt6357 },
|
||||
{ .compatible = "mediatek,mt6358", .data = &pmic_mt6358 },
|
||||
{ .compatible = "mediatek,mt6359", .data = &pmic_mt6359 },
|
||||
|
||||
/* The MT6380 PMIC only implements a regulator, so we bind it
|
||||
* directly instead of using a MFD.
|
||||
*/
|
||||
{ .compatible = "mediatek,mt6380-regulator", .data = &pmic_mt6380 },
|
||||
{ .compatible = "mediatek,mt6397", .data = &pmic_mt6397 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_slave_match_tbl);
|
||||
|
||||
@ -2136,45 +2130,19 @@ static struct pmic_wrapper_type pwrap_mt8186 = {
|
||||
};
|
||||
|
||||
static const struct of_device_id of_pwrap_match_tbl[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt2701-pwrap",
|
||||
.data = &pwrap_mt2701,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6765-pwrap",
|
||||
.data = &pwrap_mt6765,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6779-pwrap",
|
||||
.data = &pwrap_mt6779,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6797-pwrap",
|
||||
.data = &pwrap_mt6797,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6873-pwrap",
|
||||
.data = &pwrap_mt6873,
|
||||
}, {
|
||||
.compatible = "mediatek,mt7622-pwrap",
|
||||
.data = &pwrap_mt7622,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8135-pwrap",
|
||||
.data = &pwrap_mt8135,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8173-pwrap",
|
||||
.data = &pwrap_mt8173,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8183-pwrap",
|
||||
.data = &pwrap_mt8183,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8186-pwrap",
|
||||
.data = &pwrap_mt8186,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8195-pwrap",
|
||||
.data = &pwrap_mt8195,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8516-pwrap",
|
||||
.data = &pwrap_mt8516,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
{ .compatible = "mediatek,mt2701-pwrap", .data = &pwrap_mt2701 },
|
||||
{ .compatible = "mediatek,mt6765-pwrap", .data = &pwrap_mt6765 },
|
||||
{ .compatible = "mediatek,mt6779-pwrap", .data = &pwrap_mt6779 },
|
||||
{ .compatible = "mediatek,mt6797-pwrap", .data = &pwrap_mt6797 },
|
||||
{ .compatible = "mediatek,mt6873-pwrap", .data = &pwrap_mt6873 },
|
||||
{ .compatible = "mediatek,mt7622-pwrap", .data = &pwrap_mt7622 },
|
||||
{ .compatible = "mediatek,mt8135-pwrap", .data = &pwrap_mt8135 },
|
||||
{ .compatible = "mediatek,mt8173-pwrap", .data = &pwrap_mt8173 },
|
||||
{ .compatible = "mediatek,mt8183-pwrap", .data = &pwrap_mt8183 },
|
||||
{ .compatible = "mediatek,mt8186-pwrap", .data = &pwrap_mt8186 },
|
||||
{ .compatible = "mediatek,mt8195-pwrap", .data = &pwrap_mt8195 },
|
||||
{ .compatible = "mediatek,mt8516-pwrap", .data = &pwrap_mt8516 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_pwrap_match_tbl);
|
||||
|
||||
@ -2185,7 +2153,6 @@ static int pwrap_probe(struct platform_device *pdev)
|
||||
struct pmic_wrapper *wrp;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *of_slave_id = NULL;
|
||||
struct resource *res;
|
||||
|
||||
if (np->child)
|
||||
of_slave_id = of_match_node(of_slave_match_tbl, np->child);
|
||||
@ -2205,8 +2172,7 @@ static int pwrap_probe(struct platform_device *pdev)
|
||||
wrp->slave = of_slave_id->data;
|
||||
wrp->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap");
|
||||
wrp->base = devm_ioremap_resource(wrp->dev, res);
|
||||
wrp->base = devm_platform_ioremap_resource_byname(pdev, "pwrap");
|
||||
if (IS_ERR(wrp->base))
|
||||
return PTR_ERR(wrp->base);
|
||||
|
||||
@ -2220,9 +2186,7 @@ static int pwrap_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_BRIDGE)) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"pwrap-bridge");
|
||||
wrp->bridge_base = devm_ioremap_resource(wrp->dev, res);
|
||||
wrp->bridge_base = devm_platform_ioremap_resource_byname(pdev, "pwrap-bridge");
|
||||
if (IS_ERR(wrp->bridge_base))
|
||||
return PTR_ERR(wrp->bridge_base);
|
||||
|
||||
@ -2315,13 +2279,18 @@ static int pwrap_probe(struct platform_device *pdev)
|
||||
pwrap_writel(wrp, wrp->master->int1_en_all, PWRAP_INT1_EN);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto err_out2;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt,
|
||||
IRQF_TRIGGER_HIGH,
|
||||
"mt-pmic-pwrap", wrp);
|
||||
if (ret)
|
||||
goto err_out2;
|
||||
|
||||
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regmap);
|
||||
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regops->regmap);
|
||||
if (IS_ERR(wrp->regmap)) {
|
||||
ret = PTR_ERR(wrp->regmap);
|
||||
goto err_out2;
|
||||
|
2403
drivers/soc/mediatek/mtk-svs.c
Normal file
2403
drivers/soc/mediatek/mtk-svs.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -129,7 +129,10 @@ config QCOM_RPMHPD
|
||||
|
||||
config QCOM_RPMPD
|
||||
tristate "Qualcomm RPM Power domain driver"
|
||||
depends on PM
|
||||
depends on QCOM_SMD_RPM
|
||||
select PM_GENERIC_DOMAINS
|
||||
select PM_GENERIC_DOMAINS_OF
|
||||
help
|
||||
QCOM RPM Power domain driver to support power-domains with
|
||||
performance states. The driver communicates a performance state
|
||||
@ -228,4 +231,19 @@ config QCOM_APR
|
||||
application processor and QDSP6. APR is
|
||||
used by audio driver to configure QDSP6
|
||||
ASM, ADM and AFE modules.
|
||||
|
||||
config QCOM_ICC_BWMON
|
||||
tristate "QCOM Interconnect Bandwidth Monitor driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select PM_OPP
|
||||
help
|
||||
Sets up driver monitoring bandwidth on various interconnects and
|
||||
based on that voting for interconnect bandwidth, adjusting their
|
||||
speed to current demand.
|
||||
Current implementation brings support for BWMON v4, used for example
|
||||
on SDM845 to measure bandwidth between CPU (gladiator_noc) and Last
|
||||
Level Cache (memnoc). Usage of this BWMON allows to remove some of
|
||||
the fixed bandwidth votes from cpufreq (CPU nodes) thus achieve high
|
||||
memory throughput even with lower CPU frequencies.
|
||||
|
||||
endmenu
|
||||
|
@ -28,3 +28,4 @@ obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
|
||||
obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
|
||||
obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
|
||||
obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o
|
||||
obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o
|
||||
|
@ -377,17 +377,14 @@ static int apr_device_probe(struct device *dev)
|
||||
static void apr_device_remove(struct device *dev)
|
||||
{
|
||||
struct apr_device *adev = to_apr_device(dev);
|
||||
struct apr_driver *adrv;
|
||||
struct apr_driver *adrv = to_apr_driver(dev->driver);
|
||||
struct packet_router *apr = dev_get_drvdata(adev->dev.parent);
|
||||
|
||||
if (dev->driver) {
|
||||
adrv = to_apr_driver(dev->driver);
|
||||
if (adrv->remove)
|
||||
adrv->remove(adev);
|
||||
spin_lock(&apr->svcs_lock);
|
||||
idr_remove(&apr->svcs_idr, adev->svc.id);
|
||||
spin_unlock(&apr->svcs_lock);
|
||||
}
|
||||
if (adrv->remove)
|
||||
adrv->remove(adev);
|
||||
spin_lock(&apr->svcs_lock);
|
||||
idr_remove(&apr->svcs_idr, adev->svc.id);
|
||||
spin_unlock(&apr->svcs_lock);
|
||||
}
|
||||
|
||||
static int apr_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
|
@ -141,13 +141,17 @@ static int cmd_db_get_header(const char *id, const struct entry_header **eh,
|
||||
const struct rsc_hdr *rsc_hdr;
|
||||
const struct entry_header *ent;
|
||||
int ret, i, j;
|
||||
u8 query[8];
|
||||
u8 query[sizeof(ent->id)] __nonstring;
|
||||
|
||||
ret = cmd_db_ready();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Pad out query string to same length as in DB */
|
||||
/*
|
||||
* Pad out query string to same length as in DB. NOTE: the output
|
||||
* query string is not necessarily '\0' terminated if it bumps up
|
||||
* against the max size. That's OK and expected.
|
||||
*/
|
||||
strncpy(query, id, sizeof(query));
|
||||
|
||||
for (i = 0; i < MAX_SLV_ID; i++) {
|
||||
|
419
drivers/soc/qcom/icc-bwmon.c
Normal file
419
drivers/soc/qcom/icc-bwmon.c
Normal file
@ -0,0 +1,419 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2021-2022 Linaro Ltd
|
||||
* Author: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>, based on
|
||||
* previous work of Thara Gopinath and msm-4.9 downstream sources.
|
||||
*/
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
/*
|
||||
* The BWMON samples data throughput within 'sample_ms' time. With three
|
||||
* configurable thresholds (Low, Medium and High) gives four windows (called
|
||||
* zones) of current bandwidth:
|
||||
*
|
||||
* Zone 0: byte count < THRES_LO
|
||||
* Zone 1: THRES_LO < byte count < THRES_MED
|
||||
* Zone 2: THRES_MED < byte count < THRES_HIGH
|
||||
* Zone 3: THRES_HIGH < byte count
|
||||
*
|
||||
* Zones 0 and 2 are not used by this driver.
|
||||
*/
|
||||
|
||||
/* Internal sampling clock frequency */
|
||||
#define HW_TIMER_HZ 19200000
|
||||
|
||||
#define BWMON_GLOBAL_IRQ_STATUS 0x0
|
||||
#define BWMON_GLOBAL_IRQ_CLEAR 0x8
|
||||
#define BWMON_GLOBAL_IRQ_ENABLE 0xc
|
||||
#define BWMON_GLOBAL_IRQ_ENABLE_ENABLE BIT(0)
|
||||
|
||||
#define BWMON_IRQ_STATUS 0x100
|
||||
#define BWMON_IRQ_STATUS_ZONE_SHIFT 4
|
||||
#define BWMON_IRQ_CLEAR 0x108
|
||||
#define BWMON_IRQ_ENABLE 0x10c
|
||||
#define BWMON_IRQ_ENABLE_ZONE1_SHIFT 5
|
||||
#define BWMON_IRQ_ENABLE_ZONE2_SHIFT 6
|
||||
#define BWMON_IRQ_ENABLE_ZONE3_SHIFT 7
|
||||
#define BWMON_IRQ_ENABLE_MASK (BIT(BWMON_IRQ_ENABLE_ZONE1_SHIFT) | \
|
||||
BIT(BWMON_IRQ_ENABLE_ZONE3_SHIFT))
|
||||
|
||||
#define BWMON_ENABLE 0x2a0
|
||||
#define BWMON_ENABLE_ENABLE BIT(0)
|
||||
|
||||
#define BWMON_CLEAR 0x2a4
|
||||
#define BWMON_CLEAR_CLEAR BIT(0)
|
||||
|
||||
#define BWMON_SAMPLE_WINDOW 0x2a8
|
||||
#define BWMON_THRESHOLD_HIGH 0x2ac
|
||||
#define BWMON_THRESHOLD_MED 0x2b0
|
||||
#define BWMON_THRESHOLD_LOW 0x2b4
|
||||
|
||||
#define BWMON_ZONE_ACTIONS 0x2b8
|
||||
/*
|
||||
* Actions to perform on some zone 'z' when current zone hits the threshold:
|
||||
* Increment counter of zone 'z'
|
||||
*/
|
||||
#define BWMON_ZONE_ACTIONS_INCREMENT(z) (0x2 << ((z) * 2))
|
||||
/* Clear counter of zone 'z' */
|
||||
#define BWMON_ZONE_ACTIONS_CLEAR(z) (0x1 << ((z) * 2))
|
||||
|
||||
/* Zone 0 threshold hit: Clear zone count */
|
||||
#define BWMON_ZONE_ACTIONS_ZONE0 (BWMON_ZONE_ACTIONS_CLEAR(0))
|
||||
|
||||
/* Zone 1 threshold hit: Increment zone count & clear lower zones */
|
||||
#define BWMON_ZONE_ACTIONS_ZONE1 (BWMON_ZONE_ACTIONS_INCREMENT(1) | \
|
||||
BWMON_ZONE_ACTIONS_CLEAR(0))
|
||||
|
||||
/* Zone 2 threshold hit: Increment zone count & clear lower zones */
|
||||
#define BWMON_ZONE_ACTIONS_ZONE2 (BWMON_ZONE_ACTIONS_INCREMENT(2) | \
|
||||
BWMON_ZONE_ACTIONS_CLEAR(1) | \
|
||||
BWMON_ZONE_ACTIONS_CLEAR(0))
|
||||
|
||||
/* Zone 3 threshold hit: Increment zone count & clear lower zones */
|
||||
#define BWMON_ZONE_ACTIONS_ZONE3 (BWMON_ZONE_ACTIONS_INCREMENT(3) | \
|
||||
BWMON_ZONE_ACTIONS_CLEAR(2) | \
|
||||
BWMON_ZONE_ACTIONS_CLEAR(1) | \
|
||||
BWMON_ZONE_ACTIONS_CLEAR(0))
|
||||
/* Value for BWMON_ZONE_ACTIONS */
|
||||
#define BWMON_ZONE_ACTIONS_DEFAULT (BWMON_ZONE_ACTIONS_ZONE0 | \
|
||||
BWMON_ZONE_ACTIONS_ZONE1 << 8 | \
|
||||
BWMON_ZONE_ACTIONS_ZONE2 << 16 | \
|
||||
BWMON_ZONE_ACTIONS_ZONE3 << 24)
|
||||
|
||||
/*
|
||||
* There is no clear documentation/explanation of BWMON_THRESHOLD_COUNT
|
||||
* register. Based on observations, this is number of times one threshold has to
|
||||
* be reached, to trigger interrupt in given zone.
|
||||
*
|
||||
* 0xff are maximum values meant to ignore the zones 0 and 2.
|
||||
*/
|
||||
#define BWMON_THRESHOLD_COUNT 0x2bc
|
||||
#define BWMON_THRESHOLD_COUNT_ZONE1_SHIFT 8
|
||||
#define BWMON_THRESHOLD_COUNT_ZONE2_SHIFT 16
|
||||
#define BWMON_THRESHOLD_COUNT_ZONE3_SHIFT 24
|
||||
#define BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT 0xff
|
||||
#define BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT 0xff
|
||||
|
||||
/* BWMONv4 count registers use count unit of 64 kB */
|
||||
#define BWMON_COUNT_UNIT_KB 64
|
||||
#define BWMON_ZONE_COUNT 0x2d8
|
||||
#define BWMON_ZONE_MAX(zone) (0x2e0 + 4 * (zone))
|
||||
|
||||
struct icc_bwmon_data {
|
||||
unsigned int sample_ms;
|
||||
unsigned int default_highbw_kbps;
|
||||
unsigned int default_medbw_kbps;
|
||||
unsigned int default_lowbw_kbps;
|
||||
u8 zone1_thres_count;
|
||||
u8 zone3_thres_count;
|
||||
};
|
||||
|
||||
struct icc_bwmon {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
|
||||
unsigned int default_lowbw_kbps;
|
||||
unsigned int sample_ms;
|
||||
unsigned int max_bw_kbps;
|
||||
unsigned int min_bw_kbps;
|
||||
unsigned int target_kbps;
|
||||
unsigned int current_kbps;
|
||||
};
|
||||
|
||||
static void bwmon_clear_counters(struct icc_bwmon *bwmon)
|
||||
{
|
||||
/*
|
||||
* Clear counters. The order and barriers are
|
||||
* important. Quoting downstream Qualcomm msm-4.9 tree:
|
||||
*
|
||||
* The counter clear and IRQ clear bits are not in the same 4KB
|
||||
* region. So, we need to make sure the counter clear is completed
|
||||
* before we try to clear the IRQ or do any other counter operations.
|
||||
*/
|
||||
writel(BWMON_CLEAR_CLEAR, bwmon->base + BWMON_CLEAR);
|
||||
}
|
||||
|
||||
static void bwmon_clear_irq(struct icc_bwmon *bwmon)
|
||||
{
|
||||
/*
|
||||
* Clear zone and global interrupts. The order and barriers are
|
||||
* important. Quoting downstream Qualcomm msm-4.9 tree:
|
||||
*
|
||||
* Synchronize the local interrupt clear in mon_irq_clear()
|
||||
* with the global interrupt clear here. Otherwise, the CPU
|
||||
* may reorder the two writes and clear the global interrupt
|
||||
* before the local interrupt, causing the global interrupt
|
||||
* to be retriggered by the local interrupt still being high.
|
||||
*
|
||||
* Similarly, because the global registers are in a different
|
||||
* region than the local registers, we need to ensure any register
|
||||
* writes to enable the monitor after this call are ordered with the
|
||||
* clearing here so that local writes don't happen before the
|
||||
* interrupt is cleared.
|
||||
*/
|
||||
writel(BWMON_IRQ_ENABLE_MASK, bwmon->base + BWMON_IRQ_CLEAR);
|
||||
writel(BIT(0), bwmon->base + BWMON_GLOBAL_IRQ_CLEAR);
|
||||
}
|
||||
|
||||
static void bwmon_disable(struct icc_bwmon *bwmon)
|
||||
{
|
||||
/* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */
|
||||
writel(0x0, bwmon->base + BWMON_GLOBAL_IRQ_ENABLE);
|
||||
writel(0x0, bwmon->base + BWMON_IRQ_ENABLE);
|
||||
|
||||
/*
|
||||
* Disable bwmon. Must happen before bwmon_clear_irq() to avoid spurious
|
||||
* IRQ.
|
||||
*/
|
||||
writel(0x0, bwmon->base + BWMON_ENABLE);
|
||||
}
|
||||
|
||||
static void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable)
|
||||
{
|
||||
/* Enable interrupts */
|
||||
writel(BWMON_GLOBAL_IRQ_ENABLE_ENABLE,
|
||||
bwmon->base + BWMON_GLOBAL_IRQ_ENABLE);
|
||||
writel(irq_enable, bwmon->base + BWMON_IRQ_ENABLE);
|
||||
|
||||
/* Enable bwmon */
|
||||
writel(BWMON_ENABLE_ENABLE, bwmon->base + BWMON_ENABLE);
|
||||
}
|
||||
|
||||
static unsigned int bwmon_kbps_to_count(unsigned int kbps)
|
||||
{
|
||||
return kbps / BWMON_COUNT_UNIT_KB;
|
||||
}
|
||||
|
||||
static void bwmon_set_threshold(struct icc_bwmon *bwmon, unsigned int reg,
|
||||
unsigned int kbps)
|
||||
{
|
||||
unsigned int thres;
|
||||
|
||||
thres = mult_frac(bwmon_kbps_to_count(kbps), bwmon->sample_ms,
|
||||
MSEC_PER_SEC);
|
||||
writel_relaxed(thres, bwmon->base + reg);
|
||||
}
|
||||
|
||||
static void bwmon_start(struct icc_bwmon *bwmon,
|
||||
const struct icc_bwmon_data *data)
|
||||
{
|
||||
unsigned int thres_count;
|
||||
int window;
|
||||
|
||||
bwmon_clear_counters(bwmon);
|
||||
|
||||
window = mult_frac(bwmon->sample_ms, HW_TIMER_HZ, MSEC_PER_SEC);
|
||||
/* Maximum sampling window: 0xfffff */
|
||||
writel_relaxed(window, bwmon->base + BWMON_SAMPLE_WINDOW);
|
||||
|
||||
bwmon_set_threshold(bwmon, BWMON_THRESHOLD_HIGH,
|
||||
data->default_highbw_kbps);
|
||||
bwmon_set_threshold(bwmon, BWMON_THRESHOLD_MED,
|
||||
data->default_medbw_kbps);
|
||||
bwmon_set_threshold(bwmon, BWMON_THRESHOLD_LOW,
|
||||
data->default_lowbw_kbps);
|
||||
|
||||
thres_count = data->zone3_thres_count << BWMON_THRESHOLD_COUNT_ZONE3_SHIFT |
|
||||
BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT << BWMON_THRESHOLD_COUNT_ZONE2_SHIFT |
|
||||
data->zone1_thres_count << BWMON_THRESHOLD_COUNT_ZONE1_SHIFT |
|
||||
BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT;
|
||||
writel_relaxed(thres_count, bwmon->base + BWMON_THRESHOLD_COUNT);
|
||||
writel_relaxed(BWMON_ZONE_ACTIONS_DEFAULT,
|
||||
bwmon->base + BWMON_ZONE_ACTIONS);
|
||||
/* Write barriers in bwmon_clear_irq() */
|
||||
|
||||
bwmon_clear_irq(bwmon);
|
||||
bwmon_enable(bwmon, BWMON_IRQ_ENABLE_MASK);
|
||||
}
|
||||
|
||||
static irqreturn_t bwmon_intr(int irq, void *dev_id)
|
||||
{
|
||||
struct icc_bwmon *bwmon = dev_id;
|
||||
unsigned int status, max;
|
||||
int zone;
|
||||
|
||||
status = readl(bwmon->base + BWMON_IRQ_STATUS);
|
||||
status &= BWMON_IRQ_ENABLE_MASK;
|
||||
if (!status) {
|
||||
/*
|
||||
* Only zone 1 and zone 3 interrupts are enabled but zone 2
|
||||
* threshold could be hit and trigger interrupt even if not
|
||||
* enabled.
|
||||
* Such spurious interrupt might come with valuable max count or
|
||||
* not, so solution would be to always check all
|
||||
* BWMON_ZONE_MAX() registers to find the highest value.
|
||||
* Such case is currently ignored.
|
||||
*/
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
bwmon_disable(bwmon);
|
||||
|
||||
zone = get_bitmask_order(status >> BWMON_IRQ_STATUS_ZONE_SHIFT) - 1;
|
||||
/*
|
||||
* Zone max bytes count register returns count units within sampling
|
||||
* window. Downstream kernel for BWMONv4 (called BWMON type 2 in
|
||||
* downstream) always increments the max bytes count by one.
|
||||
*/
|
||||
max = readl(bwmon->base + BWMON_ZONE_MAX(zone)) + 1;
|
||||
max *= BWMON_COUNT_UNIT_KB;
|
||||
bwmon->target_kbps = mult_frac(max, MSEC_PER_SEC, bwmon->sample_ms);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t bwmon_intr_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct icc_bwmon *bwmon = dev_id;
|
||||
unsigned int irq_enable = 0;
|
||||
struct dev_pm_opp *opp, *target_opp;
|
||||
unsigned int bw_kbps, up_kbps, down_kbps;
|
||||
|
||||
bw_kbps = bwmon->target_kbps;
|
||||
|
||||
target_opp = dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_kbps, 0);
|
||||
if (IS_ERR(target_opp) && PTR_ERR(target_opp) == -ERANGE)
|
||||
target_opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0);
|
||||
|
||||
bwmon->target_kbps = bw_kbps;
|
||||
|
||||
bw_kbps--;
|
||||
opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0);
|
||||
if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)
|
||||
down_kbps = bwmon->target_kbps;
|
||||
else
|
||||
down_kbps = bw_kbps;
|
||||
|
||||
up_kbps = bwmon->target_kbps + 1;
|
||||
|
||||
if (bwmon->target_kbps >= bwmon->max_bw_kbps)
|
||||
irq_enable = BIT(BWMON_IRQ_ENABLE_ZONE1_SHIFT);
|
||||
else if (bwmon->target_kbps <= bwmon->min_bw_kbps)
|
||||
irq_enable = BIT(BWMON_IRQ_ENABLE_ZONE3_SHIFT);
|
||||
else
|
||||
irq_enable = BWMON_IRQ_ENABLE_MASK;
|
||||
|
||||
bwmon_set_threshold(bwmon, BWMON_THRESHOLD_HIGH, up_kbps);
|
||||
bwmon_set_threshold(bwmon, BWMON_THRESHOLD_MED, down_kbps);
|
||||
/* Write barriers in bwmon_clear_counters() */
|
||||
bwmon_clear_counters(bwmon);
|
||||
bwmon_clear_irq(bwmon);
|
||||
bwmon_enable(bwmon, irq_enable);
|
||||
|
||||
if (bwmon->target_kbps == bwmon->current_kbps)
|
||||
goto out;
|
||||
|
||||
dev_pm_opp_set_opp(bwmon->dev, target_opp);
|
||||
bwmon->current_kbps = bwmon->target_kbps;
|
||||
|
||||
out:
|
||||
dev_pm_opp_put(target_opp);
|
||||
if (!IS_ERR(opp))
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bwmon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dev_pm_opp *opp;
|
||||
struct icc_bwmon *bwmon;
|
||||
const struct icc_bwmon_data *data;
|
||||
int ret;
|
||||
|
||||
bwmon = devm_kzalloc(dev, sizeof(*bwmon), GFP_KERNEL);
|
||||
if (!bwmon)
|
||||
return -ENOMEM;
|
||||
|
||||
data = of_device_get_match_data(dev);
|
||||
|
||||
bwmon->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(bwmon->base)) {
|
||||
dev_err(dev, "failed to map bwmon registers\n");
|
||||
return PTR_ERR(bwmon->base);
|
||||
}
|
||||
|
||||
bwmon->irq = platform_get_irq(pdev, 0);
|
||||
if (bwmon->irq < 0)
|
||||
return bwmon->irq;
|
||||
|
||||
ret = devm_pm_opp_of_add_table(dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to add OPP table\n");
|
||||
|
||||
bwmon->max_bw_kbps = UINT_MAX;
|
||||
opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0);
|
||||
if (IS_ERR(opp))
|
||||
return dev_err_probe(dev, ret, "failed to find max peak bandwidth\n");
|
||||
|
||||
bwmon->min_bw_kbps = 0;
|
||||
opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0);
|
||||
if (IS_ERR(opp))
|
||||
return dev_err_probe(dev, ret, "failed to find min peak bandwidth\n");
|
||||
|
||||
bwmon->sample_ms = data->sample_ms;
|
||||
bwmon->default_lowbw_kbps = data->default_lowbw_kbps;
|
||||
bwmon->dev = dev;
|
||||
|
||||
bwmon_disable(bwmon);
|
||||
ret = devm_request_threaded_irq(dev, bwmon->irq, bwmon_intr,
|
||||
bwmon_intr_thread,
|
||||
IRQF_ONESHOT, dev_name(dev), bwmon);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request IRQ\n");
|
||||
|
||||
platform_set_drvdata(pdev, bwmon);
|
||||
bwmon_start(bwmon, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bwmon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct icc_bwmon *bwmon = platform_get_drvdata(pdev);
|
||||
|
||||
bwmon_disable(bwmon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* BWMON v4 */
|
||||
static const struct icc_bwmon_data msm8998_bwmon_data = {
|
||||
.sample_ms = 4,
|
||||
.default_highbw_kbps = 4800 * 1024, /* 4.8 GBps */
|
||||
.default_medbw_kbps = 512 * 1024, /* 512 MBps */
|
||||
.default_lowbw_kbps = 0,
|
||||
.zone1_thres_count = 16,
|
||||
.zone3_thres_count = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id bwmon_of_match[] = {
|
||||
{ .compatible = "qcom,msm8998-bwmon", .data = &msm8998_bwmon_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bwmon_of_match);
|
||||
|
||||
static struct platform_driver bwmon_driver = {
|
||||
.probe = bwmon_probe,
|
||||
.remove = bwmon_remove,
|
||||
.driver = {
|
||||
.name = "qcom-bwmon",
|
||||
.of_match_table = bwmon_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(bwmon_driver);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
|
||||
MODULE_DESCRIPTION("QCOM BWMON driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -382,7 +382,7 @@ static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
|
||||
* llcc_slice_getd - get llcc slice descriptor
|
||||
* @uid: usecase_id for the client
|
||||
*
|
||||
* A pointer to llcc slice descriptor will be returned on success and
|
||||
* A pointer to llcc slice descriptor will be returned on success
|
||||
* and error pointer is returned on failure
|
||||
*/
|
||||
struct llcc_slice_desc *llcc_slice_getd(u32 uid)
|
||||
|
@ -108,6 +108,8 @@ EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
|
||||
* qcom_mdt_read_metadata() - read header and metadata from mdt or mbn
|
||||
* @fw: firmware of mdt header or mbn
|
||||
* @data_len: length of the read metadata blob
|
||||
* @fw_name: name of the firmware, for construction of segment file names
|
||||
* @dev: device handle to associate resources with
|
||||
*
|
||||
* The mechanism that performs the authentication of the loading firmware
|
||||
* expects an ELF header directly followed by the segment of hashes, with no
|
||||
@ -192,7 +194,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
|
||||
* qcom_mdt_pas_init() - initialize PAS region for firmware loading
|
||||
* @dev: device handle to associate resources with
|
||||
* @fw: firmware object for the mdt file
|
||||
* @firmware: name of the firmware, for construction of segment file names
|
||||
* @fw_name: name of the firmware, for construction of segment file names
|
||||
* @pas_id: PAS identifier
|
||||
* @mem_phys: physical address of allocated memory region
|
||||
* @ctx: PAS metadata context, to be released by caller
|
||||
|
@ -194,14 +194,17 @@ struct ocmem *of_get_ocmem(struct device *dev)
|
||||
devnode = of_parse_phandle(dev->of_node, "sram", 0);
|
||||
if (!devnode || !devnode->parent) {
|
||||
dev_err(dev, "Cannot look up sram phandle\n");
|
||||
of_node_put(devnode);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(devnode->parent);
|
||||
if (!pdev) {
|
||||
dev_err(dev, "Cannot find device node %s\n", devnode->name);
|
||||
of_node_put(devnode);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
of_node_put(devnode);
|
||||
|
||||
ocmem = platform_get_drvdata(pdev);
|
||||
if (!ocmem) {
|
||||
|
@ -399,8 +399,10 @@ static int qmp_cooling_devices_register(struct qmp *qmp)
|
||||
continue;
|
||||
ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++],
|
||||
child);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
goto unroll;
|
||||
}
|
||||
}
|
||||
|
||||
if (!count)
|
||||
|
@ -23,8 +23,8 @@
|
||||
/**
|
||||
* struct rpmhpd - top level RPMh power domain resource data structure
|
||||
* @dev: rpmh power domain controller device
|
||||
* @pd: generic_pm_domain corrresponding to the power domain
|
||||
* @parent: generic_pm_domain corrresponding to the parent's power domain
|
||||
* @pd: generic_pm_domain corresponding to the power domain
|
||||
* @parent: generic_pm_domain corresponding to the parent's power domain
|
||||
* @peer: A peer power domain in case Active only Voting is
|
||||
* supported
|
||||
* @active_only: True if it represents an Active only peer
|
||||
|
@ -453,6 +453,7 @@ static const struct rpmpd_desc qcm2290_desc = {
|
||||
static const struct of_device_id rpmpd_match_table[] = {
|
||||
{ .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc },
|
||||
{ .compatible = "qcom,msm8226-rpmpd", .data = &msm8226_desc },
|
||||
{ .compatible = "qcom,msm8909-rpmpd", .data = &msm8916_desc },
|
||||
{ .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc },
|
||||
{ .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc },
|
||||
{ .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc },
|
||||
|
@ -234,6 +234,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
|
||||
{ .compatible = "qcom,rpm-apq8084" },
|
||||
{ .compatible = "qcom,rpm-ipq6018" },
|
||||
{ .compatible = "qcom,rpm-msm8226" },
|
||||
{ .compatible = "qcom,rpm-msm8909" },
|
||||
{ .compatible = "qcom,rpm-msm8916" },
|
||||
{ .compatible = "qcom,rpm-msm8936" },
|
||||
{ .compatible = "qcom,rpm-msm8953" },
|
||||
|
@ -119,6 +119,9 @@ struct smp2p_entry {
|
||||
* @out: pointer to the outbound smem item
|
||||
* @smem_items: ids of the two smem items
|
||||
* @valid_entries: already scanned inbound entries
|
||||
* @ssr_ack_enabled: SMP2P_FEATURE_SSR_ACK feature is supported and was enabled
|
||||
* @ssr_ack: current cached state of the local ack bit
|
||||
* @negotiation_done: whether negotiating finished
|
||||
* @local_pid: processor id of the inbound edge
|
||||
* @remote_pid: processor id of the outbound edge
|
||||
* @ipc_regmap: regmap for the outbound ipc
|
||||
|
@ -328,10 +328,12 @@ static const struct soc_id soc_id[] = {
|
||||
{ 455, "QRB5165" },
|
||||
{ 457, "SM8450" },
|
||||
{ 459, "SM7225" },
|
||||
{ 460, "SA8540P" },
|
||||
{ 460, "SA8295P" },
|
||||
{ 461, "SA8540P" },
|
||||
{ 480, "SM8450" },
|
||||
{ 482, "SM8450" },
|
||||
{ 487, "SC7280" },
|
||||
{ 495, "SC7180P" },
|
||||
};
|
||||
|
||||
static const char *socinfo_machine(struct device *dev, unsigned int id)
|
||||
|
@ -74,6 +74,18 @@ static const u16 spm_reg_offset_v3_0[SPM_REG_NR] = {
|
||||
[SPM_REG_SEQ_ENTRY] = 0x400,
|
||||
};
|
||||
|
||||
/* SPM register data for 8909 */
|
||||
static const struct spm_reg_data spm_reg_8909_cpu = {
|
||||
.reg_offset = spm_reg_offset_v3_0,
|
||||
.spm_cfg = 0x1,
|
||||
.spm_dly = 0x3C102800,
|
||||
.seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
|
||||
0x5B, 0x60, 0x03, 0x60, 0x76, 0x76, 0x0B, 0x94, 0x5B, 0x80,
|
||||
0x10, 0x26, 0x30, 0x0F },
|
||||
.start_index[PM_SLEEP_MODE_STBY] = 0,
|
||||
.start_index[PM_SLEEP_MODE_SPC] = 5,
|
||||
};
|
||||
|
||||
/* SPM register data for 8916 */
|
||||
static const struct spm_reg_data spm_reg_8916_cpu = {
|
||||
.reg_offset = spm_reg_offset_v3_0,
|
||||
@ -195,6 +207,8 @@ static const struct of_device_id spm_match_table[] = {
|
||||
.data = &spm_reg_660_silver_l2 },
|
||||
{ .compatible = "qcom,msm8226-saw2-v2.1-cpu",
|
||||
.data = &spm_reg_8226_cpu },
|
||||
{ .compatible = "qcom,msm8909-saw2-v3.0-cpu",
|
||||
.data = &spm_reg_8909_cpu },
|
||||
{ .compatible = "qcom,msm8916-saw2-v3.0-cpu",
|
||||
.data = &spm_reg_8916_cpu },
|
||||
{ .compatible = "qcom,msm8974-saw2-v2.1-cpu",
|
||||
|
@ -57,11 +57,11 @@ static struct rcar_gen4_sysc_area r8a779a0_areas[] __initdata = {
|
||||
{ "a2cv6", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR },
|
||||
{ "a2cn2", R8A779A0_PD_A2CN2, R8A779A0_PD_A3IR },
|
||||
{ "a2imp23", R8A779A0_PD_A2IMP23, R8A779A0_PD_A3IR },
|
||||
{ "a2dp1", R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR },
|
||||
{ "a2cv2", R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR },
|
||||
{ "a2cv3", R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR },
|
||||
{ "a2cv5", R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR },
|
||||
{ "a2cv7", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR },
|
||||
{ "a2dp1", R8A779A0_PD_A2DP1, R8A779A0_PD_A3IR },
|
||||
{ "a2cv2", R8A779A0_PD_A2CV2, R8A779A0_PD_A3IR },
|
||||
{ "a2cv3", R8A779A0_PD_A2CV3, R8A779A0_PD_A3IR },
|
||||
{ "a2cv5", R8A779A0_PD_A2CV5, R8A779A0_PD_A3IR },
|
||||
{ "a2cv7", R8A779A0_PD_A2CV7, R8A779A0_PD_A3IR },
|
||||
{ "a2cn1", R8A779A0_PD_A2CN1, R8A779A0_PD_A3IR },
|
||||
{ "a1cnn0", R8A779A0_PD_A1CNN0, R8A779A0_PD_A2CN0 },
|
||||
{ "a1cnn2", R8A779A0_PD_A1CNN2, R8A779A0_PD_A2CN2 },
|
||||
|
@ -25,8 +25,8 @@
|
||||
struct rcar_gen4_sysc_area {
|
||||
const char *name;
|
||||
u8 pdr; /* PDRn */
|
||||
int parent; /* -1 if none */
|
||||
unsigned int flags; /* See PD_* */
|
||||
s8 parent; /* -1 if none */
|
||||
u8 flags; /* See PD_* */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -31,8 +31,8 @@ struct rcar_sysc_area {
|
||||
u16 chan_offs; /* Offset of PWRSR register for this area */
|
||||
u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */
|
||||
u8 isr_bit; /* Bit in SYSCI*R */
|
||||
int parent; /* -1 if none */
|
||||
unsigned int flags; /* See PD_* */
|
||||
s8 parent; /* -1 if none */
|
||||
u8 flags; /* See PD_* */
|
||||
};
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
config SUNXI_MBUS
|
||||
bool
|
||||
default ARCH_SUNXI
|
||||
depends on ARM || ARM64
|
||||
help
|
||||
Say y to enable the fixups needed to support the Allwinner
|
||||
MBUS DMA quirks.
|
||||
|
@ -338,6 +338,7 @@ static const struct of_device_id pruss_of_match[] = {
|
||||
{ .compatible = "ti,am654-icssg", .data = &am65x_j721e_pruss_data, },
|
||||
{ .compatible = "ti,j721e-icssg", .data = &am65x_j721e_pruss_data, },
|
||||
{ .compatible = "ti,am642-icssg", .data = &am65x_j721e_pruss_data, },
|
||||
{ .compatible = "ti,am625-pruss", .data = &am65x_j721e_pruss_data, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pruss_of_match);
|
||||
|
@ -688,7 +688,7 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev)
|
||||
&m3_ipc->sd_fw_name);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n");
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for firmware loading completion in a thread so we
|
||||
|
@ -183,7 +183,7 @@ config SPI_BCM63XX
|
||||
|
||||
config SPI_BCM63XX_HSSPI
|
||||
tristate "Broadcom BCM63XX HS SPI controller driver"
|
||||
depends on BCM63XX || BMIPS_GENERIC || ARCH_BCM_63XX || COMPILE_TEST
|
||||
depends on BCM63XX || BMIPS_GENERIC || ARCH_BCMBCA || COMPILE_TEST
|
||||
help
|
||||
This enables support for the High Speed SPI controller present on
|
||||
newer Broadcom BCM63XX SoCs.
|
||||
|
@ -1099,8 +1099,8 @@ config SERIAL_TIMBERDALE
|
||||
config SERIAL_BCM63XX
|
||||
tristate "Broadcom BCM63xx/BCM33xx UART support"
|
||||
select SERIAL_CORE
|
||||
depends on ARCH_BCM4908 || ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC || COMPILE_TEST
|
||||
default ARCH_BCM4908 || ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC
|
||||
depends on ARCH_BCM4908 || ARCH_BCMBCA || BCM63XX || BMIPS_GENERIC || COMPILE_TEST
|
||||
default ARCH_BCM4908 || ARCH_BCMBCA || BCM63XX || BMIPS_GENERIC
|
||||
help
|
||||
This enables the driver for the onchip UART core found on
|
||||
the following chipsets:
|
||||
|
@ -164,10 +164,111 @@
|
||||
#define TEGRA234_CLK_PEX1_C5_CORE 225U
|
||||
/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC4_BASE */
|
||||
#define TEGRA234_CLK_PLLC4 237U
|
||||
/** @brief RX clock recovered from MGBE0 lane input */
|
||||
#define TEGRA234_CLK_MGBE0_RX_INPUT 248U
|
||||
/** @brief RX clock recovered from MGBE1 lane input */
|
||||
#define TEGRA234_CLK_MGBE1_RX_INPUT 249U
|
||||
/** @brief RX clock recovered from MGBE2 lane input */
|
||||
#define TEGRA234_CLK_MGBE2_RX_INPUT 250U
|
||||
/** @brief RX clock recovered from MGBE3 lane input */
|
||||
#define TEGRA234_CLK_MGBE3_RX_INPUT 251U
|
||||
/** @brief 32K input clock provided by PMIC */
|
||||
#define TEGRA234_CLK_CLK_32K 289U
|
||||
/** @brief Monitored branch of MBGE0 RX input clock */
|
||||
#define TEGRA234_CLK_MGBE0_RX_INPUT_M 357U
|
||||
/** @brief Monitored branch of MBGE1 RX input clock */
|
||||
#define TEGRA234_CLK_MGBE1_RX_INPUT_M 358U
|
||||
/** @brief Monitored branch of MBGE2 RX input clock */
|
||||
#define TEGRA234_CLK_MGBE2_RX_INPUT_M 359U
|
||||
/** @brief Monitored branch of MBGE3 RX input clock */
|
||||
#define TEGRA234_CLK_MGBE3_RX_INPUT_M 360U
|
||||
/** @brief Monitored branch of MGBE0 RX PCS mux output */
|
||||
#define TEGRA234_CLK_MGBE0_RX_PCS_M 361U
|
||||
/** @brief Monitored branch of MGBE1 RX PCS mux output */
|
||||
#define TEGRA234_CLK_MGBE1_RX_PCS_M 362U
|
||||
/** @brief Monitored branch of MGBE2 RX PCS mux output */
|
||||
#define TEGRA234_CLK_MGBE2_RX_PCS_M 363U
|
||||
/** @brief Monitored branch of MGBE3 RX PCS mux output */
|
||||
#define TEGRA234_CLK_MGBE3_RX_PCS_M 364U
|
||||
/** @brief RX PCS clock recovered from MGBE0 lane input */
|
||||
#define TEGRA234_CLK_MGBE0_RX_PCS_INPUT 369U
|
||||
/** @brief RX PCS clock recovered from MGBE1 lane input */
|
||||
#define TEGRA234_CLK_MGBE1_RX_PCS_INPUT 370U
|
||||
/** @brief RX PCS clock recovered from MGBE2 lane input */
|
||||
#define TEGRA234_CLK_MGBE2_RX_PCS_INPUT 371U
|
||||
/** @brief RX PCS clock recovered from MGBE3 lane input */
|
||||
#define TEGRA234_CLK_MGBE3_RX_PCS_INPUT 372U
|
||||
/** @brief output of mux controlled by GBE_UPHY_MGBE0_RX_PCS_CLK_SRC_SEL */
|
||||
#define TEGRA234_CLK_MGBE0_RX_PCS 373U
|
||||
/** @brief GBE_UPHY_MGBE0_TX_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE0_TX 374U
|
||||
/** @brief GBE_UPHY_MGBE0_TX_PCS_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE0_TX_PCS 375U
|
||||
/** @brief GBE_UPHY_MGBE0_MAC_CLK divider output */
|
||||
#define TEGRA234_CLK_MGBE0_MAC_DIVIDER 376U
|
||||
/** @brief GBE_UPHY_MGBE0_MAC_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE0_MAC 377U
|
||||
/** @brief GBE_UPHY_MGBE0_MACSEC_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE0_MACSEC 378U
|
||||
/** @brief GBE_UPHY_MGBE0_EEE_PCS_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE0_EEE_PCS 379U
|
||||
/** @brief GBE_UPHY_MGBE0_APP_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE0_APP 380U
|
||||
/** @brief GBE_UPHY_MGBE0_PTP_REF_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE0_PTP_REF 381U
|
||||
/** @brief output of mux controlled by GBE_UPHY_MGBE1_RX_PCS_CLK_SRC_SEL */
|
||||
#define TEGRA234_CLK_MGBE1_RX_PCS 382U
|
||||
/** @brief GBE_UPHY_MGBE1_TX_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE1_TX 383U
|
||||
/** @brief GBE_UPHY_MGBE1_TX_PCS_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE1_TX_PCS 384U
|
||||
/** @brief GBE_UPHY_MGBE1_MAC_CLK divider output */
|
||||
#define TEGRA234_CLK_MGBE1_MAC_DIVIDER 385U
|
||||
/** @brief GBE_UPHY_MGBE1_MAC_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE1_MAC 386U
|
||||
/** @brief GBE_UPHY_MGBE1_EEE_PCS_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE1_EEE_PCS 388U
|
||||
/** @brief GBE_UPHY_MGBE1_APP_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE1_APP 389U
|
||||
/** @brief GBE_UPHY_MGBE1_PTP_REF_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE1_PTP_REF 390U
|
||||
/** @brief output of mux controlled by GBE_UPHY_MGBE2_RX_PCS_CLK_SRC_SEL */
|
||||
#define TEGRA234_CLK_MGBE2_RX_PCS 391U
|
||||
/** @brief GBE_UPHY_MGBE2_TX_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE2_TX 392U
|
||||
/** @brief GBE_UPHY_MGBE2_TX_PCS_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE2_TX_PCS 393U
|
||||
/** @brief GBE_UPHY_MGBE2_MAC_CLK divider output */
|
||||
#define TEGRA234_CLK_MGBE2_MAC_DIVIDER 394U
|
||||
/** @brief GBE_UPHY_MGBE2_MAC_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE2_MAC 395U
|
||||
/** @brief GBE_UPHY_MGBE2_EEE_PCS_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE2_EEE_PCS 397U
|
||||
/** @brief GBE_UPHY_MGBE2_APP_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE2_APP 398U
|
||||
/** @brief GBE_UPHY_MGBE2_PTP_REF_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE2_PTP_REF 399U
|
||||
/** @brief output of mux controlled by GBE_UPHY_MGBE3_RX_PCS_CLK_SRC_SEL */
|
||||
#define TEGRA234_CLK_MGBE3_RX_PCS 400U
|
||||
/** @brief GBE_UPHY_MGBE3_TX_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE3_TX 401U
|
||||
/** @brief GBE_UPHY_MGBE3_TX_PCS_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE3_TX_PCS 402U
|
||||
/** @brief GBE_UPHY_MGBE3_MAC_CLK divider output */
|
||||
#define TEGRA234_CLK_MGBE3_MAC_DIVIDER 403U
|
||||
/** @brief GBE_UPHY_MGBE3_MAC_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE3_MAC 404U
|
||||
/** @brief GBE_UPHY_MGBE3_MACSEC_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE3_MACSEC 405U
|
||||
/** @brief GBE_UPHY_MGBE3_EEE_PCS_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE3_EEE_PCS 406U
|
||||
/** @brief GBE_UPHY_MGBE3_APP_CLK gate output */
|
||||
#define TEGRA234_CLK_MGBE3_APP 407U
|
||||
/** @brief GBE_UPHY_MGBE3_PTP_REF_CLK divider gated output */
|
||||
#define TEGRA234_CLK_MGBE3_PTP_REF 408U
|
||||
/** @brief CLK_RST_CONTROLLER_AZA2XBITCLK_OUT_SWITCH_DIVIDER switch divider output (aza_2xbitclk) */
|
||||
#define TEGRA234_CLK_AZA_2XBIT 457U
|
||||
/** @brief aza_2xbitclk / 2 (aza_bitclk) */
|
||||
#define TEGRA234_CLK_AZA_BIT 458U
|
||||
|
||||
#endif
|
||||
|
@ -11,11 +11,16 @@
|
||||
/* NISO0 stream IDs */
|
||||
#define TEGRA234_SID_APE 0x02
|
||||
#define TEGRA234_SID_HDA 0x03
|
||||
#define TEGRA234_SID_GPCDMA 0x04
|
||||
#define TEGRA234_SID_MGBE 0x06
|
||||
#define TEGRA234_SID_PCIE0 0x12
|
||||
#define TEGRA234_SID_PCIE4 0x13
|
||||
#define TEGRA234_SID_PCIE5 0x14
|
||||
#define TEGRA234_SID_PCIE6 0x15
|
||||
#define TEGRA234_SID_PCIE9 0x1f
|
||||
#define TEGRA234_SID_MGBE_VF1 0x49
|
||||
#define TEGRA234_SID_MGBE_VF2 0x4a
|
||||
#define TEGRA234_SID_MGBE_VF3 0x4b
|
||||
|
||||
/* NISO1 stream IDs */
|
||||
#define TEGRA234_SID_SDMMC4 0x02
|
||||
@ -61,8 +66,24 @@
|
||||
#define TEGRA234_MEMORY_CLIENT_PCIE10AR1 0x48
|
||||
/* PCIE7r1 read clients */
|
||||
#define TEGRA234_MEMORY_CLIENT_PCIE7AR1 0x49
|
||||
/* MGBE0 read client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBEARD 0x58
|
||||
/* MGBEB read client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBEBRD 0x59
|
||||
/* MGBEC read client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBECRD 0x5a
|
||||
/* MGBED read client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBEDRD 0x5b
|
||||
/* MGBE0 write client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBEAWR 0x5c
|
||||
/* MGBEB write client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBEBWR 0x5f
|
||||
/* MGBEC write client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBECWR 0x61
|
||||
/* sdmmcd memory read client */
|
||||
#define TEGRA234_MEMORY_CLIENT_SDMMCRAB 0x63
|
||||
/* MGBED write client */
|
||||
#define TEGRA234_MEMORY_CLIENT_MGBEDWR 0x65
|
||||
/* sdmmcd memory write client */
|
||||
#define TEGRA234_MEMORY_CLIENT_SDMMCWAB 0x67
|
||||
/* BPMP read client */
|
||||
|
16
include/dt-bindings/power/mt6795-power.h
Normal file
16
include/dt-bindings/power/mt6795-power.h
Normal file
@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
#ifndef _DT_BINDINGS_POWER_MT6795_POWER_H
|
||||
#define _DT_BINDINGS_POWER_MT6795_POWER_H
|
||||
|
||||
#define MT6795_POWER_DOMAIN_MM 0
|
||||
#define MT6795_POWER_DOMAIN_VDEC 1
|
||||
#define MT6795_POWER_DOMAIN_VENC 2
|
||||
#define MT6795_POWER_DOMAIN_ISP 3
|
||||
#define MT6795_POWER_DOMAIN_MJC 4
|
||||
#define MT6795_POWER_DOMAIN_AUDIO 5
|
||||
#define MT6795_POWER_DOMAIN_MFG_ASYNC 6
|
||||
#define MT6795_POWER_DOMAIN_MFG_2D 7
|
||||
#define MT6795_POWER_DOMAIN_MFG 8
|
||||
#define MT6795_POWER_DOMAIN_MODEM 9
|
||||
|
||||
#endif /* _DT_BINDINGS_POWER_MT6795_POWER_H */
|
@ -187,6 +187,13 @@
|
||||
#define MSM8916_VDDMX 3
|
||||
#define MSM8916_VDDMX_AO 4
|
||||
|
||||
/* MSM8909 Power Domain Indexes */
|
||||
#define MSM8909_VDDCX MSM8916_VDDCX
|
||||
#define MSM8909_VDDCX_AO MSM8916_VDDCX_AO
|
||||
#define MSM8909_VDDCX_VFC MSM8916_VDDCX_VFC
|
||||
#define MSM8909_VDDMX MSM8916_VDDMX
|
||||
#define MSM8909_VDDMX_AO MSM8916_VDDMX_AO
|
||||
|
||||
/* MSM8953 Power Domain Indexes */
|
||||
#define MSM8953_VDDMD 0
|
||||
#define MSM8953_VDDMD_AO 1
|
||||
|
@ -18,5 +18,6 @@
|
||||
#define TEGRA234_POWER_DOMAIN_MGBEA 17U
|
||||
#define TEGRA234_POWER_DOMAIN_MGBEB 18U
|
||||
#define TEGRA234_POWER_DOMAIN_MGBEC 19U
|
||||
#define TEGRA234_POWER_DOMAIN_MGBED 20U
|
||||
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user