mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 12:14:10 +08:00
ARM: driver changes for 5.19
There are minor updates to SoC specific drivers for chips by Rockchip, Samsung, NVIDIA, TI, NXP, i.MX, Qualcomm, and Broadcom. Noteworthy driver changes include: - Several conversions of DT bindings to yaml format. - Renesas adds driver support for R-Car V4H, RZ/V2M and RZ/G2UL SoCs. - Qualcomm adds a bus driver for the SSC (Snapdragon Sensor Core), and support for more chips in the RPMh power domains and the soc-id. - NXP has a new driver for the HDMI blk-ctrl on i.MX8MP. - Apple M1 gains support for the on-chip NVMe controller, making it possible to finally use the internal disks. This also includes SoC drivers for their RTKit IPC and for the SART DMA address filter. For other subsystems that merge their drivers through the SoC tree, we have - Firmware drivers for the ARM firmware stack including TEE, OP-TEE, SCMI and FF-A get a number of smaller updates and cleanups. OP-TEE now has a cache for firmware argument structures as an optimization, and SCMI now supports the 3.1 version of the specification. - Reset controller updates to Amlogic, ASpeed, Renesas and ACPI drivers - Memory controller updates for Tegra, and a few updates for other platforms. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmKOXOoACgkQmmx57+YA GNlpVQ//eQGfL0WktE5G/y0mCVuVHtXT5nSjHMgjTOdb9+QvaATCfxnLXvP7Gq7C 7YzJd68G+2ZC4rUkkjTxyMICT7eIrJSAIAFn4PWee4EQ5DfbHgG+1tToTjxqb+QQ 6wGB5MVaYUhjZE30kY2E8a+OKxHtEnkt9wcch6ei0vzsMZquQJF6byfHd5+I4Knd CyDmXX8ZGXK3FnhvuBLr3Rgwyhs0X4Ju7UaONLZxBYxdnh8WmymRszmMnv5qEkub KDe8fbhFamOT3Z55JdCA5xq7LvUzjsKpTGFxFcS0ptbkTmtAsuyYqqiWvAPx3D5u 5TxVGSx9QKid6fpIsITZ2ptO6fgljh1W9b/3Y3/eltudXsM1qqSxyN2Hre+M9egf WEDADqbNR5Y5+bq1iZWI348jXkNHVPpsLHI9Ihqf4yyrKwFkmRmNLnws53XTAPH2 FPXZvJjwFDBDHGfewSoLFePXUPNytVLXbr6Mq72ZyTDIBDU8Mxh666Wd8bu4tgbG 1Y2pMjDIdXDOsljM6Of5D3XjM1kuDwEmFxWGy+cKLgoEbHLeE1xIbTjUir4687+d VNHdtsIRFPRZzz2lUSmI8vlA2aewMWrkOF/Ulz8xh6gG8uitMSfOxghg4IWOfRVM mlvgFP5eqTInmQcbWRxaRO9JzP+rPp1sAcEpsBmuEHw5Akflbc8= =XoLF -----END PGP SIGNATURE----- Merge tag 'arm-drivers-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull ARM driver updates from Arnd Bergmann: "There are minor updates to SoC specific drivers for chips by Rockchip, Samsung, NVIDIA, TI, NXP, i.MX, Qualcomm, and Broadcom. Noteworthy driver changes include: - Several conversions of DT bindings to yaml format. - Renesas adds driver support for R-Car V4H, RZ/V2M and RZ/G2UL SoCs. - Qualcomm adds a bus driver for the SSC (Snapdragon Sensor Core), and support for more chips in the RPMh power domains and the soc-id. - NXP has a new driver for the HDMI blk-ctrl on i.MX8MP. - Apple M1 gains support for the on-chip NVMe controller, making it possible to finally use the internal disks. This also includes SoC drivers for their RTKit IPC and for the SART DMA address filter. For other subsystems that merge their drivers through the SoC tree, we have - Firmware drivers for the ARM firmware stack including TEE, OP-TEE, SCMI and FF-A get a number of smaller updates and cleanups. OP-TEE now has a cache for firmware argument structures as an optimization, and SCMI now supports the 3.1 version of the specification. - Reset controller updates to Amlogic, ASpeed, Renesas and ACPI drivers - Memory controller updates for Tegra, and a few updates for other platforms" * tag 'arm-drivers-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (159 commits) memory: tegra: Add MC error logging on Tegra186 onward memory: tegra: Add memory controller channels support memory: tegra: Add APE memory clients for Tegra234 memory: tegra: Add Tegra234 support nvme-apple: fix sparse endianess warnings soc/tegra: pmc: Document core domain fields soc: qcom: pdr: use static for servreg_* variables soc: imx: fix semicolon.cocci warnings soc: renesas: R-Car V3U is R-Car Gen4 soc: imx: add i.MX8MP HDMI blk-ctrl soc: imx: imx8m-blk-ctrl: Add i.MX8MP media blk-ctrl soc: imx: add i.MX8MP HSIO blk-ctrl soc: imx: imx8m-blk-ctrl: set power device name soc: qcom: llcc: Add sc8180x and sc8280xp configurations dt-bindings: arm: msm: Add sc8180x and sc8280xp LLCC compatibles soc/tegra: pmc: Select REGMAP dt-bindings: reset: st,sti-powerdown: Convert to yaml dt-bindings: reset: st,sti-picophyreset: Convert to yaml dt-bindings: reset: socfpga: Convert to yaml dt-bindings: reset: snps,axs10x-reset: Convert to yaml ...
This commit is contained in:
commit
cc3c470ae4
@ -23,6 +23,8 @@ properties:
|
||||
enum:
|
||||
- qcom,sc7180-llcc
|
||||
- qcom,sc7280-llcc
|
||||
- qcom,sc8180x-llcc
|
||||
- qcom,sc8280xp-llcc
|
||||
- qcom,sdm845-llcc
|
||||
- qcom,sm6350-llcc
|
||||
- qcom,sm8150-llcc
|
||||
|
@ -39,8 +39,11 @@ description: |
|
||||
msm8994
|
||||
msm8996
|
||||
sa8155p
|
||||
sa8540p
|
||||
sc7180
|
||||
sc7280
|
||||
sc8180x
|
||||
sc8280xp
|
||||
sdm630
|
||||
sdm632
|
||||
sdm660
|
||||
@ -226,6 +229,18 @@ properties:
|
||||
- google,senor
|
||||
- const: qcom,sc7280
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- lenovo,flex-5g
|
||||
- microsoft,surface-prox
|
||||
- qcom,sc8180x-primus
|
||||
- const: qcom,sc8180x
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sc8280xp-qrd
|
||||
- const: qcom,sc8280xp
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- fairphone,fp3
|
||||
@ -259,6 +274,11 @@ properties:
|
||||
- qcom,sa8155p-adp
|
||||
- const: qcom,sa8155p
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sa8295p-adp
|
||||
- const: qcom,sa8540p
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- fairphone,fp4
|
||||
|
147
Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml
Normal file
147
Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml
Normal file
@ -0,0 +1,147 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/bus/qcom,ssc-block-bus.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: The AHB Bus Providing a Global View of the SSC Block on (some) qcom SoCs
|
||||
|
||||
maintainers:
|
||||
- Michael Srba <Michael.Srba@seznam.cz>
|
||||
|
||||
description: |
|
||||
This binding describes the dependencies (clocks, resets, power domains) which
|
||||
need to be turned on in a sequence before communication over the AHB bus
|
||||
becomes possible.
|
||||
|
||||
Additionally, the reg property is used to pass to the driver the location of
|
||||
two sadly undocumented registers which need to be poked as part of the sequence.
|
||||
|
||||
The SSC (Snapdragon Sensor Core) block contains a gpio controller, i2c/spi/uart
|
||||
controllers, a hexagon core, and a clock controller which provides clocks for
|
||||
the above.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: qcom,msm8998-ssc-block-bus
|
||||
- const: qcom,ssc-block-bus
|
||||
|
||||
reg:
|
||||
description: |
|
||||
Shall contain the addresses of the SSCAON_CONFIG0 and SSCAON_CONFIG1
|
||||
registers
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: mpm_sscaon_config0
|
||||
- const: mpm_sscaon_config1
|
||||
|
||||
'#address-cells':
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
'#size-cells':
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
ranges: true
|
||||
|
||||
clocks:
|
||||
minItems: 6
|
||||
maxItems: 6
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: xo
|
||||
- const: aggre2
|
||||
- const: gcc_im_sleep
|
||||
- const: aggre2_north
|
||||
- const: ssc_xo
|
||||
- const: ssc_ahbs
|
||||
|
||||
power-domains:
|
||||
description: Power domain phandles for the ssc_cx and ssc_mx power domains
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: ssc_cx
|
||||
- const: ssc_mx
|
||||
|
||||
resets:
|
||||
description: |
|
||||
Reset phandles for the ssc_reset and ssc_bcr resets (note: ssc_bcr is the
|
||||
branch control register associated with the ssc_xo and ssc_ahbs clocks)
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: ssc_reset
|
||||
- const: ssc_bcr
|
||||
|
||||
qcom,halt-regs:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: describes how to locate the ssc AXI halt register
|
||||
items:
|
||||
- items:
|
||||
- description: Phandle reference to a syscon representing TCSR
|
||||
- description: offset for the ssc AXI halt register
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
- ranges
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
- resets
|
||||
- reset-names
|
||||
- qcom,halt-regs
|
||||
|
||||
additionalProperties:
|
||||
type: object
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-msm8998.h>
|
||||
#include <dt-bindings/clock/qcom,rpmcc.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
// devices under this node are physically located in the SSC block, connected to an ssc-internal bus;
|
||||
ssc_ahb_slave: bus@10ac008 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
compatible = "qcom,msm8998-ssc-block-bus", "qcom,ssc-block-bus";
|
||||
reg = <0x10ac008 0x4>, <0x10ac010 0x4>;
|
||||
reg-names = "mpm_sscaon_config0", "mpm_sscaon_config1";
|
||||
|
||||
clocks = <&xo>,
|
||||
<&rpmcc RPM_SMD_AGGR2_NOC_CLK>,
|
||||
<&gcc GCC_IM_SLEEP>,
|
||||
<&gcc AGGRE2_SNOC_NORTH_AXI>,
|
||||
<&gcc SSC_XO>,
|
||||
<&gcc SSC_CNOC_AHBS_CLK>;
|
||||
clock-names = "xo", "aggre2", "gcc_im_sleep", "aggre2_north", "ssc_xo", "ssc_ahbs";
|
||||
|
||||
resets = <&gcc GCC_SSC_RESET>, <&gcc GCC_SSC_BCR>;
|
||||
reset-names = "ssc_reset", "ssc_bcr";
|
||||
|
||||
power-domains = <&rpmpd MSM8998_SSCCX>, <&rpmpd MSM8998_SSCMX>;
|
||||
power-domain-names = "ssc_cx", "ssc_mx";
|
||||
|
||||
qcom,halt-regs = <&tcsr_mutex_regs 0x26000>;
|
||||
};
|
||||
};
|
@ -19,6 +19,7 @@ Required properties:
|
||||
* "qcom,scm-msm8953"
|
||||
* "qcom,scm-msm8960"
|
||||
* "qcom,scm-msm8974"
|
||||
* "qcom,scm-msm8976"
|
||||
* "qcom,scm-msm8994"
|
||||
* "qcom,scm-msm8996"
|
||||
* "qcom,scm-msm8998"
|
||||
@ -37,7 +38,7 @@ Required properties:
|
||||
* core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660" and
|
||||
"qcom,scm-msm8960"
|
||||
* core, iface and bus clocks required for "qcom,scm-apq8084",
|
||||
"qcom,scm-msm8916", "qcom,scm-msm8953" and "qcom,scm-msm8974"
|
||||
"qcom,scm-msm8916", "qcom,scm-msm8953", "qcom,scm-msm8974" and "qcom,scm-msm8976"
|
||||
- clock-names: Must contain "core" for the core clock, "iface" for the interface
|
||||
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
|
||||
|
@ -45,20 +45,20 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
# Example 1: apps bcm_voter on SDM845 SoC should be defined inside &apps_rsc node
|
||||
# as defined in Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
|
||||
# as defined in Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml
|
||||
- |
|
||||
|
||||
apps_bcm_voter: bcm_voter {
|
||||
apps_bcm_voter: bcm-voter {
|
||||
compatible = "qcom,bcm-voter";
|
||||
};
|
||||
|
||||
# Example 2: disp bcm_voter on SDM845 should be defined inside &disp_rsc node
|
||||
# as defined in Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
|
||||
# as defined in Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml
|
||||
- |
|
||||
|
||||
#include <dt-bindings/interconnect/qcom,icc.h>
|
||||
|
||||
disp_bcm_voter: bcm_voter {
|
||||
disp_bcm_voter: bcm-voter {
|
||||
compatible = "qcom,bcm-voter";
|
||||
qcom,tcs-wait = <QCOM_ICC_TAG_AMC>;
|
||||
};
|
||||
|
52
Documentation/devicetree/bindings/iommu/apple,sart.yaml
Normal file
52
Documentation/devicetree/bindings/iommu/apple,sart.yaml
Normal file
@ -0,0 +1,52 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iommu/apple,sart.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple SART DMA address filter
|
||||
|
||||
maintainers:
|
||||
- Sven Peter <sven@svenpeter.dev>
|
||||
|
||||
description:
|
||||
Apple SART is a simple address filter for DMA transactions. Regions of
|
||||
physical memory must be added to the SART's allow list before any
|
||||
DMA can target these. Unlike a proper IOMMU no remapping can be done and
|
||||
special support in the consumer driver is required since not all DMA
|
||||
transactions of a single device are subject to SART filtering.
|
||||
|
||||
SART1 has first been used since at least the A11 (iPhone 8 and iPhone X)
|
||||
and allows 36 bit of physical address space and filter entries with sizes
|
||||
up to 24 bit.
|
||||
|
||||
SART2, first seen in A14 and M1, allows 36 bit of physical address space
|
||||
and filter entry size up to 36 bit.
|
||||
|
||||
SART3, first seen in M1 Pro/Max, extends both the address space and filter
|
||||
entry size to 42 bit.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- apple,t6000-sart
|
||||
- apple,t8103-sart
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
iommu@7bc50000 {
|
||||
compatible = "apple,t8103-sart";
|
||||
reg = <0x7bc50000 0x4000>;
|
||||
};
|
@ -31,8 +31,13 @@ properties:
|
||||
- renesas,r8a774b1-rpc-if # RZ/G2N
|
||||
- renesas,r8a774c0-rpc-if # RZ/G2E
|
||||
- renesas,r8a774e1-rpc-if # RZ/G2H
|
||||
- renesas,r8a7795-rpc-if # R-Car H3
|
||||
- renesas,r8a7796-rpc-if # R-Car M3-W
|
||||
- renesas,r8a77961-rpc-if # R-Car M3-W+
|
||||
- renesas,r8a77965-rpc-if # R-Car M3-N
|
||||
- renesas,r8a77970-rpc-if # R-Car V3M
|
||||
- renesas,r8a77980-rpc-if # R-Car V3H
|
||||
- renesas,r8a77990-rpc-if # R-Car E3
|
||||
- renesas,r8a77995-rpc-if # R-Car D3
|
||||
- renesas,r8a779a0-rpc-if # R-Car V3U
|
||||
- const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2{E,H,M,N} device
|
||||
|
111
Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml
Normal file
111
Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml
Normal file
@ -0,0 +1,111 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/nvme/apple,nvme-ans.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple ANS NVM Express host controller
|
||||
|
||||
maintainers:
|
||||
- Sven Peter <sven@svenpeter.dev>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t8103-nvme-ans2
|
||||
- apple,t6000-nvme-ans2
|
||||
- const: apple,nvme-ans2
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: NVMe and NVMMU registers
|
||||
- description: ANS2 co-processor control registers
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: nvme
|
||||
- const: ans
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
# two domains for t8103, three for t6000
|
||||
minItems: 2
|
||||
items:
|
||||
- description: power domain for the NVMe controller.
|
||||
- description: power domain for the first PCIe bus connecting the NVMe
|
||||
controller to the storage modules.
|
||||
- description: optional power domain for the second PCIe bus
|
||||
connecting the NVMe controller to the storage modules.
|
||||
|
||||
power-domain-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: ans
|
||||
- const: apcie0
|
||||
- const: apcie1
|
||||
|
||||
mboxes:
|
||||
maxItems: 1
|
||||
description: Mailbox of the ANS2 co-processor
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
apple,sart:
|
||||
maxItems: 1
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: |
|
||||
Reference to the SART address filter.
|
||||
|
||||
The SART address filter is documented in iommu/apple,sart.yaml.
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: apple,t8103-nvme-ans2
|
||||
then:
|
||||
properties:
|
||||
power-domains:
|
||||
maxItems: 2
|
||||
power-domain-names:
|
||||
maxItems: 2
|
||||
else:
|
||||
properties:
|
||||
power-domains:
|
||||
minItems: 3
|
||||
power-domain-names:
|
||||
minItems: 3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- resets
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
- mboxes
|
||||
- interrupts
|
||||
- apple,sart
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/apple-aic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
nvme@7bcc0000 {
|
||||
compatible = "apple,t8103-nvme-ans2", "apple,nvme-ans2";
|
||||
reg = <0x7bcc0000 0x40000>, <0x77400000 0x4000>;
|
||||
reg-names = "nvme", "ans";
|
||||
interrupts = <AIC_IRQ 590 IRQ_TYPE_LEVEL_HIGH>;
|
||||
mboxes = <&ans>;
|
||||
apple,sart = <&sart>;
|
||||
power-domains = <&ps_ans2>, <&ps_apcie_st>;
|
||||
power-domain-names = "ans", "apcie0";
|
||||
resets = <&ps_ans2>;
|
||||
};
|
@ -27,12 +27,15 @@ properties:
|
||||
- qcom,msm8998-rpmpd
|
||||
- qcom,qcm2290-rpmpd
|
||||
- qcom,qcs404-rpmpd
|
||||
- qcom,sa8540p-rpmhpd
|
||||
- qcom,sdm660-rpmpd
|
||||
- qcom,sc7180-rpmhpd
|
||||
- qcom,sc7280-rpmhpd
|
||||
- qcom,sc8180x-rpmhpd
|
||||
- qcom,sc8280xp-rpmhpd
|
||||
- qcom,sdm845-rpmhpd
|
||||
- qcom,sdx55-rpmhpd
|
||||
- qcom,sdx65-rpmhpd
|
||||
- qcom,sm6115-rpmpd
|
||||
- qcom,sm6125-rpmpd
|
||||
- qcom,sm6350-rpmhpd
|
||||
|
@ -12,7 +12,7 @@ description:
|
||||
resides as a subnode of the SMD. As such, the SMD-RPM regulator requires
|
||||
that the SMD and RPM nodes be present.
|
||||
|
||||
Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt for
|
||||
Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml for
|
||||
information pertaining to the SMD node.
|
||||
|
||||
Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml
|
||||
@ -69,7 +69,8 @@ description:
|
||||
l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22
|
||||
|
||||
maintainers:
|
||||
- Kathiravan T <kathirav@codeaurora.org>
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -250,7 +250,7 @@ the memory regions used by the Hexagon firmware. Each sub-node must contain:
|
||||
|
||||
The Hexagon node may also have an subnode named either "smd-edge" or
|
||||
"glink-edge" that describes the communication edge, channels and devices
|
||||
related to the Hexagon. See ../soc/qcom/qcom,smd.txt and
|
||||
related to the Hexagon. See ../soc/qcom/qcom,smd.yaml and
|
||||
../soc/qcom/qcom,glink.txt for details on how to describe these.
|
||||
|
||||
= EXAMPLE
|
||||
|
@ -111,7 +111,7 @@ and its resource dependencies. It is described by the following properties:
|
||||
|
||||
The wcnss node can also have an subnode named "smd-edge" that describes the SMD
|
||||
edge, channels and devices related to the WCNSS.
|
||||
See ../soc/qcom/qcom,smd.txt for details on how to describe the SMD edge.
|
||||
See ../soc/qcom/qcom,smd.yaml for details on how to describe the SMD edge.
|
||||
|
||||
= EXAMPLE
|
||||
The following example describes the resources needed to boot control the WCNSS,
|
||||
|
47
Documentation/devicetree/bindings/reset/altr,rst-mgr.yaml
Normal file
47
Documentation/devicetree/bindings/reset/altr,rst-mgr.yaml
Normal file
@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/altr,rst-mgr.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Altera SOCFPGA Reset Manager
|
||||
|
||||
maintainers:
|
||||
- Dinh Nguyen <dinguyen@altera.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- description: Cyclone5/Arria5/Arria10
|
||||
const: altr,rst-mgr
|
||||
- description: Stratix10 ARM64 SoC
|
||||
items:
|
||||
- const: altr,stratix10-rst-mgr
|
||||
- const: altr,rst-mgr
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
altr,modrst-offset:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Offset of the first modrst register
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- altr,modrst-offset
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
rstmgr@ffd05000 {
|
||||
compatible = "altr,rst-mgr";
|
||||
reg = <0xffd05000 0x1000>;
|
||||
altr,modrst-offset = <0x10>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
* Amlogic audio memory arbiter controller
|
||||
|
||||
The Amlogic Audio ARB is a simple device which enables or
|
||||
disables the access of Audio FIFOs to DDR on AXG based SoC.
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,meson-axg-audio-arb' or
|
||||
'amlogic,meson-sm1-audio-arb'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- clocks: phandle to the fifo peripheral clock provided by the audio
|
||||
clock controller.
|
||||
- #reset-cells: must be 1.
|
||||
|
||||
Example on the A113 SoC:
|
||||
|
||||
arb: reset-controller@280 {
|
||||
compatible = "amlogic,meson-axg-audio-arb";
|
||||
reg = <0x0 0x280 0x0 0x4>;
|
||||
#reset-cells = <1>;
|
||||
clocks = <&clkc_audio AUD_CLKID_DDR_ARB>;
|
||||
};
|
@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2019 BayLibre, SAS
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/reset/amlogic,meson-axg-audio-arb.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Amlogic audio memory arbiter controller
|
||||
|
||||
maintainers:
|
||||
- Jerome Brunet <jbrunet@baylibre.com>
|
||||
|
||||
description: The Amlogic Audio ARB is a simple device which enables or disables
|
||||
the access of Audio FIFOs to DDR on AXG based SoC.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-axg-audio-arb
|
||||
- amlogic,meson-sm1-audio-arb
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: |
|
||||
phandle to the fifo peripheral clock provided by the audio clock
|
||||
controller.
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- "#reset-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
// on the A113 SoC:
|
||||
#include <dt-bindings/clock/axg-audio-clkc.h>
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
arb: reset-controller@280 {
|
||||
compatible = "amlogic,meson-axg-audio-arb";
|
||||
reg = <0x0 0x280 0x0 0x4>;
|
||||
#reset-cells = <1>;
|
||||
clocks = <&clkc_audio AUD_CLKID_DDR_ARB>;
|
||||
};
|
||||
};
|
@ -17,6 +17,7 @@ properties:
|
||||
- amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs
|
||||
- amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs
|
||||
- amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs
|
||||
- amlogic,meson-s4-reset # Reset Controller on S4 and compatible SoCs
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -1,20 +0,0 @@
|
||||
Binding for Qualcomm Atheros AR7xxx/AR9XXX reset controller
|
||||
|
||||
Please also refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
Required Properties:
|
||||
- compatible: has to be "qca,<soctype>-reset", "qca,ar7100-reset"
|
||||
as fallback
|
||||
- reg: Base address and size of the controllers memory area
|
||||
- #reset-cells : Specifies the number of cells needed to encode reset
|
||||
line, should be 1
|
||||
|
||||
Example:
|
||||
|
||||
reset-controller@1806001c {
|
||||
compatible = "qca,ar9132-reset", "qca,ar7100-reset";
|
||||
reg = <0x1806001c 0x4>;
|
||||
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -1,23 +0,0 @@
|
||||
Marvell Berlin reset controller
|
||||
===============================
|
||||
|
||||
Please also refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
The reset controller node must be a sub-node of the chip controller
|
||||
node on Berlin SoCs.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "marvell,berlin2-reset"
|
||||
- #reset-cells: must be set to 2
|
||||
|
||||
Example:
|
||||
|
||||
chip_rst: reset {
|
||||
compatible = "marvell,berlin2-reset";
|
||||
#reset-cells = <2>;
|
||||
};
|
||||
|
||||
&usb_phy0 {
|
||||
resets = <&chip_rst 0x104 12>;
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
Bitmain BM1880 SoC Reset Controller
|
||||
===================================
|
||||
|
||||
Please also refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "bitmain,bm1880-reset"
|
||||
- reg: Offset and length of reset controller space in SCTRL.
|
||||
- #reset-cells: Must be 1.
|
||||
|
||||
Example:
|
||||
|
||||
rst: reset-controller@c00 {
|
||||
compatible = "bitmain,bm1880-reset";
|
||||
reg = <0xc00 0x8>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2019 Manivannan Sadhasivam <mani@kernel.org>
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/reset/bitmain,bm1880-reset.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Bitmain BM1880 SoC Reset Controller
|
||||
|
||||
maintainers:
|
||||
- Manivannan Sadhasivam <mani@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: bitmain,bm1880-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#reset-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
rst: reset-controller@c00 {
|
||||
compatible = "bitmain,bm1880-reset";
|
||||
reg = <0xc00 0x8>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -1,30 +0,0 @@
|
||||
Lantiq XWAY SoC RCU reset controller binding
|
||||
============================================
|
||||
|
||||
This binding describes a reset-controller found on the RCU module on Lantiq
|
||||
XWAY SoCs.
|
||||
|
||||
This node has to be a sub node of the Lantiq RCU block.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Required properties:
|
||||
- compatible : Should be one of
|
||||
"lantiq,danube-reset"
|
||||
"lantiq,xrx200-reset"
|
||||
- reg : Defines the following sets of registers in the parent
|
||||
syscon device
|
||||
- Offset of the reset set register
|
||||
- Offset of the reset status register
|
||||
- #reset-cells : Specifies the number of cells needed to encode the
|
||||
reset line, should be 2.
|
||||
The first cell takes the reset set bit and the
|
||||
second cell takes the status bit.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Example for the reset-controllers on the xRX200 SoCs:
|
||||
reset0: reset-controller@10 {
|
||||
compatible = "lantiq,xrx200-reset";
|
||||
reg <0x10 0x04>, <0x14 0x04>;
|
||||
|
||||
#reset-cells = <2>;
|
||||
};
|
49
Documentation/devicetree/bindings/reset/lantiq,reset.yaml
Normal file
49
Documentation/devicetree/bindings/reset/lantiq,reset.yaml
Normal file
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/lantiq,reset.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Lantiq XWAY SoC RCU reset controller
|
||||
|
||||
maintainers:
|
||||
- Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
|
||||
description: |
|
||||
This binding describes a reset-controller found on the RCU module on Lantiq
|
||||
XWAY SoCs. This node has to be a sub node of the Lantiq RCU block.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- lantiq,danube-reset
|
||||
- lantiq,xrx200-reset
|
||||
|
||||
reg:
|
||||
description: |
|
||||
Defines the following sets of registers in the parent syscon device
|
||||
Offset of the reset set register
|
||||
Offset of the reset status register
|
||||
maxItems: 2
|
||||
|
||||
'#reset-cells':
|
||||
description: |
|
||||
The first cell takes the reset set bit and the second cell takes the
|
||||
status bit.
|
||||
const: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
// On the xRX200 SoCs:
|
||||
reset0: reset-controller@10 {
|
||||
compatible = "lantiq,xrx200-reset";
|
||||
reg = <0x10 0x04>, <0x14 0x04>;
|
||||
#reset-cells = <2>;
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2015 Antoine Tenart <atenart@kernel.org>
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/reset/marvell,berlin2-reset.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Marvell Berlin reset controller
|
||||
|
||||
maintainers:
|
||||
- Antoine Tenart <atenart@kernel.org>
|
||||
|
||||
description: The reset controller node must be a sub-node of the chip
|
||||
controller node on Berlin SoCs.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: marvell,berlin2-reset
|
||||
|
||||
"#reset-cells":
|
||||
const: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#reset-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
chip: chip-control@ea0000 {
|
||||
reg = <0xea0000 0x400>;
|
||||
|
||||
chip_rst: reset {
|
||||
compatible = "marvell,berlin2-reset";
|
||||
#reset-cells = <2>;
|
||||
};
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
Nuvoton NPCM Reset controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "nuvoton,npcm750-reset" for NPCM7XX BMC
|
||||
- reg : specifies physical base address and size of the register.
|
||||
- #reset-cells: must be set to 2
|
||||
|
||||
Optional property:
|
||||
- nuvoton,sw-reset-number - Contains the software reset number to restart the SoC.
|
||||
NPCM7xx contain four software reset that represent numbers 1 to 4.
|
||||
|
||||
If 'nuvoton,sw-reset-number' is not specified software reset is disabled.
|
||||
|
||||
Example:
|
||||
rstc: rstc@f0801000 {
|
||||
compatible = "nuvoton,npcm750-reset";
|
||||
reg = <0xf0801000 0x70>;
|
||||
#reset-cells = <2>;
|
||||
nuvoton,sw-reset-number = <2>;
|
||||
};
|
||||
|
||||
Specifying reset lines connected to IP NPCM7XX modules
|
||||
======================================================
|
||||
example:
|
||||
|
||||
spi0: spi@..... {
|
||||
...
|
||||
resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>;
|
||||
...
|
||||
};
|
||||
|
||||
The index could be found in <dt-bindings/reset/nuvoton,npcm7xx-reset.h>.
|
@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/nuvoton,npcm750-reset.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Nuvoton NPCM Reset controller
|
||||
|
||||
maintainers:
|
||||
- Tomer Maimon <tmaimon77@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: nuvoton,npcm750-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#reset-cells':
|
||||
const: 2
|
||||
|
||||
nuvoton,sw-reset-number:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
description: |
|
||||
Contains the software reset number to restart the SoC.
|
||||
If not specified, software reset is disabled.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/reset/nuvoton,npcm7xx-reset.h>
|
||||
rstc: rstc@f0801000 {
|
||||
compatible = "nuvoton,npcm750-reset";
|
||||
reg = <0xf0801000 0x70>;
|
||||
#reset-cells = <2>;
|
||||
nuvoton,sw-reset-number = <2>;
|
||||
};
|
||||
|
||||
// Specifying reset lines connected to IP NPCM7XX modules
|
||||
spi0: spi {
|
||||
resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>;
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2015 Alban Bedel <albeu@free.fr>
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/reset/qca,ar7100-reset.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Qualcomm Atheros AR7xxx/AR9XXX reset controller
|
||||
|
||||
maintainers:
|
||||
- Alban Bedel <albeu@free.fr>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qca,ar9132-reset
|
||||
- qca,ar9331-reset
|
||||
- const: qca,ar7100-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#reset-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
reset-controller@1806001c {
|
||||
compatible = "qca,ar9132-reset", "qca,ar7100-reset";
|
||||
reg = <0x1806001c 0x4>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
Binding for the AXS10x reset controller
|
||||
|
||||
This binding describes the ARC AXS10x boards custom IP-block which allows
|
||||
to control reset signals of selected peripherals. For example DW GMAC, etc...
|
||||
This block is controlled via memory-mapped register (AKA CREG) which
|
||||
represents up-to 32 reset lines.
|
||||
|
||||
As of today only the following lines are used:
|
||||
- DW GMAC - line 5
|
||||
|
||||
This binding uses the common reset binding[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/reset/reset.txt
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "snps,axs10x-reset".
|
||||
- reg: should always contain pair address - length: for creg reset
|
||||
bits register.
|
||||
- #reset-cells: from common reset binding; Should always be set to 1.
|
||||
|
||||
Example:
|
||||
reset: reset-controller@11220 {
|
||||
compatible = "snps,axs10x-reset";
|
||||
#reset-cells = <1>;
|
||||
reg = <0x11220 0x4>;
|
||||
};
|
||||
|
||||
Specifying reset lines connected to IP modules:
|
||||
ethernet@.... {
|
||||
....
|
||||
resets = <&reset 5>;
|
||||
....
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/snps,axs10x-reset.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: AXS10x reset controller
|
||||
|
||||
maintainers:
|
||||
- Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
|
||||
|
||||
description: |
|
||||
This binding describes the ARC AXS10x boards custom IP-block which allows
|
||||
to control reset signals of selected peripherals. For example DW GMAC, etc...
|
||||
This block is controlled via memory-mapped register (AKA CREG) which
|
||||
represents up-to 32 reset lines.
|
||||
As of today only the following lines are used:
|
||||
- DW GMAC - line 5
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: snps,axs10x-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
reset: reset-controller@11220 {
|
||||
compatible = "snps,axs10x-reset";
|
||||
#reset-cells = <1>;
|
||||
reg = <0x11220 0x4>;
|
||||
};
|
||||
|
||||
// Specifying reset lines connected to IP modules:
|
||||
ethernet {
|
||||
resets = <&reset 5>;
|
||||
};
|
@ -1,16 +0,0 @@
|
||||
Altera SOCFPGA Reset Manager
|
||||
|
||||
Required properties:
|
||||
- compatible : "altr,rst-mgr" for (Cyclone5/Arria5/Arria10)
|
||||
"altr,stratix10-rst-mgr","altr,rst-mgr" for Stratix10 ARM64 SoC
|
||||
- reg : Should contain 1 register ranges(address and length)
|
||||
- altr,modrst-offset : Should contain the offset of the first modrst register.
|
||||
- #reset-cells: 1
|
||||
|
||||
Example:
|
||||
rstmgr@ffd05000 {
|
||||
#reset-cells = <1>;
|
||||
compatible = "altr,rst-mgr";
|
||||
reg = <0xffd05000 0x1000>;
|
||||
altr,modrst-offset = <0x10>;
|
||||
};
|
@ -38,25 +38,49 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
oneOf:
|
||||
- items: # for Pro4, Pro5
|
||||
- const: gio
|
||||
- const: link
|
||||
- items: # for others
|
||||
- const: link
|
||||
clock-names: true
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
oneOf:
|
||||
- items: # for Pro4, Pro5
|
||||
- const: gio
|
||||
- const: link
|
||||
- items: # for others
|
||||
- const: link
|
||||
reset-names: true
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- socionext,uniphier-pro4-usb3-reset
|
||||
- socionext,uniphier-pro5-usb3-reset
|
||||
- socionext,uniphier-pro4-ahci-reset
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: gio
|
||||
- const: link
|
||||
resets:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
reset-names:
|
||||
items:
|
||||
- const: gio
|
||||
- const: link
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
clock-names:
|
||||
const: link
|
||||
resets:
|
||||
maxItems: 1
|
||||
reset-names:
|
||||
const: link
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
STMicroelectronics STi family Sysconfig Picophy SoftReset Controller
|
||||
=============================================================================
|
||||
|
||||
This binding describes a reset controller device that is used to enable and
|
||||
disable on-chip PicoPHY USB2 phy(s) using "softreset" control bits found in
|
||||
the STi family SoC system configuration registers.
|
||||
|
||||
The actual action taken when softreset is asserted is hardware dependent.
|
||||
However, when asserted it may not be possible to access the hardware's
|
||||
registers and after an assert/deassert sequence the hardware's previous state
|
||||
may no longer be valid.
|
||||
|
||||
Please refer to Documentation/devicetree/bindings/reset/reset.txt
|
||||
for common reset controller binding usage.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "st,stih407-picophyreset"
|
||||
- #reset-cells: 1, see below
|
||||
|
||||
Example:
|
||||
|
||||
picophyreset: picophyreset-controller {
|
||||
compatible = "st,stih407-picophyreset";
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
Specifying picophyreset control of devices
|
||||
=======================================
|
||||
|
||||
Device nodes should specify the reset channel required in their "resets"
|
||||
property, containing a phandle to the picophyreset device node and an
|
||||
index specifying which channel to use, as described in
|
||||
Documentation/devicetree/bindings/reset/reset.txt.
|
||||
|
||||
Example:
|
||||
|
||||
usb2_picophy0: usbpicophy@0 {
|
||||
resets = <&picophyreset STIH407_PICOPHY0_RESET>;
|
||||
};
|
||||
|
||||
Macro definitions for the supported reset channels can be found in:
|
||||
include/dt-bindings/reset/stih407-resets.h
|
@ -1,45 +0,0 @@
|
||||
STMicroelectronics STi family Sysconfig Peripheral Powerdown Reset Controller
|
||||
=============================================================================
|
||||
|
||||
This binding describes a reset controller device that is used to enable and
|
||||
disable on-chip peripheral controllers such as USB and SATA, using
|
||||
"powerdown" control bits found in the STi family SoC system configuration
|
||||
registers. These have been grouped together into a single reset controller
|
||||
device for convenience.
|
||||
|
||||
The actual action taken when powerdown is asserted is hardware dependent.
|
||||
However, when asserted it may not be possible to access the hardware's
|
||||
registers and after an assert/deassert sequence the hardware's previous state
|
||||
may no longer be valid.
|
||||
|
||||
Please refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "st,stih407-powerdown"
|
||||
- #reset-cells: 1, see below
|
||||
|
||||
example:
|
||||
|
||||
powerdown: powerdown-controller {
|
||||
compatible = "st,stih407-powerdown";
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
|
||||
Specifying powerdown control of devices
|
||||
=======================================
|
||||
|
||||
Device nodes should specify the reset channel required in their "resets"
|
||||
property, containing a phandle to the powerdown device node and an
|
||||
index specifying which channel to use, as described in reset.txt
|
||||
|
||||
example:
|
||||
|
||||
st_dwc3: dwc3@8f94000 {
|
||||
resets = <&powerdown STIH407_USB3_POWERDOWN>,
|
||||
};
|
||||
|
||||
Macro definitions for the supported reset channels can be found in:
|
||||
|
||||
include/dt-bindings/reset/stih407-resets.h
|
@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/st,stih407-picophyreset.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: STMicroelectronics STi family Sysconfig Picophy SoftReset Controller
|
||||
|
||||
maintainers:
|
||||
- Peter Griffin <peter.griffin@linaro.org>
|
||||
|
||||
description: |
|
||||
This binding describes a reset controller device that is used to enable and
|
||||
disable on-chip PicoPHY USB2 phy(s) using "softreset" control bits found in
|
||||
the STi family SoC system configuration registers.
|
||||
|
||||
The actual action taken when softreset is asserted is hardware dependent.
|
||||
However, when asserted it may not be possible to access the hardware's
|
||||
registers and after an assert/deassert sequence the hardware's previous state
|
||||
may no longer be valid.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stih407-picophyreset
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/reset/stih407-resets.h>
|
||||
|
||||
picophyreset: picophyreset-controller {
|
||||
compatible = "st,stih407-picophyreset";
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
// Specifying picophyreset control of devices
|
||||
usb2_picophy0: usbpicophy {
|
||||
resets = <&picophyreset STIH407_PICOPHY0_RESET>;
|
||||
};
|
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/st,stih407-powerdown.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: STMicroelectronics STi family Sysconfig Peripheral Powerdown Reset Controller
|
||||
|
||||
maintainers:
|
||||
- Srinivas Kandagatla <srinivas.kandagatla@st.com>
|
||||
|
||||
description: |
|
||||
This binding describes a reset controller device that is used to enable and
|
||||
disable on-chip peripheral controllers such as USB and SATA, using
|
||||
"powerdown" control bits found in the STi family SoC system configuration
|
||||
registers. These have been grouped together into a single reset controller
|
||||
device for convenience.
|
||||
|
||||
The actual action taken when powerdown is asserted is hardware dependent.
|
||||
However, when asserted it may not be possible to access the hardware's
|
||||
registers and after an assert/deassert sequence the hardware's previous state
|
||||
may no longer be valid.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stih407-powerdown
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/reset/stih407-resets.h>
|
||||
|
||||
powerdown: powerdown-controller {
|
||||
compatible = "st,stih407-powerdown";
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
// Specifying powerdown control of devices:
|
||||
st_dwc3: dwc3 {
|
||||
resets = <&powerdown STIH407_USB3_POWERDOWN>;
|
||||
};
|
@ -63,116 +63,23 @@ required:
|
||||
- ranges
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-9a-f]+$":
|
||||
type: object
|
||||
description: Common properties for GENI Serial Engine based I2C, SPI and
|
||||
UART controller.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: GENI Serial Engine register address and length.
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: se
|
||||
|
||||
clocks:
|
||||
description: Serial engine core clock needed by the device.
|
||||
maxItems: 1
|
||||
|
||||
interconnects:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
interconnect-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: qup-core
|
||||
- const: qup-config
|
||||
- const: qup-memory
|
||||
|
||||
required:
|
||||
- reg
|
||||
- clock-names
|
||||
- clocks
|
||||
|
||||
"spi@[0-9a-f]+$":
|
||||
type: object
|
||||
description: GENI serial engine based SPI controller. SPI in master mode
|
||||
supports up to 50MHz, up to four chip selects, programmable
|
||||
data path from 4 bits to 32 bits and numerous protocol
|
||||
variants.
|
||||
$ref: /schemas/spi/spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,geni-spi
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
$ref: /schemas/spi/qcom,spi-geni-qcom.yaml#
|
||||
|
||||
"i2c@[0-9a-f]+$":
|
||||
type: object
|
||||
description: GENI serial engine based I2C controller.
|
||||
$ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,geni-i2c
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
clock-frequency:
|
||||
description: Desired I2C bus clock frequency in Hz.
|
||||
default: 100000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
$ref: /schemas/i2c/qcom,i2c-geni-qcom.yaml#
|
||||
|
||||
"serial@[0-9a-f]+$":
|
||||
type: object
|
||||
description: GENI Serial Engine based UART Controller.
|
||||
$ref: /schemas/serial.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,geni-uart
|
||||
- qcom,geni-debug-uart
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: UART core irq
|
||||
- description: Wakeup irq (RX GPIO)
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
$ref: /schemas/serial/qcom,serial-geni-qcom.yaml#
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
272
Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml
Normal file
272
Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml
Normal file
@ -0,0 +1,272 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,rpmh-rsc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm RPMH RSC
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description: |
|
||||
Resource Power Manager Hardened (RPMH) is the mechanism for communicating
|
||||
with the hardened resource accelerators on Qualcomm SoCs. Requests to the
|
||||
resources can be written to the Trigger Command Set (TCS) registers and
|
||||
using a (addr, val) pair and triggered. Messages in the TCS are then sent in
|
||||
sequence over an internal bus.
|
||||
|
||||
The hardware block (Direct Resource Voter or DRV) is a part of the h/w entity
|
||||
(Resource State Coordinator a.k.a RSC) that can handle multiple sleep and
|
||||
active/wake resource requests. Multiple such DRVs can exist in a SoC and can
|
||||
be written to from Linux. The structure of each DRV follows the same template
|
||||
with a few variations that are captured by the properties here.
|
||||
|
||||
A TCS may be triggered from Linux or triggered by the F/W after all the CPUs
|
||||
have powered off to facilitate idle power saving. TCS could be classified as::
|
||||
ACTIVE - Triggered by Linux
|
||||
SLEEP - Triggered by F/W
|
||||
WAKE - Triggered by F/W
|
||||
CONTROL - Triggered by F/W
|
||||
See also:: <dt-bindings/soc/qcom,rpmh-rsc.h>
|
||||
|
||||
The order in which they are described in the DT, should match the hardware
|
||||
configuration.
|
||||
|
||||
Requests can be made for the state of a resource, when the subsystem is
|
||||
active or idle. When all subsystems like Modem, GPU, CPU are idle, the
|
||||
resource state will be an aggregate of the sleep votes from each of those
|
||||
subsystems. Clients may request a sleep value for their shared resources in
|
||||
addition to the active mode requests.
|
||||
|
||||
Drivers that want to use the RSC to communicate with RPMH must specify their
|
||||
bindings as child nodes of the RSC controllers they wish to communicate with.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,rpmh-rsc
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
description:
|
||||
The interrupt that trips when a message complete/response is received for
|
||||
this DRV from the accelerators.
|
||||
Number of interrupts must match number of DRV blocks.
|
||||
|
||||
label:
|
||||
description:
|
||||
Name for the RSC. The name would be used in trace logs.
|
||||
|
||||
qcom,drv-id:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The ID of the DRV in the RSC block that will be used by this controller.
|
||||
|
||||
qcom,tcs-config:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
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
|
||||
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
|
||||
description:
|
||||
The offset of the TCS blocks.
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: drv-0
|
||||
- const: drv-1
|
||||
- const: drv-2
|
||||
- const: drv-3
|
||||
|
||||
bcm-voter:
|
||||
$ref: /schemas/interconnect/qcom,bcm-voter.yaml#
|
||||
|
||||
clock-controller:
|
||||
$ref: /schemas/clock/qcom,rpmhcc.yaml#
|
||||
|
||||
power-controller:
|
||||
$ref: /schemas/power/qcom,rpmpd.yaml#
|
||||
|
||||
patternProperties:
|
||||
'-regulators$':
|
||||
$ref: /schemas/regulator/qcom,rpmh-regulator.yaml#
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
- qcom,drv-id
|
||||
- qcom,tcs-config
|
||||
- qcom,tcs-offset
|
||||
- reg
|
||||
- reg-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
// For a TCS whose RSC base address is 0x179C0000 and is at a DRV id of
|
||||
// 2, the register offsets for DRV2 start at 0D00, the register
|
||||
// calculations are like this::
|
||||
// DRV0: 0x179C0000
|
||||
// DRV2: 0x179C0000 + 0x10000 = 0x179D0000
|
||||
// DRV2: 0x179C0000 + 0x10000 * 2 = 0x179E0000
|
||||
// TCS-OFFSET: 0xD00
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/soc/qcom,rpmh-rsc.h>
|
||||
|
||||
rsc@179c0000 {
|
||||
compatible = "qcom,rpmh-rsc";
|
||||
reg = <0x179c0000 0x10000>,
|
||||
<0x179d0000 0x10000>,
|
||||
<0x179e0000 0x10000>;
|
||||
reg-names = "drv-0", "drv-1", "drv-2";
|
||||
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
|
||||
label = "apps_rsc";
|
||||
qcom,tcs-offset = <0xd00>;
|
||||
qcom,drv-id = <2>;
|
||||
qcom,tcs-config = <ACTIVE_TCS 2>,
|
||||
<SLEEP_TCS 3>,
|
||||
<WAKE_TCS 3>,
|
||||
<CONTROL_TCS 1>;
|
||||
};
|
||||
|
||||
- |
|
||||
// For a TCS whose RSC base address is 0xAF20000 and is at DRV id of 0, the
|
||||
// register offsets for DRV0 start at 01C00, the register calculations are
|
||||
// like this::
|
||||
// DRV0: 0xAF20000
|
||||
// TCS-OFFSET: 0x1C00
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/soc/qcom,rpmh-rsc.h>
|
||||
|
||||
rsc@af20000 {
|
||||
compatible = "qcom,rpmh-rsc";
|
||||
reg = <0xaf20000 0x10000>;
|
||||
reg-names = "drv-0";
|
||||
interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
|
||||
label = "disp_rsc";
|
||||
qcom,tcs-offset = <0x1c00>;
|
||||
qcom,drv-id = <0>;
|
||||
qcom,tcs-config = <ACTIVE_TCS 0>,
|
||||
<SLEEP_TCS 1>,
|
||||
<WAKE_TCS 1>,
|
||||
<CONTROL_TCS 0>;
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/soc/qcom,rpmh-rsc.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
rsc@18200000 {
|
||||
compatible = "qcom,rpmh-rsc";
|
||||
reg = <0x18200000 0x10000>,
|
||||
<0x18210000 0x10000>,
|
||||
<0x18220000 0x10000>;
|
||||
reg-names = "drv-0", "drv-1", "drv-2";
|
||||
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
|
||||
label = "apps_rsc";
|
||||
qcom,tcs-offset = <0xd00>;
|
||||
qcom,drv-id = <2>;
|
||||
qcom,tcs-config = <ACTIVE_TCS 2>,
|
||||
<SLEEP_TCS 3>,
|
||||
<WAKE_TCS 3>,
|
||||
<CONTROL_TCS 0>;
|
||||
|
||||
clock-controller {
|
||||
compatible = "qcom,sm8350-rpmh-clk";
|
||||
#clock-cells = <1>;
|
||||
clock-names = "xo";
|
||||
clocks = <&xo_board>;
|
||||
};
|
||||
|
||||
power-controller {
|
||||
compatible = "qcom,sm8350-rpmhpd";
|
||||
#power-domain-cells = <1>;
|
||||
operating-points-v2 = <&rpmhpd_opp_table>;
|
||||
|
||||
rpmhpd_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
rpmhpd_opp_ret: opp1 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_RETENTION>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_min_svs: opp2 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_low_svs: opp3 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_svs: opp4 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_svs_l1: opp5 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_nom: opp6 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_NOM>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_nom_l1: opp7 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_nom_l2: opp8 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_NOM_L2>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_turbo: opp9 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_TURBO>;
|
||||
};
|
||||
|
||||
rpmhpd_opp_turbo_l1: opp10 {
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
bcm-voter {
|
||||
compatible = "qcom,bcm-voter";
|
||||
};
|
||||
};
|
@ -12,7 +12,7 @@ description: |
|
||||
to vote for state of the system resources, such as clocks, regulators and bus
|
||||
frequencies.
|
||||
|
||||
The SMD information for the RPM edge should be filled out. See qcom,smd.txt
|
||||
The SMD information for the RPM edge should be filled out. See qcom,smd.yaml
|
||||
for the required edge properties. All SMD related properties will reside
|
||||
within the RPM node itself.
|
||||
|
||||
@ -25,7 +25,8 @@ description: |
|
||||
rpm_requests.
|
||||
|
||||
maintainers:
|
||||
- Kathiravan T <kathirav@codeaurora.org>
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@ -83,7 +84,7 @@ examples:
|
||||
qcom,ipc = <&apcs 8 0>;
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm_requests {
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-msm8974";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
|
@ -1,98 +0,0 @@
|
||||
Qualcomm Shared Memory Driver (SMD) binding
|
||||
|
||||
This binding describes the Qualcomm Shared Memory Driver, a fifo based
|
||||
communication channel for sending data between the various subsystems in
|
||||
Qualcomm platforms.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: must be "qcom,smd"
|
||||
|
||||
= EDGES
|
||||
|
||||
Each subnode of the SMD node represents a remote subsystem or a remote
|
||||
processor of some sort - or in SMD language an "edge". The name of the edges
|
||||
are not important.
|
||||
The edge is described by the following properties:
|
||||
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: should specify the IRQ used by the remote processor to
|
||||
signal this processor about communication related updates
|
||||
|
||||
- mboxes:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: reference to the associated doorbell in APCS, as described
|
||||
in mailbox/mailbox.txt
|
||||
|
||||
- qcom,ipc:
|
||||
Usage: required, unless mboxes is specified
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: three entries specifying the outgoing ipc bit used for
|
||||
signaling the remote processor:
|
||||
- phandle to a syscon node representing the apcs registers
|
||||
- u32 representing offset to the register within the syscon
|
||||
- u32 representing the ipc bit within the register
|
||||
|
||||
- qcom,smd-edge:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: the identifier of the remote processor in the smd channel
|
||||
allocation table
|
||||
|
||||
- qcom,remote-pid:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: the identifier for the remote processor as known by the rest
|
||||
of the system.
|
||||
|
||||
- label:
|
||||
Usage: optional
|
||||
Value type: <string>
|
||||
Definition: name of the edge, used for debugging and identification
|
||||
purposes. The node name will be used if this is not
|
||||
present.
|
||||
|
||||
= SMD DEVICES
|
||||
|
||||
In turn, subnodes of the "edges" represent devices tied to SMD channels on that
|
||||
"edge". The names of the devices are not important. The properties of these
|
||||
nodes are defined by the individual bindings for the SMD devices - but must
|
||||
contain the following property:
|
||||
|
||||
- qcom,smd-channels:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: a list of channels tied to this device, used for matching
|
||||
the device to channels
|
||||
|
||||
= EXAMPLE
|
||||
|
||||
The following example represents a smd node, with one edge representing the
|
||||
"rpm" subsystem. For the "rpm" subsystem we have a device tied to the
|
||||
"rpm_request" channel.
|
||||
|
||||
apcs: syscon@f9011000 {
|
||||
compatible = "syscon";
|
||||
reg = <0xf9011000 0x1000>;
|
||||
};
|
||||
|
||||
smd {
|
||||
compatible = "qcom,smd";
|
||||
|
||||
rpm {
|
||||
interrupts = <0 168 1>;
|
||||
qcom,ipc = <&apcs 8 0>;
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm_requests {
|
||||
compatible = "qcom,rpm-msm8974";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
137
Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml
Normal file
137
Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml
Normal file
@ -0,0 +1,137 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,smd.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Shared Memory Driver
|
||||
|
||||
maintainers:
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
|
||||
description:
|
||||
The Qualcomm Shared Memory Driver is a FIFO based communication channel for
|
||||
sending data between the various subsystems in Qualcomm platforms.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,smd
|
||||
|
||||
patternProperties:
|
||||
"^.*-edge|rpm$":
|
||||
type: object
|
||||
description:
|
||||
Each subnode of the SMD node represents a remote subsystem or a remote
|
||||
processor of some sort - or in SMD language an "edge". The name of the
|
||||
edges are not important.
|
||||
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
label:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
Name of the edge, used for debugging and identification purposes. The
|
||||
node name will be used if this is not present.
|
||||
|
||||
mboxes:
|
||||
maxItems: 1
|
||||
description:
|
||||
Reference to the mailbox representing the outgoing doorbell in APCS for
|
||||
this client.
|
||||
|
||||
qcom,ipc:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to a syscon node representing the APCS registers
|
||||
- description: u32 representing offset to the register within the syscon
|
||||
- description: u32 representing the ipc bit within the register
|
||||
description:
|
||||
Three entries specifying the outgoing ipc bit used for signaling the
|
||||
remote processor.
|
||||
|
||||
qcom,smd-edge:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The identifier of the remote processor in the smd channel allocation
|
||||
table.
|
||||
|
||||
qcom,remote-pid:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The identifier for the remote processor as known by the rest of the
|
||||
system.
|
||||
|
||||
# Binding for edge subnodes is not complete
|
||||
patternProperties:
|
||||
"^rpm-requests$":
|
||||
type: object
|
||||
description:
|
||||
In turn, subnodes of the "edges" represent devices tied to SMD
|
||||
channels on that "edge". The names of the devices are not
|
||||
important. The properties of these nodes are defined by the
|
||||
individual bindings for the SMD devices.
|
||||
|
||||
properties:
|
||||
qcom,smd-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
minItems: 1
|
||||
maxItems: 32
|
||||
description:
|
||||
A list of channels tied to this device, used for matching the
|
||||
device to channels.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- qcom,smd-channels
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
required:
|
||||
- interrupts
|
||||
- qcom,smd-edge
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- mboxes
|
||||
- required:
|
||||
- qcom,ipc
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
# The following example represents a smd node, with one edge representing the
|
||||
# "rpm" subsystem. For the "rpm" subsystem we have a device tied to the
|
||||
# "rpm_request" channel.
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
shared-memory {
|
||||
compatible = "qcom,smd";
|
||||
|
||||
rpm {
|
||||
interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
|
||||
qcom,ipc = <&apcs 8 0>;
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-msm8974";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
clock-controller {
|
||||
compatible = "qcom,rpmcc-msm8974", "qcom,rpmcc";
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
@ -1,104 +0,0 @@
|
||||
Qualcomm Shared Memory State Machine
|
||||
|
||||
The Shared Memory State Machine facilitates broadcasting of single bit state
|
||||
information between the processors in a Qualcomm SoC. Each processor is
|
||||
assigned 32 bits of state that can be modified. A processor can through a
|
||||
matrix of bitmaps signal subscription of notifications upon changes to a
|
||||
certain bit owned by a certain remote processor.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be one of:
|
||||
"qcom,smsm"
|
||||
|
||||
- qcom,ipc-N:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: three entries specifying the outgoing ipc bit used for
|
||||
signaling the N:th remote processor
|
||||
- phandle to a syscon node representing the apcs registers
|
||||
- u32 representing offset to the register within the syscon
|
||||
- u32 representing the ipc bit within the register
|
||||
|
||||
- qcom,local-host:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: identifier of the local processor in the list of hosts, or
|
||||
in other words specifier of the column in the subscription
|
||||
matrix representing the local processor
|
||||
defaults to host 0
|
||||
|
||||
- #address-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: must be 1
|
||||
|
||||
- #size-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: must be 0
|
||||
|
||||
= SUBNODES
|
||||
Each processor's state bits are described by a subnode of the smsm device node.
|
||||
Nodes can either be flagged as an interrupt-controller to denote a remote
|
||||
processor's state bits or the local processors bits. The node names are not
|
||||
important.
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: specifies the offset, in words, of the first bit for this
|
||||
entry
|
||||
|
||||
- #qcom,smem-state-cells:
|
||||
Usage: required for local entry
|
||||
Value type: <u32>
|
||||
Definition: must be 1 - denotes bit number
|
||||
|
||||
- interrupt-controller:
|
||||
Usage: required for remote entries
|
||||
Value type: <empty>
|
||||
Definition: marks the entry as a interrupt-controller and the state bits
|
||||
to belong to a remote processor
|
||||
|
||||
- #interrupt-cells:
|
||||
Usage: required for remote entries
|
||||
Value type: <u32>
|
||||
Definition: must be 2 - denotes bit number and IRQ flags
|
||||
|
||||
- interrupts:
|
||||
Usage: required for remote entries
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: one entry specifying remote IRQ used by the remote processor
|
||||
to signal changes of its state bits
|
||||
|
||||
|
||||
= EXAMPLE
|
||||
The following example shows the SMEM setup for controlling properties of the
|
||||
wireless processor, defined from the 8974 apps processor's point-of-view. It
|
||||
encompasses one outbound entry and the outgoing interrupt for the wireless
|
||||
processor.
|
||||
|
||||
smsm {
|
||||
compatible = "qcom,smsm";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
qcom,ipc-3 = <&apcs 8 19>;
|
||||
|
||||
apps_smsm: apps@0 {
|
||||
reg = <0>;
|
||||
|
||||
#qcom,smem-state-cells = <1>;
|
||||
};
|
||||
|
||||
wcnss_smsm: wcnss@7 {
|
||||
reg = <7>;
|
||||
interrupts = <0 144 1>;
|
||||
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
138
Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml
Normal file
138
Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml
Normal file
@ -0,0 +1,138 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,smsm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Shared Memory State Machine
|
||||
|
||||
maintainers:
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
|
||||
description:
|
||||
The Shared Memory State Machine facilitates broadcasting of single bit state
|
||||
information between the processors in a Qualcomm SoC. Each processor is
|
||||
assigned 32 bits of state that can be modified. A processor can through a
|
||||
matrix of bitmaps signal subscription of notifications upon changes to a
|
||||
certain bit owned by a certain remote processor.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,smsm
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
qcom,local-host:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 0
|
||||
description:
|
||||
Identifier of the local processor in the list of hosts, or in other words
|
||||
specifier of the column in the subscription matrix representing the local
|
||||
processor.
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^qcom,ipc-[1-4]$":
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to a syscon node representing the APCS registers
|
||||
- description: u32 representing offset to the register within the syscon
|
||||
- description: u32 representing the ipc bit within the register
|
||||
description:
|
||||
Three entries specifying the outgoing ipc bit used for signaling the N:th
|
||||
remote processor.
|
||||
|
||||
"@[0-9a-f]$":
|
||||
type: object
|
||||
description:
|
||||
Each processor's state bits are described by a subnode of the SMSM device
|
||||
node. Nodes can either be flagged as an interrupt-controller to denote a
|
||||
remote processor's state bits or the local processors bits. The node
|
||||
names are not important.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-controller:
|
||||
description:
|
||||
Marks the entry as a interrupt-controller and the state bits to
|
||||
belong to a remote processor.
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
One entry specifying remote IRQ used by the remote processor to
|
||||
signal changes of its state bits.
|
||||
|
||||
'#qcom,smem-state-cells':
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
const: 1
|
||||
description:
|
||||
Required for local entry. Denotes bit number.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- '#qcom,smem-state-cells'
|
||||
- required:
|
||||
- interrupt-controller
|
||||
- '#interrupt-cells'
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
anyOf:
|
||||
- required:
|
||||
- qcom,ipc-1
|
||||
- required:
|
||||
- qcom,ipc-2
|
||||
- required:
|
||||
- qcom,ipc-3
|
||||
- required:
|
||||
- qcom,ipc-4
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
# The following example shows the SMEM setup for controlling properties of
|
||||
# the wireless processor, defined from the 8974 apps processor's
|
||||
# point-of-view. It encompasses one outbound entry and the outgoing interrupt
|
||||
# for the wireless processor.
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
shared-memory {
|
||||
compatible = "qcom,smsm";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
qcom,ipc-3 = <&apcs 8 19>;
|
||||
|
||||
apps_smsm: apps@0 {
|
||||
reg = <0>;
|
||||
#qcom,smem-state-cells = <1>;
|
||||
};
|
||||
|
||||
wcnss_smsm: wcnss@7 {
|
||||
reg = <7>;
|
||||
interrupts = <GIC_SPI 144 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
@ -1,131 +0,0 @@
|
||||
Qualcomm WCNSS Binding
|
||||
|
||||
This binding describes the Qualcomm WCNSS hardware. It consists of control
|
||||
block and a BT, WiFi and FM radio block, all using SMD as command channels.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be: "qcom,wcnss",
|
||||
|
||||
- qcom,smd-channel:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: standard SMD property specifying the SMD channel used for
|
||||
communication with the WiFi firmware.
|
||||
Should be "WCNSS_CTRL".
|
||||
|
||||
- qcom,mmio:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: reference to a node specifying the wcnss "ccu" and "dxe"
|
||||
register blocks. The node must be compatible with one of
|
||||
the following:
|
||||
"qcom,riva",
|
||||
"qcom,pronto"
|
||||
|
||||
- firmware-name:
|
||||
Usage: optional
|
||||
Value type: <string>
|
||||
Definition: specifies the relative firmware image path for the WLAN NV
|
||||
blob. Defaults to "wlan/prima/WCNSS_qcom_wlan_nv.bin" if
|
||||
not specified.
|
||||
|
||||
= SUBNODES
|
||||
The subnodes of the wcnss node are optional and describe the individual blocks in
|
||||
the WCNSS.
|
||||
|
||||
== Bluetooth
|
||||
The following properties are defined to the bluetooth node:
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be:
|
||||
"qcom,wcnss-bt"
|
||||
|
||||
- local-bd-address:
|
||||
Usage: optional
|
||||
Value type: <u8 array>
|
||||
Definition: see Documentation/devicetree/bindings/net/bluetooth.txt
|
||||
|
||||
== WiFi
|
||||
The following properties are defined to the WiFi node:
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be one of:
|
||||
"qcom,wcnss-wlan",
|
||||
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: should specify the "rx" and "tx" interrupts
|
||||
|
||||
- interrupt-names:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: must contain "rx" and "tx"
|
||||
|
||||
- qcom,smem-state:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: should reference the tx-enable and tx-rings-empty SMEM states
|
||||
|
||||
- qcom,smem-state-names:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: must contain "tx-enable" and "tx-rings-empty"
|
||||
|
||||
= EXAMPLE
|
||||
The following example represents a SMD node, with one edge representing the
|
||||
"pronto" subsystem, with the wcnss device and its wcn3680 BT and WiFi blocks
|
||||
described; as found on the 8974 platform.
|
||||
|
||||
smd {
|
||||
compatible = "qcom,smd";
|
||||
|
||||
pronto-edge {
|
||||
interrupts = <0 142 1>;
|
||||
|
||||
qcom,ipc = <&apcs 8 17>;
|
||||
qcom,smd-edge = <6>;
|
||||
|
||||
wcnss {
|
||||
compatible = "qcom,wcnss";
|
||||
qcom,smd-channels = "WCNSS_CTRL";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
qcom,mmio = <&pronto>;
|
||||
|
||||
bt {
|
||||
compatible = "qcom,wcnss-bt";
|
||||
|
||||
/* BD address 00:11:22:33:44:55 */
|
||||
local-bd-address = [ 55 44 33 22 11 00 ];
|
||||
};
|
||||
|
||||
wlan {
|
||||
compatible = "qcom,wcnss-wlan";
|
||||
|
||||
interrupts = <0 145 0>, <0 146 0>;
|
||||
interrupt-names = "tx", "rx";
|
||||
|
||||
qcom,smem-state = <&apps_smsm 10>, <&apps_smsm 9>;
|
||||
qcom,smem-state-names = "tx-enable", "tx-rings-empty";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
soc {
|
||||
pronto: pronto {
|
||||
compatible = "qcom,pronto";
|
||||
|
||||
reg = <0xfb204000 0x2000>, <0xfb202000 0x1000>, <0xfb21b000 0x3000>;
|
||||
reg-names = "ccu", "dxe", "pmu";
|
||||
};
|
||||
};
|
137
Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml
Normal file
137
Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml
Normal file
@ -0,0 +1,137 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,wcnss.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm WCNSS
|
||||
|
||||
maintainers:
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description:
|
||||
The Qualcomm WCNSS hardware consists of control block and a BT, WiFi and FM
|
||||
radio block, all using SMD as command channels.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,wcnss
|
||||
|
||||
firmware-name:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
default: "wlan/prima/WCNSS_qcom_wlan_nv.bin"
|
||||
description:
|
||||
Relative firmware image path for the WLAN NV blob.
|
||||
|
||||
qcom,mmio:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: |
|
||||
Reference to a node specifying the wcnss "ccu" and "dxe" register blocks.
|
||||
The node must be compatible with one of the following::
|
||||
- qcom,riva"
|
||||
- qcom,pronto"
|
||||
|
||||
qcom,smd-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
const: WCNSS_CTRL
|
||||
description:
|
||||
Standard SMD property specifying the SMD channel used for communication
|
||||
with the WiFi firmware.
|
||||
|
||||
bluetooth:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,wcnss-bt
|
||||
|
||||
local-bd-address:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8-array
|
||||
maxItems: 6
|
||||
description:
|
||||
See Documentation/devicetree/bindings/net/bluetooth.txt
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
wifi:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,wcnss-wlan
|
||||
|
||||
interrupts:
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
qcom,smem-states:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
maxItems: 2
|
||||
description:
|
||||
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
|
||||
description:
|
||||
Names of SMEM states.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- qcom,smem-states
|
||||
- qcom,smem-state-names
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- qcom,mmio
|
||||
- qcom,smd-channels
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
smd-edge {
|
||||
interrupts = <GIC_SPI 142 IRQ_TYPE_EDGE_RISING>;
|
||||
|
||||
qcom,ipc = <&apcs 8 17>;
|
||||
qcom,smd-edge = <6>;
|
||||
qcom,remote-pid = <4>;
|
||||
|
||||
label = "pronto";
|
||||
|
||||
wcnss {
|
||||
compatible = "qcom,wcnss";
|
||||
qcom,smd-channels = "WCNSS_CTRL";
|
||||
|
||||
qcom,mmio = <&pronto>;
|
||||
|
||||
bluetooth {
|
||||
compatible = "qcom,wcnss-bt";
|
||||
/* BD address 00:11:22:33:44:55 */
|
||||
local-bd-address = [ 55 44 33 22 11 00 ];
|
||||
};
|
||||
|
||||
wifi {
|
||||
compatible = "qcom,wcnss-wlan";
|
||||
|
||||
interrupts = <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "tx", "rx";
|
||||
|
||||
qcom,smem-states = <&apps_smsm 10>, <&apps_smsm 9>;
|
||||
qcom,smem-state-names = "tx-enable", "tx-rings-empty";
|
||||
};
|
||||
};
|
||||
};
|
@ -1,137 +0,0 @@
|
||||
RPMH RSC:
|
||||
------------
|
||||
|
||||
Resource Power Manager Hardened (RPMH) is the mechanism for communicating with
|
||||
the hardened resource accelerators on Qualcomm SoCs. Requests to the resources
|
||||
can be written to the Trigger Command Set (TCS) registers and using a (addr,
|
||||
val) pair and triggered. Messages in the TCS are then sent in sequence over an
|
||||
internal bus.
|
||||
|
||||
The hardware block (Direct Resource Voter or DRV) is a part of the h/w entity
|
||||
(Resource State Coordinator a.k.a RSC) that can handle multiple sleep and
|
||||
active/wake resource requests. Multiple such DRVs can exist in a SoC and can
|
||||
be written to from Linux. The structure of each DRV follows the same template
|
||||
with a few variations that are captured by the properties here.
|
||||
|
||||
A TCS may be triggered from Linux or triggered by the F/W after all the CPUs
|
||||
have powered off to facilitate idle power saving. TCS could be classified as -
|
||||
|
||||
ACTIVE /* Triggered by Linux */
|
||||
SLEEP /* Triggered by F/W */
|
||||
WAKE /* Triggered by F/W */
|
||||
CONTROL /* Triggered by F/W */
|
||||
|
||||
The order in which they are described in the DT, should match the hardware
|
||||
configuration.
|
||||
|
||||
Requests can be made for the state of a resource, when the subsystem is active
|
||||
or idle. When all subsystems like Modem, GPU, CPU are idle, the resource state
|
||||
will be an aggregate of the sleep votes from each of those subsystems. Clients
|
||||
may request a sleep value for their shared resources in addition to the active
|
||||
mode requests.
|
||||
|
||||
Properties:
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Should be "qcom,rpmh-rsc".
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: The first register specifies the base address of the
|
||||
DRV(s). The number of DRVs in the dependent on the RSC.
|
||||
The tcs-offset specifies the start address of the
|
||||
TCS in the DRVs.
|
||||
|
||||
- reg-names:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Maps the register specified in the reg property. Must be
|
||||
"drv-0", "drv-1", "drv-2" etc and "tcs-offset". The
|
||||
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-interrupt>
|
||||
Definition: The interrupt that trips when a message complete/response
|
||||
is received for this DRV from the accelerators.
|
||||
|
||||
- qcom,drv-id:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: The id of the DRV in the RSC block that will be used by
|
||||
this controller.
|
||||
|
||||
- qcom,tcs-config:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: The tuple defining the configuration of TCS.
|
||||
Must have 2 cells which describe each TCS type.
|
||||
<type number_of_tcs>.
|
||||
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>
|
||||
|
||||
- label:
|
||||
Usage: optional
|
||||
Value type: <string>
|
||||
Definition: Name for the RSC. The name would be used in trace logs.
|
||||
|
||||
Drivers that want to use the RSC to communicate with RPMH must specify their
|
||||
bindings as child nodes of the RSC controllers they wish to communicate with.
|
||||
|
||||
Example 1:
|
||||
|
||||
For a TCS whose RSC base address is is 0x179C0000 and is at a DRV id of 2, the
|
||||
register offsets for DRV2 start at 0D00, the register calculations are like
|
||||
this -
|
||||
DRV0: 0x179C0000
|
||||
DRV2: 0x179C0000 + 0x10000 = 0x179D0000
|
||||
DRV2: 0x179C0000 + 0x10000 * 2 = 0x179E0000
|
||||
TCS-OFFSET: 0xD00
|
||||
|
||||
apps_rsc: rsc@179c0000 {
|
||||
label = "apps_rsc";
|
||||
compatible = "qcom,rpmh-rsc";
|
||||
reg = <0x179c0000 0x10000>,
|
||||
<0x179d0000 0x10000>,
|
||||
<0x179e0000 0x10000>;
|
||||
reg-names = "drv-0", "drv-1", "drv-2";
|
||||
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
|
||||
qcom,tcs-offset = <0xd00>;
|
||||
qcom,drv-id = <2>;
|
||||
qcom,tcs-config = <ACTIVE_TCS 2>,
|
||||
<SLEEP_TCS 3>,
|
||||
<WAKE_TCS 3>,
|
||||
<CONTROL_TCS 1>;
|
||||
};
|
||||
|
||||
Example 2:
|
||||
|
||||
For a TCS whose RSC base address is 0xAF20000 and is at DRV id of 0, the
|
||||
register offsets for DRV0 start at 01C00, the register calculations are like
|
||||
this -
|
||||
DRV0: 0xAF20000
|
||||
TCS-OFFSET: 0x1C00
|
||||
|
||||
disp_rsc: rsc@af20000 {
|
||||
label = "disp_rsc";
|
||||
compatible = "qcom,rpmh-rsc";
|
||||
reg = <0xaf20000 0x10000>;
|
||||
reg-names = "drv-0";
|
||||
interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
|
||||
qcom,tcs-offset = <0x1c00>;
|
||||
qcom,drv-id = <0>;
|
||||
qcom,tcs-config = <ACTIVE_TCS 0>,
|
||||
<SLEEP_TCS 1>,
|
||||
<WAKE_TCS 1>,
|
||||
<CONTROL_TCS 0>;
|
||||
};
|
@ -15,6 +15,9 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- rockchip,rk3288-sgrf
|
||||
- rockchip,rk3566-pipe-grf
|
||||
- rockchip,rk3568-pipe-grf
|
||||
- rockchip,rk3568-pipe-phy-grf
|
||||
- rockchip,rk3568-usb2phy-grf
|
||||
- rockchip,rv1108-usbgrf
|
||||
- const: syscon
|
||||
|
@ -77,7 +77,7 @@ patternProperties:
|
||||
description: Child node describing underlying UART/serial
|
||||
|
||||
"^spi@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: /schemas/spi/samsung,spi.yaml
|
||||
description: Child node describing underlying SPI
|
||||
|
||||
required:
|
||||
|
@ -1,39 +0,0 @@
|
||||
GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
|
||||
|
||||
The QUP v3 core is a GENI based AHB slave that provides a common data path
|
||||
(an output FIFO and an input FIFO) for serial peripheral interface (SPI)
|
||||
mini-core.
|
||||
|
||||
SPI in master mode supports up to 50MHz, up to four chip selects, programmable
|
||||
data path from 4 bits to 32 bits and numerous protocol variants.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must contain "qcom,geni-spi".
|
||||
- reg: Must contain SPI register location and length.
|
||||
- interrupts: Must contain SPI controller interrupts.
|
||||
- clock-names: Must contain "se".
|
||||
- clocks: Serial engine core clock needed by the device.
|
||||
- #address-cells: Must be <1> to define a chip select address on
|
||||
the SPI bus.
|
||||
- #size-cells: Must be <0>.
|
||||
|
||||
SPI Controller nodes must be child of GENI based Qualcomm Universal
|
||||
Peripharal. Please refer GENI based QUP wrapper controller node bindings
|
||||
described in Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml.
|
||||
|
||||
SPI slave nodes must be children of the SPI master node and conform to SPI bus
|
||||
binding as described in Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
|
||||
Example:
|
||||
spi0: spi@a84000 {
|
||||
compatible = "qcom,geni-spi";
|
||||
reg = <0xa84000 0x4000>;
|
||||
interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "se";
|
||||
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&qup_1_spi_2_active>;
|
||||
pinctrl-1 = <&qup_1_spi_2_sleep>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
116
Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
Normal file
116
Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
Normal file
@ -0,0 +1,116 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/qcom,spi-geni-qcom.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
|
||||
|
||||
maintainers:
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
|
||||
description:
|
||||
The QUP v3 core is a GENI based AHB slave that provides a common data path
|
||||
(an output FIFO and an input FIFO) for serial peripheral interface (SPI)
|
||||
mini-core.
|
||||
|
||||
SPI in master mode supports up to 50MHz, up to four chip selects,
|
||||
programmable data path from 4 bits to 32 bits and numerous protocol variants.
|
||||
|
||||
SPI Controller nodes must be child of GENI based Qualcomm Universal
|
||||
Peripharal. Please refer GENI based QUP wrapper controller node bindings
|
||||
described in Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,geni-spi
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: se
|
||||
|
||||
dmas:
|
||||
maxItems: 2
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
interconnects:
|
||||
maxItems: 2
|
||||
|
||||
interconnect-names:
|
||||
items:
|
||||
- const: qup-core
|
||||
- const: qup-config
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
operating-points-v2: true
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sc7180.h>
|
||||
#include <dt-bindings/interconnect/qcom,sc7180.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
spi@880000 {
|
||||
compatible = "qcom,geni-spi";
|
||||
reg = <0x00880000 0x4000>;
|
||||
clock-names = "se";
|
||||
clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&qup_spi0_default>;
|
||||
interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
power-domains = <&rpmhpd SC7180_CX>;
|
||||
operating-points-v2 = <&qup_opp_table>;
|
||||
interconnects = <&qup_virt MASTER_QUP_CORE_0 0 &qup_virt SLAVE_QUP_CORE_0 0>,
|
||||
<&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_QUP_0 0>;
|
||||
interconnect-names = "qup-core", "qup-config";
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/dma/qcom-gpi.h>
|
||||
|
||||
spi@884000 {
|
||||
compatible = "qcom,geni-spi";
|
||||
reg = <0x00884000 0x4000>;
|
||||
clock-names = "se";
|
||||
clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>;
|
||||
dmas = <&gpi_dma0 0 1 QCOM_GPI_SPI>,
|
||||
<&gpi_dma0 1 1 QCOM_GPI_SPI>;
|
||||
dma-names = "tx", "rx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&qup_spi1_default>;
|
||||
interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>;
|
||||
spi-max-frequency = <50000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
@ -19,9 +19,20 @@ description: |+
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,exynos4210-mct
|
||||
- samsung,exynos4412-mct
|
||||
oneOf:
|
||||
- enum:
|
||||
- samsung,exynos4210-mct
|
||||
- samsung,exynos4412-mct
|
||||
- items:
|
||||
- enum:
|
||||
- samsung,exynos3250-mct
|
||||
- samsung,exynos5250-mct
|
||||
- samsung,exynos5260-mct
|
||||
- samsung,exynos5420-mct
|
||||
- samsung,exynos5433-mct
|
||||
- samsung,exynos850-mct
|
||||
- tesla,fsd-mct
|
||||
- const: samsung,exynos4210-mct
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
@ -62,6 +73,56 @@ required:
|
||||
- interrupts
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos3250-mct
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 8
|
||||
maxItems: 8
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos5250-mct
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 6
|
||||
maxItems: 6
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynos5260-mct
|
||||
- samsung,exynos5420-mct
|
||||
- samsung,exynos5433-mct
|
||||
- samsung,exynos850-mct
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 12
|
||||
maxItems: 12
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- tesla,fsd-mct
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 16
|
||||
maxItems: 16
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -1837,7 +1837,9 @@ F: Documentation/devicetree/bindings/arm/apple/*
|
||||
F: Documentation/devicetree/bindings/clock/apple,nco.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/apple,*
|
||||
F: Documentation/devicetree/bindings/iommu/apple,sart.yaml
|
||||
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
|
||||
F: Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml
|
||||
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
|
||||
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
|
||||
F: Documentation/devicetree/bindings/power/apple*
|
||||
@ -1848,12 +1850,14 @@ F: drivers/i2c/busses/i2c-pasemi-core.c
|
||||
F: drivers/i2c/busses/i2c-pasemi-platform.c
|
||||
F: drivers/irqchip/irq-apple-aic.c
|
||||
F: drivers/mailbox/apple-mailbox.c
|
||||
F: drivers/nvme/host/apple.c
|
||||
F: drivers/pinctrl/pinctrl-apple-gpio.c
|
||||
F: drivers/soc/apple/*
|
||||
F: drivers/watchdog/apple_wdt.c
|
||||
F: include/dt-bindings/interrupt-controller/apple-aic.h
|
||||
F: include/dt-bindings/pinctrl/apple.h
|
||||
F: include/linux/apple-mailbox.h
|
||||
F: include/linux/soc/apple/*
|
||||
|
||||
ARM/ARTPEC MACHINE SUPPORT
|
||||
M: Jesper Nilsson <jesper.nilsson@axis.com>
|
||||
|
@ -152,6 +152,17 @@ config QCOM_EBI2
|
||||
Interface 2, which can be used to connect things like NAND Flash,
|
||||
SRAM, ethernet adapters, FPGAs and LCD displays.
|
||||
|
||||
config QCOM_SSC_BLOCK_BUS
|
||||
bool "Qualcomm SSC Block Bus Init Driver"
|
||||
depends on ARCH_QCOM
|
||||
help
|
||||
Say y here to enable support for initializing the bus that connects
|
||||
the SSC block's internal bus to the cNoC (configurantion NoC) on
|
||||
(some) qcom SoCs.
|
||||
The SSC (Snapdragon Sensor Core) block contains a gpio controller,
|
||||
i2c/spi/uart controllers, a hexagon core, and a clock controller
|
||||
which provides clocks for the above.
|
||||
|
||||
config SUN50I_DE2_BUS
|
||||
bool "Allwinner A64 DE2 Bus Driver"
|
||||
default ARM64
|
||||
|
@ -25,6 +25,7 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
|
||||
|
||||
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
|
||||
obj-$(CONFIG_QCOM_EBI2) += qcom-ebi2.o
|
||||
obj-$(CONFIG_QCOM_SSC_BLOCK_BUS) += qcom-ssc-block-bus.o
|
||||
obj-$(CONFIG_SUN50I_DE2_BUS) += sun50i-de2.o
|
||||
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
|
||||
obj-$(CONFIG_OF) += simple-pm-bus.o
|
||||
|
@ -536,7 +536,6 @@ static struct platform_driver brcmstb_gisb_arb_driver = {
|
||||
.name = "brcm-gisb-arb",
|
||||
.of_match_table = brcmstb_gisb_arb_of_match,
|
||||
.pm = &brcmstb_gisb_arb_pm_ops,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
|
389
drivers/bus/qcom-ssc-block-bus.c
Normal file
389
drivers/bus/qcom-ssc-block-bus.c
Normal file
@ -0,0 +1,389 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright (c) 2021, Michael Srba
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_clock.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
/* AXI Halt Register Offsets */
|
||||
#define AXI_HALTREQ_REG 0x0
|
||||
#define AXI_HALTACK_REG 0x4
|
||||
#define AXI_IDLE_REG 0x8
|
||||
|
||||
#define SSCAON_CONFIG0_CLAMP_EN_OVRD BIT(4)
|
||||
#define SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL BIT(5)
|
||||
|
||||
static const char *const qcom_ssc_block_pd_names[] = {
|
||||
"ssc_cx",
|
||||
"ssc_mx"
|
||||
};
|
||||
|
||||
struct qcom_ssc_block_bus_data {
|
||||
const char *const *pd_names;
|
||||
struct device *pds[ARRAY_SIZE(qcom_ssc_block_pd_names)];
|
||||
char __iomem *reg_mpm_sscaon_config0;
|
||||
char __iomem *reg_mpm_sscaon_config1;
|
||||
struct regmap *halt_map;
|
||||
struct clk *xo_clk;
|
||||
struct clk *aggre2_clk;
|
||||
struct clk *gcc_im_sleep_clk;
|
||||
struct clk *aggre2_north_clk;
|
||||
struct clk *ssc_xo_clk;
|
||||
struct clk *ssc_ahbs_clk;
|
||||
struct reset_control *ssc_bcr;
|
||||
struct reset_control *ssc_reset;
|
||||
u32 ssc_axi_halt;
|
||||
int num_pds;
|
||||
};
|
||||
|
||||
static void reg32_set_bits(char __iomem *reg, u32 value)
|
||||
{
|
||||
u32 tmp = ioread32(reg);
|
||||
|
||||
iowrite32(tmp | value, reg);
|
||||
}
|
||||
|
||||
static void reg32_clear_bits(char __iomem *reg, u32 value)
|
||||
{
|
||||
u32 tmp = ioread32(reg);
|
||||
|
||||
iowrite32(tmp & (~value), reg);
|
||||
}
|
||||
|
||||
static int qcom_ssc_block_bus_init(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct qcom_ssc_block_bus_data *data = dev_get_drvdata(dev);
|
||||
|
||||
ret = clk_prepare_enable(data->xo_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "error enabling xo_clk: %d\n", ret);
|
||||
goto err_xo_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->aggre2_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "error enabling aggre2_clk: %d\n", ret);
|
||||
goto err_aggre2_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->gcc_im_sleep_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "error enabling gcc_im_sleep_clk: %d\n", ret);
|
||||
goto err_gcc_im_sleep_clk;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to intervene here because the HW logic driving these signals cannot handle
|
||||
* initialization after power collapse by itself.
|
||||
*/
|
||||
reg32_clear_bits(data->reg_mpm_sscaon_config0,
|
||||
SSCAON_CONFIG0_CLAMP_EN_OVRD | SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL);
|
||||
/* override few_ack/rest_ack */
|
||||
reg32_clear_bits(data->reg_mpm_sscaon_config1, BIT(31));
|
||||
|
||||
ret = clk_prepare_enable(data->aggre2_north_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "error enabling aggre2_north_clk: %d\n", ret);
|
||||
goto err_aggre2_north_clk;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(data->ssc_reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "error deasserting ssc_reset: %d\n", ret);
|
||||
goto err_ssc_reset;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(data->ssc_bcr);
|
||||
if (ret) {
|
||||
dev_err(dev, "error deasserting ssc_bcr: %d\n", ret);
|
||||
goto err_ssc_bcr;
|
||||
}
|
||||
|
||||
regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 0);
|
||||
|
||||
ret = clk_prepare_enable(data->ssc_xo_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "error deasserting ssc_xo_clk: %d\n", ret);
|
||||
goto err_ssc_xo_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->ssc_ahbs_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "error deasserting ssc_ahbs_clk: %d\n", ret);
|
||||
goto err_ssc_ahbs_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_ssc_ahbs_clk:
|
||||
clk_disable(data->ssc_xo_clk);
|
||||
|
||||
err_ssc_xo_clk:
|
||||
regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 1);
|
||||
|
||||
reset_control_assert(data->ssc_bcr);
|
||||
|
||||
err_ssc_bcr:
|
||||
reset_control_assert(data->ssc_reset);
|
||||
|
||||
err_ssc_reset:
|
||||
clk_disable(data->aggre2_north_clk);
|
||||
|
||||
err_aggre2_north_clk:
|
||||
reg32_set_bits(data->reg_mpm_sscaon_config0, BIT(4) | BIT(5));
|
||||
reg32_set_bits(data->reg_mpm_sscaon_config1, BIT(31));
|
||||
|
||||
clk_disable(data->gcc_im_sleep_clk);
|
||||
|
||||
err_gcc_im_sleep_clk:
|
||||
clk_disable(data->aggre2_clk);
|
||||
|
||||
err_aggre2_clk:
|
||||
clk_disable(data->xo_clk);
|
||||
|
||||
err_xo_clk:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_ssc_block_bus_deinit(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct qcom_ssc_block_bus_data *data = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(data->ssc_xo_clk);
|
||||
clk_disable(data->ssc_ahbs_clk);
|
||||
|
||||
ret = reset_control_assert(data->ssc_bcr);
|
||||
if (ret)
|
||||
dev_err(dev, "error asserting ssc_bcr: %d\n", ret);
|
||||
|
||||
regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 1);
|
||||
|
||||
reg32_set_bits(data->reg_mpm_sscaon_config1, BIT(31));
|
||||
reg32_set_bits(data->reg_mpm_sscaon_config0, BIT(4) | BIT(5));
|
||||
|
||||
ret = reset_control_assert(data->ssc_reset);
|
||||
if (ret)
|
||||
dev_err(dev, "error asserting ssc_reset: %d\n", ret);
|
||||
|
||||
clk_disable(data->gcc_im_sleep_clk);
|
||||
|
||||
clk_disable(data->aggre2_north_clk);
|
||||
|
||||
clk_disable(data->aggre2_clk);
|
||||
clk_disable(data->xo_clk);
|
||||
}
|
||||
|
||||
static int qcom_ssc_block_bus_pds_attach(struct device *dev, struct device **pds,
|
||||
const char *const *pd_names, size_t num_pds)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_pds; i++) {
|
||||
pds[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
|
||||
if (IS_ERR_OR_NULL(pds[i])) {
|
||||
ret = PTR_ERR(pds[i]) ? : -ENODATA;
|
||||
goto unroll_attach;
|
||||
}
|
||||
}
|
||||
|
||||
return num_pds;
|
||||
|
||||
unroll_attach:
|
||||
for (i--; i >= 0; i--)
|
||||
dev_pm_domain_detach(pds[i], false);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static void qcom_ssc_block_bus_pds_detach(struct device *dev, struct device **pds, size_t num_pds)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_pds; i++)
|
||||
dev_pm_domain_detach(pds[i], false);
|
||||
}
|
||||
|
||||
static int qcom_ssc_block_bus_pds_enable(struct device **pds, size_t num_pds)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_pds; i++) {
|
||||
dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
|
||||
ret = pm_runtime_get_sync(pds[i]);
|
||||
if (ret < 0)
|
||||
goto unroll_pd_votes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unroll_pd_votes:
|
||||
for (i--; i >= 0; i--) {
|
||||
dev_pm_genpd_set_performance_state(pds[i], 0);
|
||||
pm_runtime_put(pds[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static void qcom_ssc_block_bus_pds_disable(struct device **pds, size_t num_pds)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_pds; i++) {
|
||||
dev_pm_genpd_set_performance_state(pds[i], 0);
|
||||
pm_runtime_put(pds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int qcom_ssc_block_bus_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_ssc_block_bus_data *data;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct of_phandle_args halt_args;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
data->pd_names = qcom_ssc_block_pd_names;
|
||||
data->num_pds = ARRAY_SIZE(qcom_ssc_block_pd_names);
|
||||
|
||||
/* power domains */
|
||||
ret = qcom_ssc_block_bus_pds_attach(&pdev->dev, data->pds, data->pd_names, data->num_pds);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "error when attaching power domains\n");
|
||||
|
||||
ret = qcom_ssc_block_bus_pds_enable(data->pds, data->num_pds);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "error when enabling power domains\n");
|
||||
|
||||
/* low level overrides for when the HW logic doesn't "just work" */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config0");
|
||||
data->reg_mpm_sscaon_config0 = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->reg_mpm_sscaon_config0))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config0),
|
||||
"Failed to ioremap mpm_sscaon_config0\n");
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config1");
|
||||
data->reg_mpm_sscaon_config1 = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->reg_mpm_sscaon_config1))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config1),
|
||||
"Failed to ioremap mpm_sscaon_config1\n");
|
||||
|
||||
/* resets */
|
||||
data->ssc_bcr = devm_reset_control_get_exclusive(&pdev->dev, "ssc_bcr");
|
||||
if (IS_ERR(data->ssc_bcr))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_bcr),
|
||||
"Failed to acquire reset: scc_bcr\n");
|
||||
|
||||
data->ssc_reset = devm_reset_control_get_exclusive(&pdev->dev, "ssc_reset");
|
||||
if (IS_ERR(data->ssc_reset))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_reset),
|
||||
"Failed to acquire reset: ssc_reset:\n");
|
||||
|
||||
/* clocks */
|
||||
data->xo_clk = devm_clk_get(&pdev->dev, "xo");
|
||||
if (IS_ERR(data->xo_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->xo_clk),
|
||||
"Failed to get clock: xo\n");
|
||||
|
||||
data->aggre2_clk = devm_clk_get(&pdev->dev, "aggre2");
|
||||
if (IS_ERR(data->aggre2_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_clk),
|
||||
"Failed to get clock: aggre2\n");
|
||||
|
||||
data->gcc_im_sleep_clk = devm_clk_get(&pdev->dev, "gcc_im_sleep");
|
||||
if (IS_ERR(data->gcc_im_sleep_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->gcc_im_sleep_clk),
|
||||
"Failed to get clock: gcc_im_sleep\n");
|
||||
|
||||
data->aggre2_north_clk = devm_clk_get(&pdev->dev, "aggre2_north");
|
||||
if (IS_ERR(data->aggre2_north_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_north_clk),
|
||||
"Failed to get clock: aggre2_north\n");
|
||||
|
||||
data->ssc_xo_clk = devm_clk_get(&pdev->dev, "ssc_xo");
|
||||
if (IS_ERR(data->ssc_xo_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_xo_clk),
|
||||
"Failed to get clock: ssc_xo\n");
|
||||
|
||||
data->ssc_ahbs_clk = devm_clk_get(&pdev->dev, "ssc_ahbs");
|
||||
if (IS_ERR(data->ssc_ahbs_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_ahbs_clk),
|
||||
"Failed to get clock: ssc_ahbs\n");
|
||||
|
||||
ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, "qcom,halt-regs", 1, 0,
|
||||
&halt_args);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to parse qcom,halt-regs\n");
|
||||
|
||||
data->halt_map = syscon_node_to_regmap(halt_args.np);
|
||||
of_node_put(halt_args.np);
|
||||
if (IS_ERR(data->halt_map))
|
||||
return PTR_ERR(data->halt_map);
|
||||
|
||||
data->ssc_axi_halt = halt_args.args[0];
|
||||
|
||||
qcom_ssc_block_bus_init(&pdev->dev);
|
||||
|
||||
of_platform_populate(np, NULL, NULL, &pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_ssc_block_bus_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_ssc_block_bus_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
qcom_ssc_block_bus_deinit(&pdev->dev);
|
||||
|
||||
iounmap(data->reg_mpm_sscaon_config0);
|
||||
iounmap(data->reg_mpm_sscaon_config1);
|
||||
|
||||
qcom_ssc_block_bus_pds_disable(data->pds, data->num_pds);
|
||||
qcom_ssc_block_bus_pds_detach(&pdev->dev, data->pds, data->num_pds);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_clk_destroy(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_ssc_block_bus_of_match[] = {
|
||||
{ .compatible = "qcom,ssc-block-bus", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_ssc_block_bus_of_match);
|
||||
|
||||
static struct platform_driver qcom_ssc_block_bus_driver = {
|
||||
.probe = qcom_ssc_block_bus_probe,
|
||||
.remove = qcom_ssc_block_bus_remove,
|
||||
.driver = {
|
||||
.name = "qcom-ssc-block-bus",
|
||||
.of_match_table = qcom_ssc_block_bus_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(qcom_ssc_block_bus_driver);
|
||||
|
||||
MODULE_DESCRIPTION("A driver for handling the init sequence needed for accessing the SSC block on (some) qcom SoCs over AHB");
|
||||
MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -3049,7 +3049,7 @@ static const struct soc_device_attribute sysc_soc_match[] = {
|
||||
SOC_FLAG("AM43*", SOC_AM4),
|
||||
SOC_FLAG("DRA7*", SOC_DRA7),
|
||||
|
||||
{ /* sentinel */ },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3070,7 +3070,7 @@ static const struct soc_device_attribute sysc_soc_feat_match[] = {
|
||||
SOC_FLAG("OMAP3615/AM3715", DIS_IVA),
|
||||
SOC_FLAG("OMAP3621", DIS_ISP),
|
||||
|
||||
{ /* sentinel */ },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int sysc_add_disabled(unsigned long base)
|
||||
|
@ -398,11 +398,15 @@ static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
|
||||
if (ret.a0 != FFA_SUCCESS)
|
||||
if (ret.a0 == FFA_SUCCESS) {
|
||||
if (handle)
|
||||
*handle = PACK_HANDLE(ret.a2, ret.a3);
|
||||
} else if (ret.a0 == FFA_MEM_FRAG_RX) {
|
||||
if (handle)
|
||||
*handle = PACK_HANDLE(ret.a1, ret.a2);
|
||||
} else {
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (handle)
|
||||
*handle = PACK_HANDLE(ret.a2, ret.a3);
|
||||
}
|
||||
|
||||
return frag_len;
|
||||
}
|
||||
@ -426,10 +430,12 @@ static int ffa_mem_next_frag(u64 handle, u32 frag_len)
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
|
||||
if (ret.a0 != FFA_MEM_FRAG_RX)
|
||||
return -EOPNOTSUPP;
|
||||
if (ret.a0 == FFA_MEM_FRAG_RX)
|
||||
return ret.a3;
|
||||
else if (ret.a0 == FFA_SUCCESS)
|
||||
return 0;
|
||||
|
||||
return ret.a3;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -582,7 +588,7 @@ static int ffa_partition_info_get(const char *uuid_str,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
count = ffa_partition_probe(&uuid_null, &pbuf);
|
||||
count = ffa_partition_probe(&uuid, &pbuf);
|
||||
if (count <= 0)
|
||||
return -ENOENT;
|
||||
|
||||
@ -688,8 +694,6 @@ static void ffa_setup_partitions(void)
|
||||
__func__, tpbuf->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
ffa_dev_set_drvdata(ffa_dev, drv_info);
|
||||
}
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ config ARM_SCMI_TRANSPORT_OPTEE
|
||||
depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_SHMEM
|
||||
select ARM_SCMI_HAVE_MSG
|
||||
default y
|
||||
help
|
||||
This enables the OP-TEE service based transport for SCMI.
|
||||
|
@ -178,6 +178,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
|
||||
__le32 *num_skip, *num_ret;
|
||||
u32 tot_num_ret = 0, loop_num_ret;
|
||||
struct device *dev = ph->dev;
|
||||
struct scmi_revision_info *rev = ph->get_priv(ph);
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,
|
||||
sizeof(*num_skip), 0, &t);
|
||||
@ -189,6 +190,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
|
||||
list = t->rx.buf + sizeof(*num_ret);
|
||||
|
||||
do {
|
||||
size_t real_list_sz;
|
||||
u32 calc_list_sz;
|
||||
|
||||
/* Set the number of protocols to be skipped/already read */
|
||||
*num_skip = cpu_to_le32(tot_num_ret);
|
||||
|
||||
@ -197,8 +201,30 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
|
||||
break;
|
||||
|
||||
loop_num_ret = le32_to_cpu(*num_ret);
|
||||
if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) {
|
||||
dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP");
|
||||
if (!loop_num_ret)
|
||||
break;
|
||||
|
||||
if (loop_num_ret > rev->num_protocols - tot_num_ret) {
|
||||
dev_err(dev,
|
||||
"No. Returned protocols > Total protocols.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->rx.len < (sizeof(u32) * 2)) {
|
||||
dev_err(dev, "Truncated reply - rx.len:%zd\n",
|
||||
t->rx.len);
|
||||
ret = -EPROTO;
|
||||
break;
|
||||
}
|
||||
|
||||
real_list_sz = t->rx.len - sizeof(u32);
|
||||
calc_list_sz = (1 + (loop_num_ret - 1) / sizeof(u32)) *
|
||||
sizeof(u32);
|
||||
if (calc_list_sz != real_list_sz) {
|
||||
dev_err(dev,
|
||||
"Malformed reply - real_sz:%zd calc_sz:%u\n",
|
||||
real_list_sz, calc_list_sz);
|
||||
ret = -EPROTO;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -208,7 +234,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
|
||||
tot_num_ret += loop_num_ret;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, t);
|
||||
} while (loop_num_ret);
|
||||
} while (tot_num_ret < rev->num_protocols);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
@ -351,15 +377,19 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
|
||||
if (!prot_imp)
|
||||
return -ENOMEM;
|
||||
|
||||
rev->major_ver = PROTOCOL_REV_MAJOR(version),
|
||||
rev->minor_ver = PROTOCOL_REV_MINOR(version);
|
||||
ph->set_priv(ph, rev);
|
||||
|
||||
scmi_base_attributes_get(ph);
|
||||
ret = scmi_base_attributes_get(ph);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
prot_imp = devm_kcalloc(dev, rev->num_protocols, sizeof(u8),
|
||||
GFP_KERNEL);
|
||||
if (!prot_imp)
|
||||
return -ENOMEM;
|
||||
|
||||
scmi_base_vendor_id_get(ph, false);
|
||||
scmi_base_vendor_id_get(ph, true);
|
||||
scmi_base_implementation_version_get(ph);
|
||||
|
@ -2,13 +2,15 @@
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Clock Protocol
|
||||
*
|
||||
* Copyright (C) 2018-2021 ARM Ltd.
|
||||
* Copyright (C) 2018-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_clock_protocol_cmd {
|
||||
CLOCK_ATTRIBUTES = 0x3,
|
||||
@ -16,6 +18,9 @@ enum scmi_clock_protocol_cmd {
|
||||
CLOCK_RATE_SET = 0x5,
|
||||
CLOCK_RATE_GET = 0x6,
|
||||
CLOCK_CONFIG_SET = 0x7,
|
||||
CLOCK_NAME_GET = 0x8,
|
||||
CLOCK_RATE_NOTIFY = 0x9,
|
||||
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_clock_protocol_attributes {
|
||||
@ -27,7 +32,10 @@ struct scmi_msg_resp_clock_protocol_attributes {
|
||||
struct scmi_msg_resp_clock_attributes {
|
||||
__le32 attributes;
|
||||
#define CLOCK_ENABLE BIT(0)
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
#define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31))
|
||||
#define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
__le32 clock_enable_latency;
|
||||
};
|
||||
|
||||
@ -68,6 +76,24 @@ struct scmi_clock_set_rate {
|
||||
__le32 value_high;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_set_rate_complete {
|
||||
__le32 id;
|
||||
__le32 rate_low;
|
||||
__le32 rate_high;
|
||||
};
|
||||
|
||||
struct scmi_msg_clock_rate_notify {
|
||||
__le32 clk_id;
|
||||
__le32 notify_enable;
|
||||
};
|
||||
|
||||
struct scmi_clock_rate_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 clock_id;
|
||||
__le32 rate_low;
|
||||
__le32 rate_high;
|
||||
};
|
||||
|
||||
struct clock_info {
|
||||
u32 version;
|
||||
int num_clocks;
|
||||
@ -76,6 +102,11 @@ struct clock_info {
|
||||
struct scmi_clock_info *clk;
|
||||
};
|
||||
|
||||
static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
|
||||
CLOCK_RATE_NOTIFY,
|
||||
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
|
||||
};
|
||||
|
||||
static int
|
||||
scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct clock_info *ci)
|
||||
@ -102,9 +133,11 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, struct scmi_clock_info *clk)
|
||||
u32 clk_id, struct scmi_clock_info *clk,
|
||||
u32 version)
|
||||
{
|
||||
int ret;
|
||||
u32 attributes;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_clock_attributes *attr;
|
||||
|
||||
@ -118,16 +151,33 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
u32 latency = 0;
|
||||
attributes = le32_to_cpu(attr->attributes);
|
||||
strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
|
||||
/* Is optional field clock_enable_latency provided ? */
|
||||
if (t->rx.len == sizeof(*attr))
|
||||
clk->enable_latency =
|
||||
le32_to_cpu(attr->clock_enable_latency);
|
||||
} else {
|
||||
clk->name[0] = '\0';
|
||||
/* clock_enable_latency field is present only since SCMI v3.1 */
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x2)
|
||||
latency = le32_to_cpu(attr->clock_enable_latency);
|
||||
clk->enable_latency = latency ? : U32_MAX;
|
||||
}
|
||||
|
||||
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 && PROTOCOL_REV_MAJOR(version) >= 0x2) {
|
||||
if (SUPPORTS_EXTENDED_NAMES(attributes))
|
||||
ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id,
|
||||
clk->name,
|
||||
SCMI_MAX_STR_SIZE);
|
||||
|
||||
if (SUPPORTS_RATE_CHANGED_NOTIF(attributes))
|
||||
clk->rate_changed_notifications = true;
|
||||
if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
|
||||
clk->rate_change_requested_notifications = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -143,81 +193,111 @@ static int rate_cmp_func(const void *_r1, const void *_r2)
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct scmi_clk_ipriv {
|
||||
u32 clk_id;
|
||||
struct scmi_clock_info *clk;
|
||||
};
|
||||
|
||||
static void iter_clk_describe_prepare_message(void *message,
|
||||
const unsigned int desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_msg_clock_describe_rates *msg = message;
|
||||
const struct scmi_clk_ipriv *p = priv;
|
||||
|
||||
msg->id = cpu_to_le32(p->clk_id);
|
||||
/* Set the number of rates to be skipped/already read */
|
||||
msg->rate_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
static int
|
||||
iter_clk_describe_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
u32 flags;
|
||||
struct scmi_clk_ipriv *p = priv;
|
||||
const struct scmi_msg_resp_clock_describe_rates *r = response;
|
||||
|
||||
flags = le32_to_cpu(r->num_rates_flags);
|
||||
st->num_remaining = NUM_REMAINING(flags);
|
||||
st->num_returned = NUM_RETURNED(flags);
|
||||
p->clk->rate_discrete = RATE_DISCRETE(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iter_clk_describe_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scmi_clk_ipriv *p = priv;
|
||||
const struct scmi_msg_resp_clock_describe_rates *r = response;
|
||||
|
||||
if (!p->clk->rate_discrete) {
|
||||
switch (st->desc_index + st->loop_idx) {
|
||||
case 0:
|
||||
p->clk->range.min_rate = RATE_TO_U64(r->rate[0]);
|
||||
break;
|
||||
case 1:
|
||||
p->clk->range.max_rate = RATE_TO_U64(r->rate[1]);
|
||||
break;
|
||||
case 2:
|
||||
p->clk->range.step_size = RATE_TO_U64(r->rate[2]);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
u64 *rate = &p->clk->list.rates[st->desc_index + st->loop_idx];
|
||||
|
||||
*rate = RATE_TO_U64(r->rate[st->loop_idx]);
|
||||
p->clk->list.num_rates++;
|
||||
//XXX dev_dbg(ph->dev, "Rate %llu Hz\n", *rate);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
struct scmi_clock_info *clk)
|
||||
{
|
||||
u64 *rate = NULL;
|
||||
int ret, cnt;
|
||||
bool rate_discrete = false;
|
||||
u32 tot_rate_cnt = 0, rates_flag;
|
||||
u16 num_returned, num_remaining;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_clock_describe_rates *clk_desc;
|
||||
struct scmi_msg_resp_clock_describe_rates *rlist;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_DESCRIBE_RATES,
|
||||
sizeof(*clk_desc), 0, &t);
|
||||
void *iter;
|
||||
struct scmi_msg_clock_describe_rates *msg;
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_clk_describe_prepare_message,
|
||||
.update_state = iter_clk_describe_update_state,
|
||||
.process_response = iter_clk_describe_process_response,
|
||||
};
|
||||
struct scmi_clk_ipriv cpriv = {
|
||||
.clk_id = clk_id,
|
||||
.clk = clk,
|
||||
};
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, SCMI_MAX_NUM_RATES,
|
||||
CLOCK_DESCRIBE_RATES,
|
||||
sizeof(*msg), &cpriv);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
ret = ph->hops->iter_response_run(iter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_desc = t->tx.buf;
|
||||
rlist = t->rx.buf;
|
||||
|
||||
do {
|
||||
clk_desc->id = cpu_to_le32(clk_id);
|
||||
/* Set the number of rates to be skipped/already read */
|
||||
clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
rates_flag = le32_to_cpu(rlist->num_rates_flags);
|
||||
num_remaining = NUM_REMAINING(rates_flag);
|
||||
rate_discrete = RATE_DISCRETE(rates_flag);
|
||||
num_returned = NUM_RETURNED(rates_flag);
|
||||
|
||||
if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) {
|
||||
dev_err(ph->dev, "No. of rates > MAX_NUM_RATES");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rate_discrete) {
|
||||
clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
|
||||
clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
|
||||
clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
|
||||
dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
|
||||
clk->range.min_rate, clk->range.max_rate,
|
||||
clk->range.step_size);
|
||||
break;
|
||||
}
|
||||
|
||||
rate = &clk->list.rates[tot_rate_cnt];
|
||||
for (cnt = 0; cnt < num_returned; cnt++, rate++) {
|
||||
*rate = RATE_TO_U64(rlist->rate[cnt]);
|
||||
dev_dbg(ph->dev, "Rate %llu Hz\n", *rate);
|
||||
}
|
||||
|
||||
tot_rate_cnt += num_returned;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, t);
|
||||
/*
|
||||
* check for both returned and remaining to avoid infinite
|
||||
* loop due to buggy firmware
|
||||
*/
|
||||
} while (num_returned && num_remaining);
|
||||
|
||||
if (rate_discrete && rate) {
|
||||
clk->list.num_rates = tot_rate_cnt;
|
||||
sort(clk->list.rates, tot_rate_cnt, sizeof(*rate),
|
||||
rate_cmp_func, NULL);
|
||||
if (!clk->rate_discrete) {
|
||||
dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
|
||||
clk->range.min_rate, clk->range.max_rate,
|
||||
clk->range.step_size);
|
||||
} else if (clk->list.num_rates) {
|
||||
sort(clk->list.rates, clk->list.num_rates,
|
||||
sizeof(clk->list.rates[0]), rate_cmp_func, NULL);
|
||||
}
|
||||
|
||||
clk->rate_discrete = rate_discrete;
|
||||
|
||||
err:
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -266,10 +346,22 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
|
||||
cfg->value_low = cpu_to_le32(rate & 0xffffffff);
|
||||
cfg->value_high = cpu_to_le32(rate >> 32);
|
||||
|
||||
if (flags & CLOCK_SET_ASYNC)
|
||||
if (flags & CLOCK_SET_ASYNC) {
|
||||
ret = ph->xops->do_xfer_with_response(ph, t);
|
||||
else
|
||||
if (!ret) {
|
||||
struct scmi_msg_resp_set_rate_complete *resp;
|
||||
|
||||
resp = t->rx.buf;
|
||||
if (le32_to_cpu(resp->id) == clk_id)
|
||||
dev_dbg(ph->dev,
|
||||
"Clk ID %d set async to %llu\n", clk_id,
|
||||
get_unaligned_le64(&resp->rate_low));
|
||||
else
|
||||
ret = -EPROTO;
|
||||
}
|
||||
} else {
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
}
|
||||
|
||||
if (ci->max_async_req)
|
||||
atomic_dec(&ci->cur_async_req);
|
||||
@ -355,13 +447,111 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
|
||||
.disable_atomic = scmi_clock_disable_atomic,
|
||||
};
|
||||
|
||||
static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, int message_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_clock_rate_notify *notify;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, message_id, sizeof(*notify), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
notify = t->tx.buf;
|
||||
notify->clk_id = cpu_to_le32(clk_id);
|
||||
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clk_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret, cmd_id;
|
||||
|
||||
if (evt_id >= ARRAY_SIZE(evt_2_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
cmd_id = evt_2_cmd[evt_id];
|
||||
ret = scmi_clk_rate_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);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *scmi_clk_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)
|
||||
{
|
||||
const struct scmi_clock_rate_notify_payld *p = payld;
|
||||
struct scmi_clock_rate_notif_report *r = report;
|
||||
|
||||
if (sizeof(*p) != payld_sz ||
|
||||
(evt_id != SCMI_EVENT_CLOCK_RATE_CHANGED &&
|
||||
evt_id != SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED))
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->clock_id = le32_to_cpu(p->clock_id);
|
||||
r->rate = get_unaligned_le64(&p->rate_low);
|
||||
*src_id = r->clock_id;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int scmi_clk_get_num_sources(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
if (!ci)
|
||||
return -EINVAL;
|
||||
|
||||
return ci->num_clocks;
|
||||
}
|
||||
|
||||
static const struct scmi_event clk_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_CLOCK_RATE_CHANGED,
|
||||
.max_payld_sz = sizeof(struct scmi_clock_rate_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_clock_rate_notif_report),
|
||||
},
|
||||
{
|
||||
.id = SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED,
|
||||
.max_payld_sz = sizeof(struct scmi_clock_rate_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_clock_rate_notif_report),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops clk_event_ops = {
|
||||
.get_num_sources = scmi_clk_get_num_sources,
|
||||
.set_notify_enabled = scmi_clk_set_notify_enabled,
|
||||
.fill_custom_report = scmi_clk_fill_custom_report,
|
||||
};
|
||||
|
||||
static const struct scmi_protocol_events clk_protocol_events = {
|
||||
.queue_sz = SCMI_PROTO_QUEUE_SZ,
|
||||
.ops = &clk_event_ops,
|
||||
.evts = clk_events,
|
||||
.num_events = ARRAY_SIZE(clk_events),
|
||||
};
|
||||
|
||||
static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
u32 version;
|
||||
int clkid, ret;
|
||||
struct clock_info *cinfo;
|
||||
|
||||
ph->xops->version_get(ph, &version);
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Clock Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
@ -370,7 +560,9 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (!cinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
scmi_clock_protocol_attributes_get(ph, cinfo);
|
||||
ret = scmi_clock_protocol_attributes_get(ph, cinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks,
|
||||
sizeof(*cinfo->clk), GFP_KERNEL);
|
||||
@ -380,7 +572,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
|
||||
struct scmi_clock_info *clk = cinfo->clk + clkid;
|
||||
|
||||
ret = scmi_clock_attributes_get(ph, clkid, clk);
|
||||
ret = scmi_clock_attributes_get(ph, clkid, clk, version);
|
||||
if (!ret)
|
||||
scmi_clock_describe_rates_get(ph, clkid, clk);
|
||||
}
|
||||
@ -394,6 +586,7 @@ static const struct scmi_protocol scmi_clock = {
|
||||
.owner = THIS_MODULE,
|
||||
.instance_init = &scmi_clock_protocol_init,
|
||||
.ops = &clk_proto_ops,
|
||||
.events = &clk_protocol_events,
|
||||
};
|
||||
|
||||
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(clock, scmi_clock)
|
||||
|
@ -4,7 +4,7 @@
|
||||
* driver common header file containing some definitions, structures
|
||||
* and function prototypes used in all the different SCMI protocols.
|
||||
*
|
||||
* Copyright (C) 2018-2021 ARM Ltd.
|
||||
* Copyright (C) 2018-2022 ARM Ltd.
|
||||
*/
|
||||
#ifndef _SCMI_COMMON_H
|
||||
#define _SCMI_COMMON_H
|
||||
@ -24,38 +24,9 @@
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
|
||||
#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
|
||||
#define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))
|
||||
#define PROTOCOL_REV_MINOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x)))
|
||||
#define MAX_PROTOCOLS_IMP 16
|
||||
#define MAX_OPPS 16
|
||||
|
||||
enum scmi_common_cmd {
|
||||
PROTOCOL_VERSION = 0x0,
|
||||
PROTOCOL_ATTRIBUTES = 0x1,
|
||||
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_msg_resp_prot_version - Response for a message
|
||||
*
|
||||
* @minor_version: Minor version of the ABI that firmware supports
|
||||
* @major_version: Major version of the ABI that firmware supports
|
||||
*
|
||||
* In general, ABI version changes follow the rule that minor version increments
|
||||
* are backward compatible. Major revision changes in ABI may not be
|
||||
* backward compatible.
|
||||
*
|
||||
* Response to a generic message with message type SCMI_MSG_VERSION
|
||||
*/
|
||||
struct scmi_msg_resp_prot_version {
|
||||
__le16 minor_version;
|
||||
__le16 major_version;
|
||||
};
|
||||
|
||||
#define MSG_ID_MASK GENMASK(7, 0)
|
||||
#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
|
||||
#define MSG_TYPE_MASK GENMASK(9, 8)
|
||||
@ -79,28 +50,6 @@ struct scmi_msg_resp_prot_version {
|
||||
*/
|
||||
#define SCMI_PENDING_XFERS_HT_ORDER_SZ 9
|
||||
|
||||
/**
|
||||
* struct scmi_msg_hdr - Message(Tx/Rx) header
|
||||
*
|
||||
* @id: The identifier of the message being sent
|
||||
* @protocol_id: The identifier of the protocol used to send @id message
|
||||
* @type: The SCMI type for this message
|
||||
* @seq: The token to identify the message. When a message returns, the
|
||||
* platform returns the whole message header unmodified including the
|
||||
* token
|
||||
* @status: Status of the transfer once it's complete
|
||||
* @poll_completion: Indicate if the transfer needs to be polled for
|
||||
* completion or interrupt mode is used
|
||||
*/
|
||||
struct scmi_msg_hdr {
|
||||
u8 id;
|
||||
u8 protocol_id;
|
||||
u8 type;
|
||||
u16 seq;
|
||||
u32 status;
|
||||
bool poll_completion;
|
||||
};
|
||||
|
||||
/**
|
||||
* pack_scmi_header() - packs and returns 32-bit header
|
||||
*
|
||||
@ -130,72 +79,6 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
|
||||
hdr->type = MSG_XTRACT_TYPE(msg_hdr);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct scmi_msg - Message(Tx/Rx) structure
|
||||
*
|
||||
* @buf: Buffer pointer
|
||||
* @len: Length of data in the Buffer
|
||||
*/
|
||||
struct scmi_msg {
|
||||
void *buf;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_xfer - Structure representing a message flow
|
||||
*
|
||||
* @transfer_id: Unique ID for debug & profiling purpose
|
||||
* @hdr: Transmit message header
|
||||
* @tx: Transmit message
|
||||
* @rx: Receive message, the buffer should be pre-allocated to store
|
||||
* message. If request-ACK protocol is used, we can reuse the same
|
||||
* buffer for the rx path as we use for the tx path.
|
||||
* @done: command message transmit completion event
|
||||
* @async_done: pointer to delayed response message received event completion
|
||||
* @pending: True for xfers added to @pending_xfers hashtable
|
||||
* @node: An hlist_node reference used to store this xfer, alternatively, on
|
||||
* the free list @free_xfers or in the @pending_xfers hashtable
|
||||
* @users: A refcount to track the active users for this xfer.
|
||||
* This is meant to protect against the possibility that, when a command
|
||||
* transaction times out concurrently with the reception of a valid
|
||||
* response message, the xfer could be finally put on the TX path, and
|
||||
* so vanish, while on the RX path scmi_rx_callback() is still
|
||||
* processing it: in such a case this refcounting will ensure that, even
|
||||
* though the timed-out transaction will anyway cause the command
|
||||
* request to be reported as failed by time-out, the underlying xfer
|
||||
* cannot be discarded and possibly reused until the last one user on
|
||||
* the RX path has released it.
|
||||
* @busy: An atomic flag to ensure exclusive write access to this xfer
|
||||
* @state: The current state of this transfer, with states transitions deemed
|
||||
* valid being:
|
||||
* - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
|
||||
* - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
|
||||
* (Missing synchronous response is assumed OK and ignored)
|
||||
* @lock: A spinlock to protect state and busy fields.
|
||||
* @priv: A pointer for transport private usage.
|
||||
*/
|
||||
struct scmi_xfer {
|
||||
int transfer_id;
|
||||
struct scmi_msg_hdr hdr;
|
||||
struct scmi_msg tx;
|
||||
struct scmi_msg rx;
|
||||
struct completion done;
|
||||
struct completion *async_done;
|
||||
bool pending;
|
||||
struct hlist_node node;
|
||||
refcount_t users;
|
||||
#define SCMI_XFER_FREE 0
|
||||
#define SCMI_XFER_BUSY 1
|
||||
atomic_t busy;
|
||||
#define SCMI_XFER_SENT_OK 0
|
||||
#define SCMI_XFER_RESP_OK 1
|
||||
#define SCMI_XFER_DRESP_OK 2
|
||||
int state;
|
||||
/* A lock to protect state and busy fields */
|
||||
spinlock_t lock;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/*
|
||||
* An helper macro to lookup an xfer from the @pending_xfers hashtable
|
||||
* using the message sequence number token as a key.
|
||||
@ -211,64 +94,6 @@ struct scmi_xfer {
|
||||
xfer_; \
|
||||
})
|
||||
|
||||
struct scmi_xfer_ops;
|
||||
|
||||
/**
|
||||
* struct scmi_protocol_handle - Reference to an initialized protocol instance
|
||||
*
|
||||
* @dev: A reference to the associated SCMI instance device (handle->dev).
|
||||
* @xops: A reference to a struct holding refs to the core xfer operations that
|
||||
* can be used by the protocol implementation to generate SCMI messages.
|
||||
* @set_priv: A method to set protocol private data for this instance.
|
||||
* @get_priv: A method to get protocol private data previously set.
|
||||
*
|
||||
* This structure represents a protocol initialized against specific SCMI
|
||||
* instance and it will be used as follows:
|
||||
* - as a parameter fed from the core to the protocol initialization code so
|
||||
* that it can access the core xfer operations to build and generate SCMI
|
||||
* messages exclusively for the specific underlying protocol instance.
|
||||
* - as an opaque handle fed by an SCMI driver user when it tries to access
|
||||
* this protocol through its own protocol operations.
|
||||
* In this case this handle will be returned as an opaque object together
|
||||
* with the related protocol operations when the SCMI driver tries to access
|
||||
* the protocol.
|
||||
*/
|
||||
struct scmi_protocol_handle {
|
||||
struct device *dev;
|
||||
const struct scmi_xfer_ops *xops;
|
||||
int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
|
||||
void *(*get_priv)(const struct scmi_protocol_handle *ph);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_xfer_ops - References to the core SCMI xfer operations.
|
||||
* @version_get: Get this version protocol.
|
||||
* @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
|
||||
* @reset_rx_to_maxsz: Reset rx size to max transport size.
|
||||
* @do_xfer: Do the SCMI transfer.
|
||||
* @do_xfer_with_response: Do the SCMI transfer waiting for a response.
|
||||
* @xfer_put: Free the xfer slot.
|
||||
*
|
||||
* Note that all this operations expect a protocol handle as first parameter;
|
||||
* they then internally use it to infer the underlying protocol number: this
|
||||
* way is not possible for a protocol implementation to forge messages for
|
||||
* another protocol.
|
||||
*/
|
||||
struct scmi_xfer_ops {
|
||||
int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
|
||||
int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
|
||||
size_t tx_size, size_t rx_size,
|
||||
struct scmi_xfer **p);
|
||||
void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
int (*do_xfer)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
void (*xfer_put)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
};
|
||||
|
||||
struct scmi_revision_info *
|
||||
scmi_revision_area_get(const struct scmi_protocol_handle *ph);
|
||||
int scmi_handle_put(const struct scmi_handle *handle);
|
||||
@ -277,55 +102,9 @@ void scmi_set_handle(struct scmi_device *scmi_dev);
|
||||
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
|
||||
u8 *prot_imp);
|
||||
|
||||
typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
|
||||
|
||||
/**
|
||||
* struct scmi_protocol - Protocol descriptor
|
||||
* @id: Protocol ID.
|
||||
* @owner: Module reference if any.
|
||||
* @instance_init: Mandatory protocol initialization function.
|
||||
* @instance_deinit: Optional protocol de-initialization function.
|
||||
* @ops: Optional reference to the operations provided by the protocol and
|
||||
* exposed in scmi_protocol.h.
|
||||
* @events: An optional reference to the events supported by this protocol.
|
||||
*/
|
||||
struct scmi_protocol {
|
||||
const u8 id;
|
||||
struct module *owner;
|
||||
const scmi_prot_init_ph_fn_t instance_init;
|
||||
const scmi_prot_init_ph_fn_t instance_deinit;
|
||||
const void *ops;
|
||||
const struct scmi_protocol_events *events;
|
||||
};
|
||||
|
||||
int __init scmi_bus_init(void);
|
||||
void __exit scmi_bus_exit(void);
|
||||
|
||||
#define DECLARE_SCMI_REGISTER_UNREGISTER(func) \
|
||||
int __init scmi_##func##_register(void); \
|
||||
void __exit scmi_##func##_unregister(void)
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(base);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(clock);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(perf);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(power);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(system);
|
||||
|
||||
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
|
||||
static const struct scmi_protocol *__this_proto = &(proto); \
|
||||
\
|
||||
int __init scmi_##name##_register(void) \
|
||||
{ \
|
||||
return scmi_protocol_register(__this_proto); \
|
||||
} \
|
||||
\
|
||||
void __exit scmi_##name##_unregister(void) \
|
||||
{ \
|
||||
scmi_protocol_unregister(__this_proto); \
|
||||
}
|
||||
|
||||
const struct scmi_protocol *scmi_protocol_get(int protocol_id);
|
||||
void scmi_protocol_put(int protocol_id);
|
||||
|
||||
|
@ -128,7 +128,8 @@ struct scmi_protocol_instance {
|
||||
* usage.
|
||||
* @protocols_mtx: A mutex to protect protocols instances initialization.
|
||||
* @protocols_imp: List of protocols implemented, currently maximum of
|
||||
* MAX_PROTOCOLS_IMP elements allocated by the base protocol
|
||||
* scmi_revision_info.num_protocols elements allocated by the
|
||||
* base protocol
|
||||
* @active_protocols: IDR storing device_nodes for protocols actually defined
|
||||
* in the DT and confirmed as implemented by fw.
|
||||
* @atomic_threshold: Optional system wide DT-configured threshold, expressed
|
||||
@ -1102,6 +1103,167 @@ static const struct scmi_xfer_ops xfer_ops = {
|
||||
.xfer_put = xfer_put,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_domain_name_get {
|
||||
__le32 flags;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* scmi_common_extended_name_get - Common helper to get extended resources name
|
||||
* @ph: A protocol handle reference.
|
||||
* @cmd_id: The specific command ID to use.
|
||||
* @res_id: The specific resource ID to use.
|
||||
* @name: A pointer to the preallocated area where the retrieved name will be
|
||||
* stored as a NULL terminated string.
|
||||
* @len: The len in bytes of the @name char array.
|
||||
*
|
||||
* Return: 0 on Succcess
|
||||
*/
|
||||
static int scmi_common_extended_name_get(const struct scmi_protocol_handle *ph,
|
||||
u8 cmd_id, u32 res_id, char *name,
|
||||
size_t len)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_domain_name_get *resp;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(res_id),
|
||||
sizeof(*resp), &t);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
put_unaligned_le32(res_id, t->tx.buf);
|
||||
resp = t->rx.buf;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret)
|
||||
strscpy(name, resp->name, len);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
out:
|
||||
if (ret)
|
||||
dev_warn(ph->dev,
|
||||
"Failed to get extended name - id:%u (ret:%d). Using %s\n",
|
||||
res_id, ret, name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct scmi_iterator - Iterator descriptor
|
||||
* @msg: A reference to the message TX buffer; filled by @prepare_message with
|
||||
* a proper custom command payload for each multi-part command request.
|
||||
* @resp: A reference to the response RX buffer; used by @update_state and
|
||||
* @process_response to parse the multi-part replies.
|
||||
* @t: A reference to the underlying xfer initialized and used transparently by
|
||||
* the iterator internal routines.
|
||||
* @ph: A reference to the associated protocol handle to be used.
|
||||
* @ops: A reference to the custom provided iterator operations.
|
||||
* @state: The current iterator state; used and updated in turn by the iterators
|
||||
* internal routines and by the caller-provided @scmi_iterator_ops.
|
||||
* @priv: A reference to optional private data as provided by the caller and
|
||||
* passed back to the @@scmi_iterator_ops.
|
||||
*/
|
||||
struct scmi_iterator {
|
||||
void *msg;
|
||||
void *resp;
|
||||
struct scmi_xfer *t;
|
||||
const struct scmi_protocol_handle *ph;
|
||||
struct scmi_iterator_ops *ops;
|
||||
struct scmi_iterator_state state;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
static void *scmi_iterator_init(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_iterator_ops *ops,
|
||||
unsigned int max_resources, u8 msg_id,
|
||||
size_t tx_size, void *priv)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_iterator *i;
|
||||
|
||||
i = devm_kzalloc(ph->dev, sizeof(*i), GFP_KERNEL);
|
||||
if (!i)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
i->ph = ph;
|
||||
i->ops = ops;
|
||||
i->priv = priv;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, msg_id, tx_size, 0, &i->t);
|
||||
if (ret) {
|
||||
devm_kfree(ph->dev, i);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
i->state.max_resources = max_resources;
|
||||
i->msg = i->t->tx.buf;
|
||||
i->resp = i->t->rx.buf;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int scmi_iterator_run(void *iter)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct scmi_iterator_ops *iops;
|
||||
const struct scmi_protocol_handle *ph;
|
||||
struct scmi_iterator_state *st;
|
||||
struct scmi_iterator *i = iter;
|
||||
|
||||
if (!i || !i->ops || !i->ph)
|
||||
return ret;
|
||||
|
||||
iops = i->ops;
|
||||
ph = i->ph;
|
||||
st = &i->state;
|
||||
|
||||
do {
|
||||
iops->prepare_message(i->msg, st->desc_index, i->priv);
|
||||
ret = ph->xops->do_xfer(ph, i->t);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = iops->update_state(st, i->resp, i->priv);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (st->num_returned > st->max_resources - st->desc_index) {
|
||||
dev_err(ph->dev,
|
||||
"No. of resources can't exceed %d\n",
|
||||
st->max_resources);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (st->loop_idx = 0; st->loop_idx < st->num_returned;
|
||||
st->loop_idx++) {
|
||||
ret = iops->process_response(ph, i->resp, st, i->priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
st->desc_index += st->num_returned;
|
||||
ph->xops->reset_rx_to_maxsz(ph, i->t);
|
||||
/*
|
||||
* check for both returned and remaining to avoid infinite
|
||||
* loop due to buggy firmware
|
||||
*/
|
||||
} while (st->num_returned && st->num_remaining);
|
||||
|
||||
out:
|
||||
/* Finalize and destroy iterator */
|
||||
ph->xops->xfer_put(ph, i->t);
|
||||
devm_kfree(ph->dev, i);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
/**
|
||||
* scmi_revision_area_get - Retrieve version memory area.
|
||||
*
|
||||
@ -1162,6 +1324,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
|
||||
pi->handle = handle;
|
||||
pi->ph.dev = handle->dev;
|
||||
pi->ph.xops = &xfer_ops;
|
||||
pi->ph.hops = &helpers_ops;
|
||||
pi->ph.set_priv = scmi_set_protocol_priv;
|
||||
pi->ph.get_priv = scmi_get_protocol_priv;
|
||||
refcount_set(&pi->users, 1);
|
||||
@ -1310,11 +1473,12 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
|
||||
{
|
||||
int i;
|
||||
struct scmi_info *info = handle_to_scmi_info(handle);
|
||||
struct scmi_revision_info *rev = handle->version;
|
||||
|
||||
if (!info->protocols_imp)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
|
||||
for (i = 0; i < rev->num_protocols; i++)
|
||||
if (info->protocols_imp[i] == prot_id)
|
||||
return true;
|
||||
return false;
|
||||
|
@ -64,6 +64,22 @@ enum scmi_optee_pta_cmd {
|
||||
* [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps)
|
||||
*/
|
||||
PTA_SCMI_CMD_GET_CHANNEL = 3,
|
||||
|
||||
/*
|
||||
* PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG
|
||||
* buffer pointed by memref parameters
|
||||
*
|
||||
* [in] value[0].a: Channel handle
|
||||
* [in] memref[1]: Message buffer (MSG and SCMI payload)
|
||||
* [out] memref[2]: Response buffer (MSG and SCMI payload)
|
||||
*
|
||||
* Shared memories used for SCMI message/response are MSG buffers
|
||||
* referenced by param[1] and param[2]. MSG transport protocol
|
||||
* uses a 32bit header to carry SCMI meta-data (protocol ID and
|
||||
* protocol message ID) followed by the effective SCMI message
|
||||
* payload.
|
||||
*/
|
||||
PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -72,9 +88,17 @@ enum scmi_optee_pta_cmd {
|
||||
* PTA_SCMI_CAPS_SMT_HEADER
|
||||
* When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in
|
||||
* shared memory buffers to carry SCMI protocol synchronisation information.
|
||||
*
|
||||
* PTA_SCMI_CAPS_MSG_HEADER
|
||||
* When set, OP-TEE supports command using MSG header protocol in an OP-TEE
|
||||
* shared memory to carry SCMI protocol synchronisation information and SCMI
|
||||
* message payload.
|
||||
*/
|
||||
#define PTA_SCMI_CAPS_NONE 0
|
||||
#define PTA_SCMI_CAPS_SMT_HEADER BIT(0)
|
||||
#define PTA_SCMI_CAPS_MSG_HEADER BIT(1)
|
||||
#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \
|
||||
PTA_SCMI_CAPS_MSG_HEADER)
|
||||
|
||||
/**
|
||||
* struct scmi_optee_channel - Description of an OP-TEE SCMI channel
|
||||
@ -85,7 +109,8 @@ enum scmi_optee_pta_cmd {
|
||||
* @mu: Mutex protection on channel access
|
||||
* @cinfo: SCMI channel information
|
||||
* @shmem: Virtual base address of the shared memory
|
||||
* @tee_shm: Reference to TEE shared memory or NULL if using static shmem
|
||||
* @req: Shared memory protocol handle for SCMI request and synchronous response
|
||||
* @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem
|
||||
* @link: Reference in agent's channel list
|
||||
*/
|
||||
struct scmi_optee_channel {
|
||||
@ -94,7 +119,10 @@ struct scmi_optee_channel {
|
||||
u32 caps;
|
||||
struct mutex mu;
|
||||
struct scmi_chan_info *cinfo;
|
||||
struct scmi_shared_mem __iomem *shmem;
|
||||
union {
|
||||
struct scmi_shared_mem __iomem *shmem;
|
||||
struct scmi_msg_payld *msg;
|
||||
} req;
|
||||
struct tee_shm *tee_shm;
|
||||
struct list_head link;
|
||||
};
|
||||
@ -178,8 +206,8 @@ static int get_capabilities(struct scmi_optee_agent *agent)
|
||||
|
||||
caps = param[0].u.value.a;
|
||||
|
||||
if (!(caps & PTA_SCMI_CAPS_SMT_HEADER)) {
|
||||
dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT\n");
|
||||
if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) {
|
||||
dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -193,9 +221,14 @@ static int get_channel(struct scmi_optee_channel *channel)
|
||||
struct device *dev = scmi_optee_private->dev;
|
||||
struct tee_ioctl_invoke_arg arg = { };
|
||||
struct tee_param param[1] = { };
|
||||
unsigned int caps = PTA_SCMI_CAPS_SMT_HEADER;
|
||||
unsigned int caps = 0;
|
||||
int ret;
|
||||
|
||||
if (channel->tee_shm)
|
||||
caps = PTA_SCMI_CAPS_MSG_HEADER;
|
||||
else
|
||||
caps = PTA_SCMI_CAPS_SMT_HEADER;
|
||||
|
||||
arg.func = PTA_SCMI_CMD_GET_CHANNEL;
|
||||
arg.session = channel->tee_session;
|
||||
arg.num_params = 1;
|
||||
@ -220,25 +253,48 @@ static int get_channel(struct scmi_optee_channel *channel)
|
||||
|
||||
static int invoke_process_smt_channel(struct scmi_optee_channel *channel)
|
||||
{
|
||||
struct tee_ioctl_invoke_arg arg = { };
|
||||
struct tee_param param[2] = { };
|
||||
struct tee_ioctl_invoke_arg arg = {
|
||||
.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL,
|
||||
.session = channel->tee_session,
|
||||
.num_params = 1,
|
||||
};
|
||||
struct tee_param param[1] = { };
|
||||
int ret;
|
||||
|
||||
arg.session = channel->tee_session;
|
||||
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
|
||||
param[0].u.value.a = channel->channel_id;
|
||||
|
||||
if (channel->tee_shm) {
|
||||
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
|
||||
param[1].u.memref.shm = channel->tee_shm;
|
||||
param[1].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE;
|
||||
arg.num_params = 2;
|
||||
arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE;
|
||||
} else {
|
||||
arg.num_params = 1;
|
||||
arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL;
|
||||
ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);
|
||||
if (ret < 0 || arg.ret) {
|
||||
dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",
|
||||
channel->channel_id, ret, arg.ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size)
|
||||
{
|
||||
struct tee_ioctl_invoke_arg arg = {
|
||||
.func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL,
|
||||
.session = channel->tee_session,
|
||||
.num_params = 3,
|
||||
};
|
||||
struct tee_param param[3] = { };
|
||||
int ret;
|
||||
|
||||
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
|
||||
param[0].u.value.a = channel->channel_id;
|
||||
|
||||
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
|
||||
param[1].u.memref.shm = channel->tee_shm;
|
||||
param[1].u.memref.size = msg_size;
|
||||
|
||||
param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
|
||||
param[2].u.memref.shm = channel->tee_shm;
|
||||
param[2].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE;
|
||||
|
||||
ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);
|
||||
if (ret < 0 || arg.ret) {
|
||||
dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",
|
||||
@ -279,7 +335,26 @@ static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo)
|
||||
{
|
||||
struct scmi_optee_channel *channel = cinfo->transport_info;
|
||||
|
||||
shmem_clear_channel(channel->shmem);
|
||||
if (!channel->tee_shm)
|
||||
shmem_clear_channel(channel->req.shmem);
|
||||
}
|
||||
|
||||
static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel)
|
||||
{
|
||||
const size_t msg_size = SCMI_OPTEE_MAX_MSG_SIZE;
|
||||
void *shbuf;
|
||||
|
||||
channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size);
|
||||
if (IS_ERR(channel->tee_shm)) {
|
||||
dev_err(channel->cinfo->dev, "shmem allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
shbuf = tee_shm_get_va(channel->tee_shm, 0);
|
||||
memset(shbuf, 0, msg_size);
|
||||
channel->req.msg = shbuf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo,
|
||||
@ -304,8 +379,8 @@ static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo,
|
||||
|
||||
size = resource_size(&res);
|
||||
|
||||
channel->shmem = devm_ioremap(dev, res.start, size);
|
||||
if (!channel->shmem) {
|
||||
channel->req.shmem = devm_ioremap(dev, res.start, size);
|
||||
if (!channel->req.shmem) {
|
||||
dev_err(dev, "Failed to ioremap SCMI Tx shared memory\n");
|
||||
ret = -EADDRNOTAVAIL;
|
||||
goto out;
|
||||
@ -325,7 +400,7 @@ static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo,
|
||||
if (of_find_property(cinfo->dev->of_node, "shmem", NULL))
|
||||
return setup_static_shmem(dev, cinfo, channel);
|
||||
else
|
||||
return -ENOMEM;
|
||||
return setup_dynamic_shmem(dev, channel);
|
||||
}
|
||||
|
||||
static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx)
|
||||
@ -405,27 +480,22 @@ static int scmi_optee_chan_free(int id, void *p, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct scmi_shared_mem __iomem *
|
||||
get_channel_shm(struct scmi_optee_channel *chan, struct scmi_xfer *xfer)
|
||||
{
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
return chan->shmem;
|
||||
}
|
||||
|
||||
|
||||
static int scmi_optee_send_message(struct scmi_chan_info *cinfo,
|
||||
struct scmi_xfer *xfer)
|
||||
{
|
||||
struct scmi_optee_channel *channel = cinfo->transport_info;
|
||||
struct scmi_shared_mem __iomem *shmem = get_channel_shm(channel, xfer);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&channel->mu);
|
||||
shmem_tx_prepare(shmem, xfer);
|
||||
|
||||
ret = invoke_process_smt_channel(channel);
|
||||
if (channel->tee_shm) {
|
||||
msg_tx_prepare(channel->req.msg, xfer);
|
||||
ret = invoke_process_msg_channel(channel, msg_command_size(xfer));
|
||||
} else {
|
||||
shmem_tx_prepare(channel->req.shmem, xfer);
|
||||
ret = invoke_process_smt_channel(channel);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
mutex_unlock(&channel->mu);
|
||||
|
||||
@ -436,9 +506,11 @@ static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo,
|
||||
struct scmi_xfer *xfer)
|
||||
{
|
||||
struct scmi_optee_channel *channel = cinfo->transport_info;
|
||||
struct scmi_shared_mem __iomem *shmem = get_channel_shm(channel, xfer);
|
||||
|
||||
shmem_fetch_response(shmem, xfer);
|
||||
if (channel->tee_shm)
|
||||
msg_fetch_response(channel->req.msg, SCMI_OPTEE_MAX_MSG_SIZE, xfer);
|
||||
else
|
||||
shmem_fetch_response(channel->req.shmem, xfer);
|
||||
}
|
||||
|
||||
static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret,
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Performance Protocol
|
||||
*
|
||||
* Copyright (C) 2018-2021 ARM Ltd.
|
||||
* Copyright (C) 2018-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications PERF - " fmt
|
||||
@ -17,9 +17,11 @@
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define MAX_OPPS 16
|
||||
|
||||
enum scmi_performance_protocol_cmd {
|
||||
PERF_DOMAIN_ATTRIBUTES = 0x3,
|
||||
PERF_DESCRIBE_LEVELS = 0x4,
|
||||
@ -30,6 +32,7 @@ enum scmi_performance_protocol_cmd {
|
||||
PERF_NOTIFY_LIMITS = 0x9,
|
||||
PERF_NOTIFY_LEVEL = 0xa,
|
||||
PERF_DESCRIBE_FASTCHANNEL = 0xb,
|
||||
PERF_DOMAIN_NAME_GET = 0xc,
|
||||
};
|
||||
|
||||
struct scmi_opp {
|
||||
@ -42,6 +45,7 @@ struct scmi_msg_resp_perf_attributes {
|
||||
__le16 num_domains;
|
||||
__le16 flags;
|
||||
#define POWER_SCALE_IN_MILLIWATT(x) ((x) & BIT(0))
|
||||
#define POWER_SCALE_IN_MICROWATT(x) ((x) & BIT(1))
|
||||
__le32 stats_addr_low;
|
||||
__le32 stats_addr_high;
|
||||
__le32 stats_size;
|
||||
@ -54,10 +58,11 @@ struct scmi_msg_resp_perf_domain_attributes {
|
||||
#define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29))
|
||||
#define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28))
|
||||
#define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(26))
|
||||
__le32 rate_limit_us;
|
||||
__le32 sustained_freq_khz;
|
||||
__le32 sustained_perf_level;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
};
|
||||
|
||||
struct scmi_msg_perf_describe_levels {
|
||||
@ -166,6 +171,7 @@ struct scmi_perf_info {
|
||||
u32 version;
|
||||
int num_domains;
|
||||
bool power_scale_mw;
|
||||
bool power_scale_uw;
|
||||
u64 stats_addr;
|
||||
u32 stats_size;
|
||||
struct perf_dom_info *dom_info;
|
||||
@ -196,6 +202,8 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
pi->num_domains = le16_to_cpu(attr->num_domains);
|
||||
pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags);
|
||||
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3)
|
||||
pi->power_scale_uw = POWER_SCALE_IN_MICROWATT(flags);
|
||||
pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
|
||||
(u64)le32_to_cpu(attr->stats_addr_high) << 32;
|
||||
pi->stats_size = le32_to_cpu(attr->stats_size);
|
||||
@ -207,9 +215,11 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
static int
|
||||
scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct perf_dom_info *dom_info)
|
||||
u32 domain, struct perf_dom_info *dom_info,
|
||||
u32 version)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_perf_domain_attributes *attr;
|
||||
|
||||
@ -223,7 +233,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
u32 flags = le32_to_cpu(attr->flags);
|
||||
flags = le32_to_cpu(attr->flags);
|
||||
|
||||
dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
|
||||
dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
|
||||
@ -246,6 +256,16 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
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 && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(flags))
|
||||
ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, domain,
|
||||
dom_info->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -256,66 +276,87 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
|
||||
return t1->perf - t2->perf;
|
||||
}
|
||||
|
||||
struct scmi_perf_ipriv {
|
||||
u32 domain;
|
||||
struct perf_dom_info *perf_dom;
|
||||
};
|
||||
|
||||
static void iter_perf_levels_prepare_message(void *message,
|
||||
unsigned int desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_msg_perf_describe_levels *msg = message;
|
||||
const struct scmi_perf_ipriv *p = priv;
|
||||
|
||||
msg->domain = cpu_to_le32(p->domain);
|
||||
/* Set the number of OPPs to be skipped/already read */
|
||||
msg->level_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
static int iter_perf_levels_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
const struct scmi_msg_resp_perf_describe_levels *r = response;
|
||||
|
||||
st->num_returned = le16_to_cpu(r->num_returned);
|
||||
st->num_remaining = le16_to_cpu(r->num_remaining);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv)
|
||||
{
|
||||
struct scmi_opp *opp;
|
||||
const struct scmi_msg_resp_perf_describe_levels *r = response;
|
||||
struct scmi_perf_ipriv *p = priv;
|
||||
|
||||
opp = &p->perf_dom->opp[st->desc_index + st->loop_idx];
|
||||
opp->perf = le32_to_cpu(r->opp[st->loop_idx].perf_val);
|
||||
opp->power = le32_to_cpu(r->opp[st->loop_idx].power);
|
||||
opp->trans_latency_us =
|
||||
le16_to_cpu(r->opp[st->loop_idx].transition_latency_us);
|
||||
p->perf_dom->opp_count++;
|
||||
|
||||
dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n",
|
||||
opp->perf, opp->power, opp->trans_latency_us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain,
|
||||
struct perf_dom_info *perf_dom)
|
||||
{
|
||||
int ret, cnt;
|
||||
u32 tot_opp_cnt = 0;
|
||||
u16 num_returned, num_remaining;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_opp *opp;
|
||||
struct scmi_msg_perf_describe_levels *dom_info;
|
||||
struct scmi_msg_resp_perf_describe_levels *level_info;
|
||||
int ret;
|
||||
void *iter;
|
||||
struct scmi_msg_perf_describe_levels *msg;
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_perf_levels_prepare_message,
|
||||
.update_state = iter_perf_levels_update_state,
|
||||
.process_response = iter_perf_levels_process_response,
|
||||
};
|
||||
struct scmi_perf_ipriv ppriv = {
|
||||
.domain = domain,
|
||||
.perf_dom = perf_dom,
|
||||
};
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, PERF_DESCRIBE_LEVELS,
|
||||
sizeof(*dom_info), 0, &t);
|
||||
iter = ph->hops->iter_response_init(ph, &ops, MAX_OPPS,
|
||||
PERF_DESCRIBE_LEVELS,
|
||||
sizeof(*msg), &ppriv);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
ret = ph->hops->iter_response_run(iter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dom_info = t->tx.buf;
|
||||
level_info = t->rx.buf;
|
||||
if (perf_dom->opp_count)
|
||||
sort(perf_dom->opp, perf_dom->opp_count,
|
||||
sizeof(struct scmi_opp), opp_cmp_func, NULL);
|
||||
|
||||
do {
|
||||
dom_info->domain = cpu_to_le32(domain);
|
||||
/* Set the number of OPPs to be skipped/already read */
|
||||
dom_info->level_index = cpu_to_le32(tot_opp_cnt);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
num_returned = le16_to_cpu(level_info->num_returned);
|
||||
num_remaining = le16_to_cpu(level_info->num_remaining);
|
||||
if (tot_opp_cnt + num_returned > MAX_OPPS) {
|
||||
dev_err(ph->dev, "No. of OPPs exceeded MAX_OPPS");
|
||||
break;
|
||||
}
|
||||
|
||||
opp = &perf_dom->opp[tot_opp_cnt];
|
||||
for (cnt = 0; cnt < num_returned; cnt++, opp++) {
|
||||
opp->perf = le32_to_cpu(level_info->opp[cnt].perf_val);
|
||||
opp->power = le32_to_cpu(level_info->opp[cnt].power);
|
||||
opp->trans_latency_us = le16_to_cpu
|
||||
(level_info->opp[cnt].transition_latency_us);
|
||||
|
||||
dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n",
|
||||
opp->perf, opp->power, opp->trans_latency_us);
|
||||
}
|
||||
|
||||
tot_opp_cnt += num_returned;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, t);
|
||||
/*
|
||||
* check for both returned and remaining to avoid infinite
|
||||
* loop due to buggy firmware
|
||||
*/
|
||||
} while (num_returned && num_remaining);
|
||||
|
||||
perf_dom->opp_count = tot_opp_cnt;
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -382,6 +423,9 @@ static int scmi_perf_limits_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 (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);
|
||||
@ -873,11 +917,13 @@ static const struct scmi_protocol_events perf_protocol_events = {
|
||||
|
||||
static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain;
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct scmi_perf_info *pinfo;
|
||||
|
||||
ph->xops->version_get(ph, &version);
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Performance Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
@ -886,7 +932,9 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
scmi_perf_attributes_get(ph, pinfo);
|
||||
ret = scmi_perf_attributes_get(ph, pinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
|
||||
sizeof(*pinfo->dom_info), GFP_KERNEL);
|
||||
@ -896,7 +944,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
for (domain = 0; domain < pinfo->num_domains; domain++) {
|
||||
struct perf_dom_info *dom = pinfo->dom_info + domain;
|
||||
|
||||
scmi_perf_domain_attributes_get(ph, domain, dom);
|
||||
scmi_perf_domain_attributes_get(ph, domain, dom, version);
|
||||
scmi_perf_describe_levels_get(ph, domain, dom);
|
||||
|
||||
if (dom->perf_fastchannels)
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Power Protocol
|
||||
*
|
||||
* Copyright (C) 2018-2021 ARM Ltd.
|
||||
* Copyright (C) 2018-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
|
||||
@ -10,7 +10,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_power_protocol_cmd {
|
||||
@ -18,6 +18,7 @@ enum scmi_power_protocol_cmd {
|
||||
POWER_STATE_SET = 0x4,
|
||||
POWER_STATE_GET = 0x5,
|
||||
POWER_STATE_NOTIFY = 0x6,
|
||||
POWER_DOMAIN_NAME_GET = 0x8,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_power_attributes {
|
||||
@ -33,7 +34,8 @@ struct scmi_msg_resp_power_domain_attributes {
|
||||
#define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31))
|
||||
#define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30))
|
||||
#define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29))
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(27))
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
};
|
||||
|
||||
struct scmi_power_set_state {
|
||||
@ -97,9 +99,11 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
static int
|
||||
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct power_dom_info *dom_info)
|
||||
u32 domain, struct power_dom_info *dom_info,
|
||||
u32 version)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_power_domain_attributes *attr;
|
||||
|
||||
@ -113,15 +117,26 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
u32 flags = le32_to_cpu(attr->flags);
|
||||
flags = le32_to_cpu(attr->flags);
|
||||
|
||||
dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
|
||||
dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
|
||||
dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
|
||||
strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
|
||||
}
|
||||
|
||||
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 && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(flags)) {
|
||||
ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
|
||||
domain, dom_info->name,
|
||||
SCMI_MAX_STR_SIZE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -174,8 +189,9 @@ static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
|
||||
return pi->num_domains;
|
||||
}
|
||||
|
||||
static char *scmi_power_name_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain)
|
||||
static const char *
|
||||
scmi_power_name_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain)
|
||||
{
|
||||
struct scmi_power_info *pi = ph->get_priv(ph);
|
||||
struct power_dom_info *dom = pi->dom_info + domain;
|
||||
@ -280,11 +296,13 @@ static const struct scmi_protocol_events power_protocol_events = {
|
||||
|
||||
static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain;
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct scmi_power_info *pinfo;
|
||||
|
||||
ph->xops->version_get(ph, &version);
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Power Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
@ -293,7 +311,9 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
scmi_power_attributes_get(ph, pinfo);
|
||||
ret = scmi_power_attributes_get(ph, pinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
|
||||
sizeof(*pinfo->dom_info), GFP_KERNEL);
|
||||
@ -303,7 +323,7 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
for (domain = 0; domain < pinfo->num_domains; domain++) {
|
||||
struct power_dom_info *dom = pinfo->dom_info + domain;
|
||||
|
||||
scmi_power_domain_attributes_get(ph, domain, dom);
|
||||
scmi_power_domain_attributes_get(ph, domain, dom, version);
|
||||
}
|
||||
|
||||
pinfo->version = version;
|
||||
|
318
drivers/firmware/arm_scmi/protocols.h
Normal file
318
drivers/firmware/arm_scmi/protocols.h
Normal file
@ -0,0 +1,318 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Message Protocol
|
||||
* protocols common header file containing some definitions, structures
|
||||
* and function prototypes used in all the different SCMI protocols.
|
||||
*
|
||||
* Copyright (C) 2022 ARM Ltd.
|
||||
*/
|
||||
#ifndef _SCMI_PROTOCOLS_H
|
||||
#define _SCMI_PROTOCOLS_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define SCMI_SHORT_NAME_MAX_SIZE 16
|
||||
|
||||
#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
|
||||
#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
|
||||
#define PROTOCOL_REV_MAJOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))))
|
||||
#define PROTOCOL_REV_MINOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x))))
|
||||
|
||||
enum scmi_common_cmd {
|
||||
PROTOCOL_VERSION = 0x0,
|
||||
PROTOCOL_ATTRIBUTES = 0x1,
|
||||
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_msg_resp_prot_version - Response for a message
|
||||
*
|
||||
* @minor_version: Minor version of the ABI that firmware supports
|
||||
* @major_version: Major version of the ABI that firmware supports
|
||||
*
|
||||
* In general, ABI version changes follow the rule that minor version increments
|
||||
* are backward compatible. Major revision changes in ABI may not be
|
||||
* backward compatible.
|
||||
*
|
||||
* Response to a generic message with message type SCMI_MSG_VERSION
|
||||
*/
|
||||
struct scmi_msg_resp_prot_version {
|
||||
__le16 minor_version;
|
||||
__le16 major_version;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_msg - Message(Tx/Rx) structure
|
||||
*
|
||||
* @buf: Buffer pointer
|
||||
* @len: Length of data in the Buffer
|
||||
*/
|
||||
struct scmi_msg {
|
||||
void *buf;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_msg_hdr - Message(Tx/Rx) header
|
||||
*
|
||||
* @id: The identifier of the message being sent
|
||||
* @protocol_id: The identifier of the protocol used to send @id message
|
||||
* @type: The SCMI type for this message
|
||||
* @seq: The token to identify the message. When a message returns, the
|
||||
* platform returns the whole message header unmodified including the
|
||||
* token
|
||||
* @status: Status of the transfer once it's complete
|
||||
* @poll_completion: Indicate if the transfer needs to be polled for
|
||||
* completion or interrupt mode is used
|
||||
*/
|
||||
struct scmi_msg_hdr {
|
||||
u8 id;
|
||||
u8 protocol_id;
|
||||
u8 type;
|
||||
u16 seq;
|
||||
u32 status;
|
||||
bool poll_completion;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_xfer - Structure representing a message flow
|
||||
*
|
||||
* @transfer_id: Unique ID for debug & profiling purpose
|
||||
* @hdr: Transmit message header
|
||||
* @tx: Transmit message
|
||||
* @rx: Receive message, the buffer should be pre-allocated to store
|
||||
* message. If request-ACK protocol is used, we can reuse the same
|
||||
* buffer for the rx path as we use for the tx path.
|
||||
* @done: command message transmit completion event
|
||||
* @async_done: pointer to delayed response message received event completion
|
||||
* @pending: True for xfers added to @pending_xfers hashtable
|
||||
* @node: An hlist_node reference used to store this xfer, alternatively, on
|
||||
* the free list @free_xfers or in the @pending_xfers hashtable
|
||||
* @users: A refcount to track the active users for this xfer.
|
||||
* This is meant to protect against the possibility that, when a command
|
||||
* transaction times out concurrently with the reception of a valid
|
||||
* response message, the xfer could be finally put on the TX path, and
|
||||
* so vanish, while on the RX path scmi_rx_callback() is still
|
||||
* processing it: in such a case this refcounting will ensure that, even
|
||||
* though the timed-out transaction will anyway cause the command
|
||||
* request to be reported as failed by time-out, the underlying xfer
|
||||
* cannot be discarded and possibly reused until the last one user on
|
||||
* the RX path has released it.
|
||||
* @busy: An atomic flag to ensure exclusive write access to this xfer
|
||||
* @state: The current state of this transfer, with states transitions deemed
|
||||
* valid being:
|
||||
* - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
|
||||
* - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
|
||||
* (Missing synchronous response is assumed OK and ignored)
|
||||
* @lock: A spinlock to protect state and busy fields.
|
||||
* @priv: A pointer for transport private usage.
|
||||
*/
|
||||
struct scmi_xfer {
|
||||
int transfer_id;
|
||||
struct scmi_msg_hdr hdr;
|
||||
struct scmi_msg tx;
|
||||
struct scmi_msg rx;
|
||||
struct completion done;
|
||||
struct completion *async_done;
|
||||
bool pending;
|
||||
struct hlist_node node;
|
||||
refcount_t users;
|
||||
#define SCMI_XFER_FREE 0
|
||||
#define SCMI_XFER_BUSY 1
|
||||
atomic_t busy;
|
||||
#define SCMI_XFER_SENT_OK 0
|
||||
#define SCMI_XFER_RESP_OK 1
|
||||
#define SCMI_XFER_DRESP_OK 2
|
||||
int state;
|
||||
/* A lock to protect state and busy fields */
|
||||
spinlock_t lock;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct scmi_xfer_ops;
|
||||
struct scmi_proto_helpers_ops;
|
||||
|
||||
/**
|
||||
* struct scmi_protocol_handle - Reference to an initialized protocol instance
|
||||
*
|
||||
* @dev: A reference to the associated SCMI instance device (handle->dev).
|
||||
* @xops: A reference to a struct holding refs to the core xfer operations that
|
||||
* can be used by the protocol implementation to generate SCMI messages.
|
||||
* @set_priv: A method to set protocol private data for this instance.
|
||||
* @get_priv: A method to get protocol private data previously set.
|
||||
*
|
||||
* This structure represents a protocol initialized against specific SCMI
|
||||
* instance and it will be used as follows:
|
||||
* - as a parameter fed from the core to the protocol initialization code so
|
||||
* that it can access the core xfer operations to build and generate SCMI
|
||||
* messages exclusively for the specific underlying protocol instance.
|
||||
* - as an opaque handle fed by an SCMI driver user when it tries to access
|
||||
* this protocol through its own protocol operations.
|
||||
* In this case this handle will be returned as an opaque object together
|
||||
* with the related protocol operations when the SCMI driver tries to access
|
||||
* the protocol.
|
||||
*/
|
||||
struct scmi_protocol_handle {
|
||||
struct device *dev;
|
||||
const struct scmi_xfer_ops *xops;
|
||||
const struct scmi_proto_helpers_ops *hops;
|
||||
int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
|
||||
void *(*get_priv)(const struct scmi_protocol_handle *ph);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_iterator_state - Iterator current state descriptor
|
||||
* @desc_index: Starting index for the current mulit-part request.
|
||||
* @num_returned: Number of returned items in the last multi-part reply.
|
||||
* @num_remaining: Number of remaining items in the multi-part message.
|
||||
* @max_resources: Maximum acceptable number of items, configured by the caller
|
||||
* depending on the underlying resources that it is querying.
|
||||
* @loop_idx: The iterator loop index in the current multi-part reply.
|
||||
* @priv: Optional pointer to some additional state-related private data setup
|
||||
* by the caller during the iterations.
|
||||
*/
|
||||
struct scmi_iterator_state {
|
||||
unsigned int desc_index;
|
||||
unsigned int num_returned;
|
||||
unsigned int num_remaining;
|
||||
unsigned int max_resources;
|
||||
unsigned int loop_idx;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_iterator_ops - Custom iterator operations
|
||||
* @prepare_message: An operation to provide the custom logic to fill in the
|
||||
* SCMI command request pointed by @message. @desc_index is
|
||||
* a reference to the next index to use in the multi-part
|
||||
* request.
|
||||
* @update_state: An operation to provide the custom logic to update the
|
||||
* iterator state from the actual message response.
|
||||
* @process_response: An operation to provide the custom logic needed to process
|
||||
* each chunk of the multi-part message.
|
||||
*/
|
||||
struct scmi_iterator_ops {
|
||||
void (*prepare_message)(void *message, unsigned int desc_index,
|
||||
const void *priv);
|
||||
int (*update_state)(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv);
|
||||
int (*process_response)(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_proto_helpers_ops - References to common protocol helpers
|
||||
* @extended_name_get: A common helper function to retrieve extended naming
|
||||
* for the specified resource using the specified command.
|
||||
* Result is returned as a NULL terminated string in the
|
||||
* pre-allocated area pointed to by @name with maximum
|
||||
* capacity of @len bytes.
|
||||
* @iter_response_init: A common helper to initialize a generic iterator to
|
||||
* parse multi-message responses: when run the iterator
|
||||
* will take care to send the initial command request as
|
||||
* specified by @msg_id and @tx_size and then to parse the
|
||||
* multi-part responses using the custom operations
|
||||
* provided in @ops.
|
||||
* @iter_response_run: A common helper to trigger the run of a previously
|
||||
* initialized iterator.
|
||||
*/
|
||||
struct scmi_proto_helpers_ops {
|
||||
int (*extended_name_get)(const struct scmi_protocol_handle *ph,
|
||||
u8 cmd_id, u32 res_id, char *name, size_t len);
|
||||
void *(*iter_response_init)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_iterator_ops *ops,
|
||||
unsigned int max_resources, u8 msg_id,
|
||||
size_t tx_size, void *priv);
|
||||
int (*iter_response_run)(void *iter);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_xfer_ops - References to the core SCMI xfer operations.
|
||||
* @version_get: Get this version protocol.
|
||||
* @xfer_get_init: Initialize one struct xfer if any xfer slot is free.
|
||||
* @reset_rx_to_maxsz: Reset rx size to max transport size.
|
||||
* @do_xfer: Do the SCMI transfer.
|
||||
* @do_xfer_with_response: Do the SCMI transfer waiting for a response.
|
||||
* @xfer_put: Free the xfer slot.
|
||||
*
|
||||
* Note that all this operations expect a protocol handle as first parameter;
|
||||
* they then internally use it to infer the underlying protocol number: this
|
||||
* way is not possible for a protocol implementation to forge messages for
|
||||
* another protocol.
|
||||
*/
|
||||
struct scmi_xfer_ops {
|
||||
int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version);
|
||||
int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id,
|
||||
size_t tx_size, size_t rx_size,
|
||||
struct scmi_xfer **p);
|
||||
void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
int (*do_xfer)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
void (*xfer_put)(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *xfer);
|
||||
};
|
||||
|
||||
typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
|
||||
|
||||
/**
|
||||
* struct scmi_protocol - Protocol descriptor
|
||||
* @id: Protocol ID.
|
||||
* @owner: Module reference if any.
|
||||
* @instance_init: Mandatory protocol initialization function.
|
||||
* @instance_deinit: Optional protocol de-initialization function.
|
||||
* @ops: Optional reference to the operations provided by the protocol and
|
||||
* exposed in scmi_protocol.h.
|
||||
* @events: An optional reference to the events supported by this protocol.
|
||||
*/
|
||||
struct scmi_protocol {
|
||||
const u8 id;
|
||||
struct module *owner;
|
||||
const scmi_prot_init_ph_fn_t instance_init;
|
||||
const scmi_prot_init_ph_fn_t instance_deinit;
|
||||
const void *ops;
|
||||
const struct scmi_protocol_events *events;
|
||||
};
|
||||
|
||||
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
|
||||
static const struct scmi_protocol *__this_proto = &(proto); \
|
||||
\
|
||||
int __init scmi_##name##_register(void) \
|
||||
{ \
|
||||
return scmi_protocol_register(__this_proto); \
|
||||
} \
|
||||
\
|
||||
void __exit scmi_##name##_unregister(void) \
|
||||
{ \
|
||||
scmi_protocol_unregister(__this_proto); \
|
||||
}
|
||||
|
||||
#define DECLARE_SCMI_REGISTER_UNREGISTER(func) \
|
||||
int __init scmi_##func##_register(void); \
|
||||
void __exit scmi_##func##_unregister(void)
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(base);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(clock);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(perf);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(power);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(system);
|
||||
|
||||
#endif /* _SCMI_PROTOCOLS_H */
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Reset Protocol
|
||||
*
|
||||
* Copyright (C) 2019-2021 ARM Ltd.
|
||||
* Copyright (C) 2019-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
|
||||
@ -10,13 +10,14 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_reset_protocol_cmd {
|
||||
RESET_DOMAIN_ATTRIBUTES = 0x3,
|
||||
RESET = 0x4,
|
||||
RESET_NOTIFY = 0x5,
|
||||
RESET_DOMAIN_NAME_GET = 0x6,
|
||||
};
|
||||
|
||||
#define NUM_RESET_DOMAIN_MASK 0xffff
|
||||
@ -26,8 +27,9 @@ struct scmi_msg_resp_reset_domain_attributes {
|
||||
__le32 attributes;
|
||||
#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
|
||||
#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
|
||||
__le32 latency;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
};
|
||||
|
||||
struct scmi_msg_reset_domain_reset {
|
||||
@ -89,9 +91,11 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
static int
|
||||
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct reset_dom_info *dom_info)
|
||||
u32 domain, struct reset_dom_info *dom_info,
|
||||
u32 version)
|
||||
{
|
||||
int ret;
|
||||
u32 attributes;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_reset_domain_attributes *attr;
|
||||
|
||||
@ -105,7 +109,7 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
u32 attributes = le32_to_cpu(attr->attributes);
|
||||
attributes = le32_to_cpu(attr->attributes);
|
||||
|
||||
dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
|
||||
dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
|
||||
@ -116,6 +120,16 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
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 && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(attributes))
|
||||
ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,
|
||||
dom_info->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -126,8 +140,8 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
|
||||
return pi->num_domains;
|
||||
}
|
||||
|
||||
static char *scmi_reset_name_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain)
|
||||
static const char *
|
||||
scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)
|
||||
{
|
||||
struct scmi_reset_info *pi = ph->get_priv(ph);
|
||||
|
||||
@ -293,11 +307,13 @@ static const struct scmi_protocol_events reset_protocol_events = {
|
||||
|
||||
static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int domain;
|
||||
int domain, ret;
|
||||
u32 version;
|
||||
struct scmi_reset_info *pinfo;
|
||||
|
||||
ph->xops->version_get(ph, &version);
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Reset Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
@ -306,7 +322,9 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
scmi_reset_attributes_get(ph, pinfo);
|
||||
ret = scmi_reset_attributes_get(ph, pinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
|
||||
sizeof(*pinfo->dom_info), GFP_KERNEL);
|
||||
@ -316,7 +334,7 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
for (domain = 0; domain < pinfo->num_domains; domain++) {
|
||||
struct reset_dom_info *dom = pinfo->dom_info + domain;
|
||||
|
||||
scmi_reset_domain_attributes_get(ph, domain, dom);
|
||||
scmi_reset_domain_attributes_get(ph, domain, dom, version);
|
||||
}
|
||||
|
||||
pinfo->version = version;
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Sensor Protocol
|
||||
*
|
||||
* Copyright (C) 2018-2021 ARM Ltd.
|
||||
* Copyright (C) 2018-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt
|
||||
@ -11,7 +11,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define SCMI_MAX_NUM_SENSOR_AXIS 63
|
||||
@ -27,6 +27,8 @@ enum scmi_sensor_protocol_cmd {
|
||||
SENSOR_CONFIG_GET = 0x9,
|
||||
SENSOR_CONFIG_SET = 0xA,
|
||||
SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0xB,
|
||||
SENSOR_NAME_GET = 0xC,
|
||||
SENSOR_AXIS_NAME_GET = 0xD,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_sensor_attributes {
|
||||
@ -63,6 +65,10 @@ struct scmi_msg_resp_attrs {
|
||||
__le32 max_range_high;
|
||||
};
|
||||
|
||||
struct scmi_msg_sensor_description {
|
||||
__le32 desc_index;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_sensor_description {
|
||||
__le16 num_returned;
|
||||
__le16 num_remaining;
|
||||
@ -71,6 +77,7 @@ struct scmi_msg_resp_sensor_description {
|
||||
__le32 attributes_low;
|
||||
/* Common attributes_low macros */
|
||||
#define SUPPORTS_ASYNC_READ(x) FIELD_GET(BIT(31), (x))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) FIELD_GET(BIT(29), (x))
|
||||
#define NUM_TRIP_POINTS(x) FIELD_GET(GENMASK(7, 0), (x))
|
||||
__le32 attributes_high;
|
||||
/* Common attributes_high macros */
|
||||
@ -78,7 +85,7 @@ struct scmi_msg_resp_sensor_description {
|
||||
#define SENSOR_SCALE_SIGN BIT(4)
|
||||
#define SENSOR_SCALE_EXTEND GENMASK(31, 5)
|
||||
#define SENSOR_TYPE(x) FIELD_GET(GENMASK(7, 0), (x))
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
/* only for version > 2.0 */
|
||||
__le32 power;
|
||||
__le32 resolution;
|
||||
@ -111,13 +118,22 @@ struct scmi_msg_resp_sensor_axis_description {
|
||||
struct scmi_axis_descriptor {
|
||||
__le32 id;
|
||||
__le32 attributes_low;
|
||||
#define SUPPORTS_EXTENDED_AXIS_NAMES(x) FIELD_GET(BIT(9), (x))
|
||||
__le32 attributes_high;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
__le32 resolution;
|
||||
struct scmi_msg_resp_attrs attrs;
|
||||
} desc[];
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_sensor_axis_names_description {
|
||||
__le32 num_axis_flags;
|
||||
struct scmi_sensor_axis_name_descriptor {
|
||||
__le32 axis_id;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
} desc[];
|
||||
};
|
||||
|
||||
/* Base scmi_axis_descriptor size excluding extended attrs after name */
|
||||
#define SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ 28
|
||||
|
||||
@ -231,333 +247,412 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
static inline void scmi_parse_range_attrs(struct scmi_range_attrs *out,
|
||||
struct scmi_msg_resp_attrs *in)
|
||||
const struct scmi_msg_resp_attrs *in)
|
||||
{
|
||||
out->min_range = get_unaligned_le64((void *)&in->min_range_low);
|
||||
out->max_range = get_unaligned_le64((void *)&in->max_range_low);
|
||||
}
|
||||
|
||||
struct scmi_sens_ipriv {
|
||||
void *priv;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static void iter_intervals_prepare_message(void *message,
|
||||
unsigned int desc_index,
|
||||
const void *p)
|
||||
{
|
||||
struct scmi_msg_sensor_list_update_intervals *msg = message;
|
||||
const struct scmi_sensor_info *s;
|
||||
|
||||
s = ((const struct scmi_sens_ipriv *)p)->priv;
|
||||
/* Set the number of sensors to be skipped/already read */
|
||||
msg->id = cpu_to_le32(s->id);
|
||||
msg->index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
static int iter_intervals_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *p)
|
||||
{
|
||||
u32 flags;
|
||||
struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv;
|
||||
struct device *dev = ((struct scmi_sens_ipriv *)p)->dev;
|
||||
const struct scmi_msg_resp_sensor_list_update_intervals *r = response;
|
||||
|
||||
flags = le32_to_cpu(r->num_intervals_flags);
|
||||
st->num_returned = NUM_INTERVALS_RETURNED(flags);
|
||||
st->num_remaining = NUM_INTERVALS_REMAINING(flags);
|
||||
|
||||
/*
|
||||
* Max intervals is not declared previously anywhere so we
|
||||
* assume it's returned+remaining on first call.
|
||||
*/
|
||||
if (!st->max_resources) {
|
||||
s->intervals.segmented = SEGMENTED_INTVL_FORMAT(flags);
|
||||
s->intervals.count = st->num_returned + st->num_remaining;
|
||||
/* segmented intervals are reported in one triplet */
|
||||
if (s->intervals.segmented &&
|
||||
(st->num_remaining || st->num_returned != 3)) {
|
||||
dev_err(dev,
|
||||
"Sensor ID:%d advertises an invalid segmented interval (%d)\n",
|
||||
s->id, s->intervals.count);
|
||||
s->intervals.segmented = false;
|
||||
s->intervals.count = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Direct allocation when exceeding pre-allocated */
|
||||
if (s->intervals.count >= SCMI_MAX_PREALLOC_POOL) {
|
||||
s->intervals.desc =
|
||||
devm_kcalloc(dev,
|
||||
s->intervals.count,
|
||||
sizeof(*s->intervals.desc),
|
||||
GFP_KERNEL);
|
||||
if (!s->intervals.desc) {
|
||||
s->intervals.segmented = false;
|
||||
s->intervals.count = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
st->max_resources = s->intervals.count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iter_intervals_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *p)
|
||||
{
|
||||
const struct scmi_msg_resp_sensor_list_update_intervals *r = response;
|
||||
struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv;
|
||||
|
||||
s->intervals.desc[st->desc_index + st->loop_idx] =
|
||||
le32_to_cpu(r->intervals[st->loop_idx]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_sensor_update_intervals(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_sensor_info *s)
|
||||
{
|
||||
int ret, cnt;
|
||||
u32 desc_index = 0;
|
||||
u16 num_returned, num_remaining;
|
||||
struct scmi_xfer *ti;
|
||||
struct scmi_msg_resp_sensor_list_update_intervals *buf;
|
||||
void *iter;
|
||||
struct scmi_msg_sensor_list_update_intervals *msg;
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_intervals_prepare_message,
|
||||
.update_state = iter_intervals_update_state,
|
||||
.process_response = iter_intervals_process_response,
|
||||
};
|
||||
struct scmi_sens_ipriv upriv = {
|
||||
.priv = s,
|
||||
.dev = ph->dev,
|
||||
};
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SENSOR_LIST_UPDATE_INTERVALS,
|
||||
sizeof(*msg), 0, &ti);
|
||||
if (ret)
|
||||
return ret;
|
||||
iter = ph->hops->iter_response_init(ph, &ops, s->intervals.count,
|
||||
SENSOR_LIST_UPDATE_INTERVALS,
|
||||
sizeof(*msg), &upriv);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
buf = ti->rx.buf;
|
||||
do {
|
||||
u32 flags;
|
||||
return ph->hops->iter_response_run(iter);
|
||||
}
|
||||
|
||||
msg = ti->tx.buf;
|
||||
/* Set the number of sensors to be skipped/already read */
|
||||
msg->id = cpu_to_le32(s->id);
|
||||
msg->index = cpu_to_le32(desc_index);
|
||||
static void iter_axes_desc_prepare_message(void *message,
|
||||
const unsigned int desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_msg_sensor_axis_description_get *msg = message;
|
||||
const struct scmi_sensor_info *s = priv;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, ti);
|
||||
if (ret)
|
||||
break;
|
||||
/* Set the number of sensors to be skipped/already read */
|
||||
msg->id = cpu_to_le32(s->id);
|
||||
msg->axis_desc_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
flags = le32_to_cpu(buf->num_intervals_flags);
|
||||
num_returned = NUM_INTERVALS_RETURNED(flags);
|
||||
num_remaining = NUM_INTERVALS_REMAINING(flags);
|
||||
static int
|
||||
iter_axes_desc_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
u32 flags;
|
||||
const struct scmi_msg_resp_sensor_axis_description *r = response;
|
||||
|
||||
/*
|
||||
* Max intervals is not declared previously anywhere so we
|
||||
* assume it's returned+remaining.
|
||||
*/
|
||||
if (!s->intervals.count) {
|
||||
s->intervals.segmented = SEGMENTED_INTVL_FORMAT(flags);
|
||||
s->intervals.count = num_returned + num_remaining;
|
||||
/* segmented intervals are reported in one triplet */
|
||||
if (s->intervals.segmented &&
|
||||
(num_remaining || num_returned != 3)) {
|
||||
dev_err(ph->dev,
|
||||
"Sensor ID:%d advertises an invalid segmented interval (%d)\n",
|
||||
s->id, s->intervals.count);
|
||||
s->intervals.segmented = false;
|
||||
s->intervals.count = 0;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
/* Direct allocation when exceeding pre-allocated */
|
||||
if (s->intervals.count >= SCMI_MAX_PREALLOC_POOL) {
|
||||
s->intervals.desc =
|
||||
devm_kcalloc(ph->dev,
|
||||
s->intervals.count,
|
||||
sizeof(*s->intervals.desc),
|
||||
GFP_KERNEL);
|
||||
if (!s->intervals.desc) {
|
||||
s->intervals.segmented = false;
|
||||
s->intervals.count = 0;
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (desc_index + num_returned > s->intervals.count) {
|
||||
dev_err(ph->dev,
|
||||
"No. of update intervals can't exceed %d\n",
|
||||
s->intervals.count);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
flags = le32_to_cpu(r->num_axis_flags);
|
||||
st->num_returned = NUM_AXIS_RETURNED(flags);
|
||||
st->num_remaining = NUM_AXIS_REMAINING(flags);
|
||||
st->priv = (void *)&r->desc[0];
|
||||
|
||||
for (cnt = 0; cnt < num_returned; cnt++)
|
||||
s->intervals.desc[desc_index + cnt] =
|
||||
le32_to_cpu(buf->intervals[cnt]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
desc_index += num_returned;
|
||||
static int
|
||||
iter_axes_desc_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv)
|
||||
{
|
||||
u32 attrh, attrl;
|
||||
struct scmi_sensor_axis_info *a;
|
||||
size_t dsize = SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ;
|
||||
struct scmi_sensor_info *s = priv;
|
||||
const struct scmi_axis_descriptor *adesc = st->priv;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, ti);
|
||||
/*
|
||||
* check for both returned and remaining to avoid infinite
|
||||
* loop due to buggy firmware
|
||||
*/
|
||||
} while (num_returned && num_remaining);
|
||||
attrl = le32_to_cpu(adesc->attributes_low);
|
||||
|
||||
ph->xops->xfer_put(ph, ti);
|
||||
return ret;
|
||||
a = &s->axis[st->desc_index + st->loop_idx];
|
||||
a->id = le32_to_cpu(adesc->id);
|
||||
a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl);
|
||||
|
||||
attrh = le32_to_cpu(adesc->attributes_high);
|
||||
a->scale = S32_EXT(SENSOR_SCALE(attrh));
|
||||
a->type = SENSOR_TYPE(attrh);
|
||||
strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
if (a->extended_attrs) {
|
||||
unsigned int ares = le32_to_cpu(adesc->resolution);
|
||||
|
||||
a->resolution = SENSOR_RES(ares);
|
||||
a->exponent = S32_EXT(SENSOR_RES_EXP(ares));
|
||||
dsize += sizeof(adesc->resolution);
|
||||
|
||||
scmi_parse_range_attrs(&a->attrs, &adesc->attrs);
|
||||
dsize += sizeof(adesc->attrs);
|
||||
}
|
||||
st->priv = ((u8 *)adesc + dsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iter_axes_extended_name_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
u32 flags;
|
||||
const struct scmi_msg_resp_sensor_axis_names_description *r = response;
|
||||
|
||||
flags = le32_to_cpu(r->num_axis_flags);
|
||||
st->num_returned = NUM_AXIS_RETURNED(flags);
|
||||
st->num_remaining = NUM_AXIS_REMAINING(flags);
|
||||
st->priv = (void *)&r->desc[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iter_axes_extended_name_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st,
|
||||
void *priv)
|
||||
{
|
||||
struct scmi_sensor_axis_info *a;
|
||||
const struct scmi_sensor_info *s = priv;
|
||||
struct scmi_sensor_axis_name_descriptor *adesc = st->priv;
|
||||
|
||||
a = &s->axis[st->desc_index + st->loop_idx];
|
||||
strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE);
|
||||
st->priv = ++adesc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_sensor_axis_extended_names_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_sensor_info *s)
|
||||
{
|
||||
void *iter;
|
||||
struct scmi_msg_sensor_axis_description_get *msg;
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_axes_desc_prepare_message,
|
||||
.update_state = iter_axes_extended_name_update_state,
|
||||
.process_response = iter_axes_extended_name_process_response,
|
||||
};
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, s->num_axis,
|
||||
SENSOR_AXIS_NAME_GET,
|
||||
sizeof(*msg), s);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
return ph->hops->iter_response_run(iter);
|
||||
}
|
||||
|
||||
static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_sensor_info *s)
|
||||
struct scmi_sensor_info *s,
|
||||
u32 version)
|
||||
{
|
||||
int ret, cnt;
|
||||
u32 desc_index = 0;
|
||||
u16 num_returned, num_remaining;
|
||||
struct scmi_xfer *te;
|
||||
struct scmi_msg_resp_sensor_axis_description *buf;
|
||||
int ret;
|
||||
void *iter;
|
||||
struct scmi_msg_sensor_axis_description_get *msg;
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_axes_desc_prepare_message,
|
||||
.update_state = iter_axes_desc_update_state,
|
||||
.process_response = iter_axes_desc_process_response,
|
||||
};
|
||||
|
||||
s->axis = devm_kcalloc(ph->dev, s->num_axis,
|
||||
sizeof(*s->axis), GFP_KERNEL);
|
||||
if (!s->axis)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SENSOR_AXIS_DESCRIPTION_GET,
|
||||
sizeof(*msg), 0, &te);
|
||||
iter = ph->hops->iter_response_init(ph, &ops, s->num_axis,
|
||||
SENSOR_AXIS_DESCRIPTION_GET,
|
||||
sizeof(*msg), s);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
ret = ph->hops->iter_response_run(iter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf = te->rx.buf;
|
||||
do {
|
||||
u32 flags;
|
||||
struct scmi_axis_descriptor *adesc;
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x3)
|
||||
ret = scmi_sensor_axis_extended_names_get(ph, s);
|
||||
|
||||
msg = te->tx.buf;
|
||||
/* Set the number of sensors to be skipped/already read */
|
||||
msg->id = cpu_to_le32(s->id);
|
||||
msg->axis_desc_index = cpu_to_le32(desc_index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ph->xops->do_xfer(ph, te);
|
||||
if (ret)
|
||||
break;
|
||||
static void iter_sens_descr_prepare_message(void *message,
|
||||
unsigned int desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_msg_sensor_description *msg = message;
|
||||
|
||||
flags = le32_to_cpu(buf->num_axis_flags);
|
||||
num_returned = NUM_AXIS_RETURNED(flags);
|
||||
num_remaining = NUM_AXIS_REMAINING(flags);
|
||||
msg->desc_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
if (desc_index + num_returned > s->num_axis) {
|
||||
dev_err(ph->dev, "No. of axis can't exceed %d\n",
|
||||
s->num_axis);
|
||||
break;
|
||||
}
|
||||
static int iter_sens_descr_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
const struct scmi_msg_resp_sensor_description *r = response;
|
||||
|
||||
adesc = &buf->desc[0];
|
||||
for (cnt = 0; cnt < num_returned; cnt++) {
|
||||
u32 attrh, attrl;
|
||||
struct scmi_sensor_axis_info *a;
|
||||
size_t dsize = SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ;
|
||||
st->num_returned = le16_to_cpu(r->num_returned);
|
||||
st->num_remaining = le16_to_cpu(r->num_remaining);
|
||||
st->priv = (void *)&r->desc[0];
|
||||
|
||||
attrl = le32_to_cpu(adesc->attributes_low);
|
||||
return 0;
|
||||
}
|
||||
|
||||
a = &s->axis[desc_index + cnt];
|
||||
static int
|
||||
iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv)
|
||||
|
||||
a->id = le32_to_cpu(adesc->id);
|
||||
a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl);
|
||||
{
|
||||
int ret = 0;
|
||||
u32 attrh, attrl;
|
||||
size_t dsize = SCMI_MSG_RESP_SENS_DESCR_BASE_SZ;
|
||||
struct scmi_sensor_info *s;
|
||||
struct sensors_info *si = priv;
|
||||
const struct scmi_sensor_descriptor *sdesc = st->priv;
|
||||
|
||||
attrh = le32_to_cpu(adesc->attributes_high);
|
||||
a->scale = S32_EXT(SENSOR_SCALE(attrh));
|
||||
a->type = SENSOR_TYPE(attrh);
|
||||
strlcpy(a->name, adesc->name, SCMI_MAX_STR_SIZE);
|
||||
s = &si->sensors[st->desc_index + st->loop_idx];
|
||||
s->id = le32_to_cpu(sdesc->id);
|
||||
|
||||
if (a->extended_attrs) {
|
||||
unsigned int ares =
|
||||
le32_to_cpu(adesc->resolution);
|
||||
attrl = le32_to_cpu(sdesc->attributes_low);
|
||||
/* common bitfields parsing */
|
||||
s->async = SUPPORTS_ASYNC_READ(attrl);
|
||||
s->num_trip_points = NUM_TRIP_POINTS(attrl);
|
||||
/**
|
||||
* only SCMIv3.0 specific bitfield below.
|
||||
* Such bitfields are assumed to be zeroed on non
|
||||
* relevant fw versions...assuming fw not buggy !
|
||||
*/
|
||||
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
|
||||
s->timestamped = SUPPORTS_TIMESTAMP(attrl);
|
||||
if (s->timestamped)
|
||||
s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl));
|
||||
s->extended_scalar_attrs = SUPPORTS_EXTEND_ATTRS(attrl);
|
||||
|
||||
a->resolution = SENSOR_RES(ares);
|
||||
a->exponent =
|
||||
S32_EXT(SENSOR_RES_EXP(ares));
|
||||
dsize += sizeof(adesc->resolution);
|
||||
|
||||
scmi_parse_range_attrs(&a->attrs,
|
||||
&adesc->attrs);
|
||||
dsize += sizeof(adesc->attrs);
|
||||
}
|
||||
|
||||
adesc = (typeof(adesc))((u8 *)adesc + dsize);
|
||||
}
|
||||
|
||||
desc_index += num_returned;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, te);
|
||||
attrh = le32_to_cpu(sdesc->attributes_high);
|
||||
/* common bitfields parsing */
|
||||
s->scale = S32_EXT(SENSOR_SCALE(attrh));
|
||||
s->type = SENSOR_TYPE(attrh);
|
||||
/* Use pre-allocated pool wherever possible */
|
||||
s->intervals.desc = s->intervals.prealloc_pool;
|
||||
if (si->version == SCMIv2_SENSOR_PROTOCOL) {
|
||||
s->intervals.segmented = false;
|
||||
s->intervals.count = 1;
|
||||
/*
|
||||
* check for both returned and remaining to avoid infinite
|
||||
* loop due to buggy firmware
|
||||
* Convert SCMIv2.0 update interval format to
|
||||
* SCMIv3.0 to be used as the common exposed
|
||||
* descriptor, accessible via common macros.
|
||||
*/
|
||||
} while (num_returned && num_remaining);
|
||||
s->intervals.desc[0] = (SENSOR_UPDATE_BASE(attrh) << 5) |
|
||||
SENSOR_UPDATE_SCALE(attrh);
|
||||
} else {
|
||||
/*
|
||||
* From SCMIv3.0 update intervals are retrieved
|
||||
* via a dedicated (optional) command.
|
||||
* Since the command is optional, on error carry
|
||||
* on without any update interval.
|
||||
*/
|
||||
if (scmi_sensor_update_intervals(ph, s))
|
||||
dev_dbg(ph->dev,
|
||||
"Update Intervals not available for sensor ID:%d\n",
|
||||
s->id);
|
||||
}
|
||||
/**
|
||||
* only > SCMIv2.0 specific bitfield below.
|
||||
* Such bitfields are assumed to be zeroed on non
|
||||
* relevant fw versions...assuming fw not buggy !
|
||||
*/
|
||||
s->num_axis = min_t(unsigned int,
|
||||
SUPPORTS_AXIS(attrh) ?
|
||||
SENSOR_AXIS_NUMBER(attrh) : 0,
|
||||
SCMI_MAX_NUM_SENSOR_AXIS);
|
||||
strscpy(s->name, sdesc->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
/*
|
||||
* If supported overwrite short name with the extended
|
||||
* one; on error just carry on and use already provided
|
||||
* short name.
|
||||
*/
|
||||
if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(attrl))
|
||||
ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id,
|
||||
s->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
if (s->extended_scalar_attrs) {
|
||||
s->sensor_power = le32_to_cpu(sdesc->power);
|
||||
dsize += sizeof(sdesc->power);
|
||||
|
||||
/* Only for sensors reporting scalar values */
|
||||
if (s->num_axis == 0) {
|
||||
unsigned int sres = le32_to_cpu(sdesc->resolution);
|
||||
|
||||
s->resolution = SENSOR_RES(sres);
|
||||
s->exponent = S32_EXT(SENSOR_RES_EXP(sres));
|
||||
dsize += sizeof(sdesc->resolution);
|
||||
|
||||
scmi_parse_range_attrs(&s->scalar_attrs,
|
||||
&sdesc->scalar_attrs);
|
||||
dsize += sizeof(sdesc->scalar_attrs);
|
||||
}
|
||||
}
|
||||
|
||||
if (s->num_axis > 0)
|
||||
ret = scmi_sensor_axis_description(ph, s, si->version);
|
||||
|
||||
st->priv = ((u8 *)sdesc + dsize);
|
||||
|
||||
ph->xops->xfer_put(ph, te);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph,
|
||||
struct sensors_info *si)
|
||||
{
|
||||
int ret, cnt;
|
||||
u32 desc_index = 0;
|
||||
u16 num_returned, num_remaining;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_sensor_description *buf;
|
||||
void *iter;
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_sens_descr_prepare_message,
|
||||
.update_state = iter_sens_descr_update_state,
|
||||
.process_response = iter_sens_descr_process_response,
|
||||
};
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SENSOR_DESCRIPTION_GET,
|
||||
sizeof(__le32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
iter = ph->hops->iter_response_init(ph, &ops, si->num_sensors,
|
||||
SENSOR_DESCRIPTION_GET,
|
||||
sizeof(__le32), si);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
buf = t->rx.buf;
|
||||
|
||||
do {
|
||||
struct scmi_sensor_descriptor *sdesc;
|
||||
|
||||
/* Set the number of sensors to be skipped/already read */
|
||||
put_unaligned_le32(desc_index, t->tx.buf);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
num_returned = le16_to_cpu(buf->num_returned);
|
||||
num_remaining = le16_to_cpu(buf->num_remaining);
|
||||
|
||||
if (desc_index + num_returned > si->num_sensors) {
|
||||
dev_err(ph->dev, "No. of sensors can't exceed %d",
|
||||
si->num_sensors);
|
||||
break;
|
||||
}
|
||||
|
||||
sdesc = &buf->desc[0];
|
||||
for (cnt = 0; cnt < num_returned; cnt++) {
|
||||
u32 attrh, attrl;
|
||||
struct scmi_sensor_info *s;
|
||||
size_t dsize = SCMI_MSG_RESP_SENS_DESCR_BASE_SZ;
|
||||
|
||||
s = &si->sensors[desc_index + cnt];
|
||||
s->id = le32_to_cpu(sdesc->id);
|
||||
|
||||
attrl = le32_to_cpu(sdesc->attributes_low);
|
||||
/* common bitfields parsing */
|
||||
s->async = SUPPORTS_ASYNC_READ(attrl);
|
||||
s->num_trip_points = NUM_TRIP_POINTS(attrl);
|
||||
/**
|
||||
* only SCMIv3.0 specific bitfield below.
|
||||
* Such bitfields are assumed to be zeroed on non
|
||||
* relevant fw versions...assuming fw not buggy !
|
||||
*/
|
||||
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
|
||||
s->timestamped = SUPPORTS_TIMESTAMP(attrl);
|
||||
if (s->timestamped)
|
||||
s->tstamp_scale =
|
||||
S32_EXT(SENSOR_TSTAMP_EXP(attrl));
|
||||
s->extended_scalar_attrs =
|
||||
SUPPORTS_EXTEND_ATTRS(attrl);
|
||||
|
||||
attrh = le32_to_cpu(sdesc->attributes_high);
|
||||
/* common bitfields parsing */
|
||||
s->scale = S32_EXT(SENSOR_SCALE(attrh));
|
||||
s->type = SENSOR_TYPE(attrh);
|
||||
/* Use pre-allocated pool wherever possible */
|
||||
s->intervals.desc = s->intervals.prealloc_pool;
|
||||
if (si->version == SCMIv2_SENSOR_PROTOCOL) {
|
||||
s->intervals.segmented = false;
|
||||
s->intervals.count = 1;
|
||||
/*
|
||||
* Convert SCMIv2.0 update interval format to
|
||||
* SCMIv3.0 to be used as the common exposed
|
||||
* descriptor, accessible via common macros.
|
||||
*/
|
||||
s->intervals.desc[0] =
|
||||
(SENSOR_UPDATE_BASE(attrh) << 5) |
|
||||
SENSOR_UPDATE_SCALE(attrh);
|
||||
} else {
|
||||
/*
|
||||
* From SCMIv3.0 update intervals are retrieved
|
||||
* via a dedicated (optional) command.
|
||||
* Since the command is optional, on error carry
|
||||
* on without any update interval.
|
||||
*/
|
||||
if (scmi_sensor_update_intervals(ph, s))
|
||||
dev_dbg(ph->dev,
|
||||
"Update Intervals not available for sensor ID:%d\n",
|
||||
s->id);
|
||||
}
|
||||
/**
|
||||
* only > SCMIv2.0 specific bitfield below.
|
||||
* Such bitfields are assumed to be zeroed on non
|
||||
* relevant fw versions...assuming fw not buggy !
|
||||
*/
|
||||
s->num_axis = min_t(unsigned int,
|
||||
SUPPORTS_AXIS(attrh) ?
|
||||
SENSOR_AXIS_NUMBER(attrh) : 0,
|
||||
SCMI_MAX_NUM_SENSOR_AXIS);
|
||||
strlcpy(s->name, sdesc->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
if (s->extended_scalar_attrs) {
|
||||
s->sensor_power = le32_to_cpu(sdesc->power);
|
||||
dsize += sizeof(sdesc->power);
|
||||
/* Only for sensors reporting scalar values */
|
||||
if (s->num_axis == 0) {
|
||||
unsigned int sres =
|
||||
le32_to_cpu(sdesc->resolution);
|
||||
|
||||
s->resolution = SENSOR_RES(sres);
|
||||
s->exponent =
|
||||
S32_EXT(SENSOR_RES_EXP(sres));
|
||||
dsize += sizeof(sdesc->resolution);
|
||||
|
||||
scmi_parse_range_attrs(&s->scalar_attrs,
|
||||
&sdesc->scalar_attrs);
|
||||
dsize += sizeof(sdesc->scalar_attrs);
|
||||
}
|
||||
}
|
||||
if (s->num_axis > 0) {
|
||||
ret = scmi_sensor_axis_description(ph, s);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdesc = (typeof(sdesc))((u8 *)sdesc + dsize);
|
||||
}
|
||||
|
||||
desc_index += num_returned;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, t);
|
||||
/*
|
||||
* check for both returned and remaining to avoid infinite
|
||||
* loop due to buggy firmware
|
||||
*/
|
||||
} while (num_returned && num_remaining);
|
||||
|
||||
out:
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
return ph->hops->iter_response_run(iter);
|
||||
}
|
||||
|
||||
static inline int
|
||||
@ -966,7 +1061,9 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
int ret;
|
||||
struct sensors_info *sinfo;
|
||||
|
||||
ph->xops->version_get(ph, &version);
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "Sensor Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) System Power Protocol
|
||||
*
|
||||
* Copyright (C) 2020-2021 ARM Ltd.
|
||||
* Copyright (C) 2020-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
|
||||
@ -10,7 +10,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define SCMI_SYSTEM_NUM_SOURCES 1
|
||||
@ -113,10 +113,13 @@ static const struct scmi_protocol_events system_protocol_events = {
|
||||
|
||||
static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int ret;
|
||||
u32 version;
|
||||
struct scmi_system_info *pinfo;
|
||||
|
||||
ph->xops->version_get(ph, &version);
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(ph->dev, "System Power Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
@ -2,13 +2,13 @@
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Voltage Protocol
|
||||
*
|
||||
* Copyright (C) 2020-2021 ARM Ltd.
|
||||
* Copyright (C) 2020-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "protocols.h"
|
||||
|
||||
#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
|
||||
#define REMAINING_LEVELS_MASK GENMASK(31, 16)
|
||||
@ -21,13 +21,16 @@ enum scmi_voltage_protocol_cmd {
|
||||
VOLTAGE_CONFIG_GET = 0x6,
|
||||
VOLTAGE_LEVEL_SET = 0x7,
|
||||
VOLTAGE_LEVEL_GET = 0x8,
|
||||
VOLTAGE_DOMAIN_NAME_GET = 0x09,
|
||||
};
|
||||
|
||||
#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
|
||||
|
||||
struct scmi_msg_resp_domain_attributes {
|
||||
__le32 attr;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
#define SUPPORTS_ASYNC_LEVEL_SET(x) ((x) & BIT(31))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(30))
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
};
|
||||
|
||||
struct scmi_msg_cmd_describe_levels {
|
||||
@ -54,6 +57,11 @@ struct scmi_msg_cmd_level_set {
|
||||
__le32 voltage_level;
|
||||
};
|
||||
|
||||
struct scmi_resp_voltage_level_set_complete {
|
||||
__le32 domain_id;
|
||||
__le32 voltage_level;
|
||||
};
|
||||
|
||||
struct voltage_info {
|
||||
unsigned int version;
|
||||
unsigned int num_domains;
|
||||
@ -110,14 +118,100 @@ static int scmi_init_voltage_levels(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct scmi_volt_ipriv {
|
||||
struct device *dev;
|
||||
struct scmi_voltage_info *v;
|
||||
};
|
||||
|
||||
static void iter_volt_levels_prepare_message(void *message,
|
||||
unsigned int desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_msg_cmd_describe_levels *msg = message;
|
||||
const struct scmi_volt_ipriv *p = priv;
|
||||
|
||||
msg->domain_id = cpu_to_le32(p->v->id);
|
||||
msg->level_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
static int iter_volt_levels_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 flags;
|
||||
const struct scmi_msg_resp_describe_levels *r = response;
|
||||
struct scmi_volt_ipriv *p = priv;
|
||||
|
||||
flags = le32_to_cpu(r->flags);
|
||||
st->num_returned = NUM_RETURNED_LEVELS(flags);
|
||||
st->num_remaining = NUM_REMAINING_LEVELS(flags);
|
||||
|
||||
/* Allocate space for num_levels if not already done */
|
||||
if (!p->v->num_levels) {
|
||||
ret = scmi_init_voltage_levels(p->dev, p->v, st->num_returned,
|
||||
st->num_remaining,
|
||||
SUPPORTS_SEGMENTED_LEVELS(flags));
|
||||
if (!ret)
|
||||
st->max_resources = p->v->num_levels;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
iter_volt_levels_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv)
|
||||
{
|
||||
s32 val;
|
||||
const struct scmi_msg_resp_describe_levels *r = response;
|
||||
struct scmi_volt_ipriv *p = priv;
|
||||
|
||||
val = (s32)le32_to_cpu(r->voltage[st->loop_idx]);
|
||||
p->v->levels_uv[st->desc_index + st->loop_idx] = val;
|
||||
if (val < 0)
|
||||
p->v->negative_volts_allowed = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_voltage_levels_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_voltage_info *v)
|
||||
{
|
||||
int ret;
|
||||
void *iter;
|
||||
struct scmi_msg_cmd_describe_levels *msg;
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_volt_levels_prepare_message,
|
||||
.update_state = iter_volt_levels_update_state,
|
||||
.process_response = iter_volt_levels_process_response,
|
||||
};
|
||||
struct scmi_volt_ipriv vpriv = {
|
||||
.dev = ph->dev,
|
||||
.v = v,
|
||||
};
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, v->num_levels,
|
||||
VOLTAGE_DESCRIBE_LEVELS,
|
||||
sizeof(*msg), &vpriv);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
ret = ph->hops->iter_response_run(iter);
|
||||
if (ret) {
|
||||
v->num_levels = 0;
|
||||
devm_kfree(ph->dev, v->levels_uv);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
|
||||
struct voltage_info *vinfo)
|
||||
{
|
||||
int ret, dom;
|
||||
struct scmi_xfer *td, *tl;
|
||||
struct device *dev = ph->dev;
|
||||
struct scmi_xfer *td;
|
||||
struct scmi_msg_resp_domain_attributes *resp_dom;
|
||||
struct scmi_msg_resp_describe_levels *resp_levels;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES,
|
||||
sizeof(__le32), sizeof(*resp_dom), &td);
|
||||
@ -125,16 +219,8 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
resp_dom = td->rx.buf;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, VOLTAGE_DESCRIBE_LEVELS,
|
||||
sizeof(__le64), 0, &tl);
|
||||
if (ret)
|
||||
goto outd;
|
||||
resp_levels = tl->rx.buf;
|
||||
|
||||
for (dom = 0; dom < vinfo->num_domains; dom++) {
|
||||
u32 desc_index = 0;
|
||||
u16 num_returned = 0, num_remaining = 0;
|
||||
struct scmi_msg_cmd_describe_levels *cmd;
|
||||
u32 attributes;
|
||||
struct scmi_voltage_info *v;
|
||||
|
||||
/* Retrieve domain attributes at first ... */
|
||||
@ -146,69 +232,31 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
v = vinfo->domains + dom;
|
||||
v->id = dom;
|
||||
v->attributes = le32_to_cpu(resp_dom->attr);
|
||||
attributes = le32_to_cpu(resp_dom->attr);
|
||||
strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
cmd = tl->tx.buf;
|
||||
/* ...then retrieve domain levels descriptions */
|
||||
do {
|
||||
u32 flags;
|
||||
int cnt;
|
||||
|
||||
cmd->domain_id = cpu_to_le32(v->id);
|
||||
cmd->level_index = cpu_to_le32(desc_index);
|
||||
ret = ph->xops->do_xfer(ph, tl);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
flags = le32_to_cpu(resp_levels->flags);
|
||||
num_returned = NUM_RETURNED_LEVELS(flags);
|
||||
num_remaining = NUM_REMAINING_LEVELS(flags);
|
||||
|
||||
/* Allocate space for num_levels if not already done */
|
||||
if (!v->num_levels) {
|
||||
ret = scmi_init_voltage_levels(dev, v,
|
||||
num_returned,
|
||||
num_remaining,
|
||||
SUPPORTS_SEGMENTED_LEVELS(flags));
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (desc_index + num_returned > v->num_levels) {
|
||||
dev_err(ph->dev,
|
||||
"No. of voltage levels can't exceed %d\n",
|
||||
v->num_levels);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < num_returned; cnt++) {
|
||||
s32 val;
|
||||
|
||||
val =
|
||||
(s32)le32_to_cpu(resp_levels->voltage[cnt]);
|
||||
v->levels_uv[desc_index + cnt] = val;
|
||||
if (val < 0)
|
||||
v->negative_volts_allowed = true;
|
||||
}
|
||||
|
||||
desc_index += num_returned;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, tl);
|
||||
/* check both to avoid infinite loop due to buggy fw */
|
||||
} while (num_returned && num_remaining);
|
||||
|
||||
if (ret) {
|
||||
v->num_levels = 0;
|
||||
devm_kfree(dev, v->levels_uv);
|
||||
/*
|
||||
* If supported overwrite short name with the extended one;
|
||||
* on error just carry on and use already provided short name.
|
||||
*/
|
||||
if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) {
|
||||
if (SUPPORTS_EXTENDED_NAMES(attributes))
|
||||
ph->hops->extended_name_get(ph,
|
||||
VOLTAGE_DOMAIN_NAME_GET,
|
||||
v->id, v->name,
|
||||
SCMI_MAX_STR_SIZE);
|
||||
if (SUPPORTS_ASYNC_LEVEL_SET(attributes))
|
||||
v->async_level_set = true;
|
||||
}
|
||||
|
||||
ret = scmi_voltage_levels_get(ph, v);
|
||||
/* Skip invalid voltage descriptors */
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ph->xops->reset_rx_to_maxsz(ph, td);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, tl);
|
||||
outd:
|
||||
ph->xops->xfer_put(ph, td);
|
||||
|
||||
return ret;
|
||||
@ -271,12 +319,15 @@ static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, u32 flags, s32 volt_uV)
|
||||
u32 domain_id,
|
||||
enum scmi_voltage_level_mode mode,
|
||||
s32 volt_uV)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct voltage_info *vinfo = ph->get_priv(ph);
|
||||
struct scmi_msg_cmd_level_set *cmd;
|
||||
struct scmi_voltage_info *v;
|
||||
|
||||
if (domain_id >= vinfo->num_domains)
|
||||
return -EINVAL;
|
||||
@ -286,12 +337,31 @@ static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
v = vinfo->domains + domain_id;
|
||||
|
||||
cmd = t->tx.buf;
|
||||
cmd->domain_id = cpu_to_le32(domain_id);
|
||||
cmd->flags = cpu_to_le32(flags);
|
||||
cmd->voltage_level = cpu_to_le32(volt_uV);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!v->async_level_set || mode != SCMI_VOLTAGE_LEVEL_SET_AUTO) {
|
||||
cmd->flags = cpu_to_le32(0x0);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
} else {
|
||||
cmd->flags = cpu_to_le32(0x1);
|
||||
ret = ph->xops->do_xfer_with_response(ph, t);
|
||||
if (!ret) {
|
||||
struct scmi_resp_voltage_level_set_complete *resp;
|
||||
|
||||
resp = t->rx.buf;
|
||||
if (le32_to_cpu(resp->domain_id) == domain_id)
|
||||
dev_dbg(ph->dev,
|
||||
"Voltage domain %d set async to %d\n",
|
||||
v->id,
|
||||
le32_to_cpu(resp->voltage_level));
|
||||
else
|
||||
ret = -EPROTO;
|
||||
}
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
|
@ -1379,6 +1379,10 @@ static const struct of_device_id qcom_scm_dt_match[] = {
|
||||
SCM_HAS_IFACE_CLK |
|
||||
SCM_HAS_BUS_CLK)
|
||||
},
|
||||
{ .compatible = "qcom,scm-msm8976", .data = (void *)(SCM_HAS_CORE_CLK |
|
||||
SCM_HAS_IFACE_CLK |
|
||||
SCM_HAS_BUS_CLK)
|
||||
},
|
||||
{ .compatible = "qcom,scm-msm8994" },
|
||||
{ .compatible = "qcom,scm-msm8996" },
|
||||
{ .compatible = "qcom,scm" },
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Texas Instruments System Control Interface Protocol Driver
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Copyright (C) 2015-2022 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Nishanth Menon
|
||||
*/
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
@ -96,6 +97,7 @@ struct ti_sci_desc {
|
||||
* @node: list head
|
||||
* @host_id: Host ID
|
||||
* @users: Number of users of this instance
|
||||
* @is_suspending: Flag set to indicate in suspend path.
|
||||
*/
|
||||
struct ti_sci_info {
|
||||
struct device *dev;
|
||||
@ -114,7 +116,7 @@ struct ti_sci_info {
|
||||
u8 host_id;
|
||||
/* protected by ti_sci_list_mutex */
|
||||
int users;
|
||||
|
||||
bool is_suspending;
|
||||
};
|
||||
|
||||
#define cl_to_ti_sci_info(c) container_of(c, struct ti_sci_info, cl)
|
||||
@ -349,6 +351,8 @@ static struct ti_sci_xfer *ti_sci_get_one_xfer(struct ti_sci_info *info,
|
||||
|
||||
hdr = (struct ti_sci_msg_hdr *)xfer->tx_message.buf;
|
||||
xfer->tx_message.len = tx_message_size;
|
||||
xfer->tx_message.chan_rx = info->chan_rx;
|
||||
xfer->tx_message.timeout_rx_ms = info->desc->max_rx_timeout_ms;
|
||||
xfer->rx_len = (u8)rx_message_size;
|
||||
|
||||
reinit_completion(&xfer->done);
|
||||
@ -406,6 +410,7 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info,
|
||||
int ret;
|
||||
int timeout;
|
||||
struct device *dev = info->dev;
|
||||
bool done_state = true;
|
||||
|
||||
ret = mbox_send_message(info->chan_tx, &xfer->tx_message);
|
||||
if (ret < 0)
|
||||
@ -413,13 +418,27 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info,
|
||||
|
||||
ret = 0;
|
||||
|
||||
/* And we wait for the response. */
|
||||
timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
|
||||
if (!wait_for_completion_timeout(&xfer->done, timeout)) {
|
||||
if (!info->is_suspending) {
|
||||
/* And we wait for the response. */
|
||||
timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
|
||||
if (!wait_for_completion_timeout(&xfer->done, timeout))
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
/*
|
||||
* If we are suspending, we cannot use wait_for_completion_timeout
|
||||
* during noirq phase, so we must manually poll the completion.
|
||||
*/
|
||||
ret = read_poll_timeout_atomic(try_wait_for_completion, done_state,
|
||||
true, 1,
|
||||
info->desc->max_rx_timeout_ms * 1000,
|
||||
false, &xfer->done);
|
||||
}
|
||||
|
||||
if (ret == -ETIMEDOUT || !done_state) {
|
||||
dev_err(dev, "Mbox timedout in resp(caller: %pS)\n",
|
||||
(void *)_RET_IP_);
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: we might prefer not to need the mailbox ticker to manage the
|
||||
* transfer queueing since the protocol layer queues things by itself.
|
||||
@ -3264,6 +3283,35 @@ static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
|
||||
static void ti_sci_set_is_suspending(struct ti_sci_info *info, bool is_suspending)
|
||||
{
|
||||
info->is_suspending = is_suspending;
|
||||
}
|
||||
|
||||
static int ti_sci_suspend(struct device *dev)
|
||||
{
|
||||
struct ti_sci_info *info = dev_get_drvdata(dev);
|
||||
/*
|
||||
* We must switch operation to polled mode now as drivers and the genpd
|
||||
* layer may make late TI SCI calls to change clock and device states
|
||||
* from the noirq phase of suspend.
|
||||
*/
|
||||
ti_sci_set_is_suspending(info, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_sci_resume(struct device *dev)
|
||||
{
|
||||
struct ti_sci_info *info = dev_get_drvdata(dev);
|
||||
|
||||
ti_sci_set_is_suspending(info, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(ti_sci_pm_ops, ti_sci_suspend, ti_sci_resume);
|
||||
|
||||
/* Description for K2G */
|
||||
static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = {
|
||||
.default_host_id = 2,
|
||||
@ -3472,6 +3520,7 @@ static struct platform_driver ti_sci_driver = {
|
||||
.driver = {
|
||||
.name = "ti-sci",
|
||||
.of_match_table = of_match_ptr(ti_sci_of_match),
|
||||
.pm = &ti_sci_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_sci_driver);
|
||||
|
@ -103,7 +103,7 @@ config TI_EMIF
|
||||
temperature changes
|
||||
|
||||
config OMAP_GPMC
|
||||
bool "Texas Instruments OMAP SoC GPMC driver" if COMPILE_TEST
|
||||
tristate "Texas Instruments OMAP SoC GPMC driver"
|
||||
depends on OF_ADDRESS
|
||||
select GPIOLIB
|
||||
help
|
||||
|
@ -857,7 +857,6 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct brcmstb_dpfe_priv *priv;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -869,22 +868,19 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev)
|
||||
mutex_init(&priv->lock);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu");
|
||||
priv->regs = devm_ioremap_resource(dev, res);
|
||||
priv->regs = devm_platform_ioremap_resource_byname(pdev, "dpfe-cpu");
|
||||
if (IS_ERR(priv->regs)) {
|
||||
dev_err(dev, "couldn't map DCPU registers\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem");
|
||||
priv->dmem = devm_ioremap_resource(dev, res);
|
||||
priv->dmem = devm_platform_ioremap_resource_byname(pdev, "dpfe-dmem");
|
||||
if (IS_ERR(priv->dmem)) {
|
||||
dev_err(dev, "Couldn't map DCPU data memory\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem");
|
||||
priv->imem = devm_ioremap_resource(dev, res);
|
||||
priv->imem = devm_platform_ioremap_resource_byname(pdev, "dpfe-imem");
|
||||
if (IS_ERR(priv->imem)) {
|
||||
dev_err(dev, "Couldn't map DCPU instruction memory\n");
|
||||
return -ENOENT;
|
||||
|
@ -115,8 +115,7 @@ static int da8xx_ddrctl_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ddrctl = devm_ioremap_resource(dev, res);
|
||||
ddrctl = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(ddrctl)) {
|
||||
dev_err(dev, "unable to map memory controller registers\n");
|
||||
return PTR_ERR(ddrctl);
|
||||
|
@ -1025,10 +1025,8 @@ static struct emif_data *__init_or_module get_device_details(
|
||||
temp = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
|
||||
dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);
|
||||
|
||||
if (!emif || !temp || !dev_info) {
|
||||
dev_err(dev, "%s:%d: allocation error\n", __func__, __LINE__);
|
||||
if (!emif || !temp || !dev_info)
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy(temp, pd, sizeof(*pd));
|
||||
pd = temp;
|
||||
@ -1067,9 +1065,6 @@ static struct emif_data *__init_or_module get_device_details(
|
||||
temp = devm_kzalloc(dev, sizeof(*cust_cfgs), GFP_KERNEL);
|
||||
if (temp)
|
||||
memcpy(temp, cust_cfgs, sizeof(*cust_cfgs));
|
||||
else
|
||||
dev_warn(dev, "%s:%d: allocation error\n", __func__,
|
||||
__LINE__);
|
||||
pd->custom_configs = temp;
|
||||
}
|
||||
|
||||
@ -1084,8 +1079,6 @@ static struct emif_data *__init_or_module get_device_details(
|
||||
memcpy(temp, pd->timings, size);
|
||||
pd->timings = temp;
|
||||
} else {
|
||||
dev_warn(dev, "%s:%d: allocation error\n", __func__,
|
||||
__LINE__);
|
||||
get_default_timings(emif);
|
||||
}
|
||||
} else {
|
||||
@ -1098,8 +1091,6 @@ static struct emif_data *__init_or_module get_device_details(
|
||||
memcpy(temp, pd->min_tck, sizeof(*pd->min_tck));
|
||||
pd->min_tck = temp;
|
||||
} else {
|
||||
dev_warn(dev, "%s:%d: allocation error\n", __func__,
|
||||
__LINE__);
|
||||
pd->min_tck = &lpddr2_jedec_min_tck;
|
||||
}
|
||||
} else {
|
||||
@ -1116,7 +1107,6 @@ error:
|
||||
static int __init_or_module emif_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct emif_data *emif;
|
||||
struct resource *res;
|
||||
int irq, ret;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
@ -1135,8 +1125,7 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
|
||||
emif->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, emif);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
emif->base = devm_ioremap_resource(emif->dev, res);
|
||||
emif->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(emif->base))
|
||||
goto error;
|
||||
|
||||
|
@ -172,7 +172,6 @@ out:
|
||||
static int ccf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ccf_private *ccf;
|
||||
struct resource *r;
|
||||
const struct of_device_id *match;
|
||||
u32 errinten;
|
||||
int ret, irq;
|
||||
@ -185,13 +184,7 @@ static int ccf_probe(struct platform_device *pdev)
|
||||
if (!ccf)
|
||||
return -ENOMEM;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "%s: no mem resource\n", __func__);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ccf->regs = devm_ioremap_resource(&pdev->dev, r);
|
||||
ccf->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ccf->regs))
|
||||
return PTR_ERR(ccf->regs);
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
@ -1889,16 +1890,6 @@ int gpmc_cs_program_settings(int cs, struct gpmc_settings *p)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id gpmc_dt_ids[] = {
|
||||
{ .compatible = "ti,omap2420-gpmc" },
|
||||
{ .compatible = "ti,omap2430-gpmc" },
|
||||
{ .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */
|
||||
{ .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */
|
||||
{ .compatible = "ti,am3352-gpmc" }, /* am335x devices */
|
||||
{ .compatible = "ti,am64-gpmc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static void gpmc_cs_set_name(int cs, const char *name)
|
||||
{
|
||||
struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
|
||||
@ -2257,11 +2248,9 @@ no_timings:
|
||||
if (!of_platform_device_create(child, NULL, &pdev->dev))
|
||||
goto err_child_fail;
|
||||
|
||||
/* is child a common bus? */
|
||||
if (of_match_node(of_default_bus_match_table, child))
|
||||
/* create children and other common bus children */
|
||||
if (of_platform_default_populate(child, NULL, &pdev->dev))
|
||||
goto err_child_fail;
|
||||
/* create children and other common bus children */
|
||||
if (of_platform_default_populate(child, NULL, &pdev->dev))
|
||||
goto err_child_fail;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2278,6 +2267,8 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id gpmc_dt_ids[];
|
||||
|
||||
static int gpmc_probe_dt(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@ -2644,6 +2635,19 @@ static int gpmc_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id gpmc_dt_ids[] = {
|
||||
{ .compatible = "ti,omap2420-gpmc" },
|
||||
{ .compatible = "ti,omap2430-gpmc" },
|
||||
{ .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */
|
||||
{ .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */
|
||||
{ .compatible = "ti,am3352-gpmc" }, /* am335x devices */
|
||||
{ .compatible = "ti,am64-gpmc" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpmc_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver gpmc_driver = {
|
||||
.probe = gpmc_probe,
|
||||
.remove = gpmc_remove,
|
||||
@ -2654,8 +2658,7 @@ static struct platform_driver gpmc_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static __init int gpmc_init(void)
|
||||
{
|
||||
return platform_driver_register(&gpmc_driver);
|
||||
}
|
||||
postcore_initcall(gpmc_init);
|
||||
module_platform_driver(gpmc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Texas Instruments GPMC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -259,8 +259,7 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev)
|
||||
|
||||
rpc->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
rpc->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
rpc->base = devm_platform_ioremap_resource_byname(pdev, "regs");
|
||||
if (IS_ERR(rpc->base))
|
||||
return PTR_ERR(rpc->base);
|
||||
|
||||
@ -488,7 +487,7 @@ int rpcif_manual_xfer(struct rpcif *rpc)
|
||||
case RPCIF_DATA_OUT:
|
||||
while (pos < rpc->xferlen) {
|
||||
u32 bytes_left = rpc->xferlen - pos;
|
||||
u32 nbytes, data[2];
|
||||
u32 nbytes, data[2], *p = data;
|
||||
|
||||
smcr = rpc->smcr | RPCIF_SMCR_SPIE;
|
||||
|
||||
@ -502,15 +501,9 @@ int rpcif_manual_xfer(struct rpcif *rpc)
|
||||
rpc->xfer_size = nbytes;
|
||||
|
||||
memcpy(data, rpc->buffer + pos, nbytes);
|
||||
if (nbytes == 8) {
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR1,
|
||||
data[0]);
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR0,
|
||||
data[1]);
|
||||
} else {
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR0,
|
||||
data[0]);
|
||||
}
|
||||
if (nbytes == 8)
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR1, *p++);
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR0, *p);
|
||||
|
||||
regmap_write(rpc->regmap, RPCIF_SMCR, smcr);
|
||||
ret = wait_msg_xfer_end(rpc);
|
||||
@ -552,7 +545,7 @@ int rpcif_manual_xfer(struct rpcif *rpc)
|
||||
}
|
||||
while (pos < rpc->xferlen) {
|
||||
u32 bytes_left = rpc->xferlen - pos;
|
||||
u32 nbytes, data[2];
|
||||
u32 nbytes, data[2], *p = data;
|
||||
|
||||
/* nbytes may only be 1, 2, 4, or 8 */
|
||||
nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left));
|
||||
@ -569,15 +562,9 @@ int rpcif_manual_xfer(struct rpcif *rpc)
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
if (nbytes == 8) {
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR1,
|
||||
&data[0]);
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR0,
|
||||
&data[1]);
|
||||
} else {
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR0,
|
||||
&data[0]);
|
||||
}
|
||||
if (nbytes == 8)
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR1, p++);
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR0, p);
|
||||
memcpy(rpc->buffer + pos, data, nbytes);
|
||||
|
||||
pos += nbytes;
|
||||
|
@ -1322,7 +1322,6 @@ static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc)
|
||||
*/
|
||||
static int exynos5_performance_counters_init(struct exynos5_dmc *dmc)
|
||||
{
|
||||
int counters_size;
|
||||
int ret, i;
|
||||
|
||||
dmc->num_counters = devfreq_event_get_edev_count(dmc->dev,
|
||||
@ -1332,8 +1331,8 @@ static int exynos5_performance_counters_init(struct exynos5_dmc *dmc)
|
||||
return dmc->num_counters;
|
||||
}
|
||||
|
||||
counters_size = sizeof(struct devfreq_event_dev) * dmc->num_counters;
|
||||
dmc->counter = devm_kzalloc(dmc->dev, counters_size, GFP_KERNEL);
|
||||
dmc->counter = devm_kcalloc(dmc->dev, dmc->num_counters,
|
||||
sizeof(*dmc->counter), GFP_KERNEL);
|
||||
if (!dmc->counter)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -9,6 +9,7 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra124.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186.o tegra234.o
|
||||
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
|
||||
|
||||
@ -19,5 +20,6 @@ obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o
|
||||
obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186-emc.o
|
||||
|
||||
tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o
|
||||
|
@ -44,6 +44,9 @@ static const struct of_device_id tegra_mc_of_match[] = {
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_194_SOC
|
||||
{ .compatible = "nvidia,tegra194-mc", .data = &tegra194_mc_soc },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_234_SOC
|
||||
{ .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc },
|
||||
#endif
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
@ -505,14 +508,54 @@ int tegra30_mc_probe(struct tegra_mc *mc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
const struct tegra_mc_ops tegra30_mc_ops = {
|
||||
.probe = tegra30_mc_probe,
|
||||
.handle_irq = tegra30_mc_handle_irq,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int mc_global_intstatus_to_channel(const struct tegra_mc *mc, u32 status,
|
||||
unsigned int *mc_channel)
|
||||
{
|
||||
if ((status & mc->soc->ch_intmask) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
*mc_channel = __ffs((status & mc->soc->ch_intmask) >>
|
||||
mc->soc->global_intstatus_channel_shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 mc_channel_to_global_intstatus(const struct tegra_mc *mc,
|
||||
unsigned int channel)
|
||||
{
|
||||
return BIT(channel) << mc->soc->global_intstatus_channel_shift;
|
||||
}
|
||||
|
||||
irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
{
|
||||
struct tegra_mc *mc = data;
|
||||
unsigned int bit, channel;
|
||||
unsigned long status;
|
||||
unsigned int bit;
|
||||
|
||||
/* mask all interrupts to avoid flooding */
|
||||
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
|
||||
if (mc->soc->num_channels) {
|
||||
u32 global_status;
|
||||
int err;
|
||||
|
||||
global_status = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MC_GLOBAL_INTSTATUS);
|
||||
err = mc_global_intstatus_to_channel(mc, global_status, &channel);
|
||||
if (err < 0) {
|
||||
dev_err_ratelimited(mc->dev, "unknown interrupt channel 0x%08x\n",
|
||||
global_status);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* mask all interrupts to avoid flooding */
|
||||
status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask;
|
||||
} else {
|
||||
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
|
||||
}
|
||||
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
@ -520,18 +563,70 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
const char *error = tegra_mc_status_names[bit] ?: "unknown";
|
||||
const char *client = "unknown", *desc;
|
||||
const char *direction, *secure;
|
||||
u32 status_reg, addr_reg;
|
||||
u32 intmask = BIT(bit);
|
||||
phys_addr_t addr = 0;
|
||||
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||
u32 addr_hi_reg = 0;
|
||||
#endif
|
||||
unsigned int i;
|
||||
char perm[7];
|
||||
u8 id, type;
|
||||
u32 value;
|
||||
|
||||
value = mc_readl(mc, MC_ERR_STATUS);
|
||||
switch (intmask) {
|
||||
case MC_INT_DECERR_VPR:
|
||||
status_reg = MC_ERR_VPR_STATUS;
|
||||
addr_reg = MC_ERR_VPR_ADR;
|
||||
break;
|
||||
|
||||
case MC_INT_SECERR_SEC:
|
||||
status_reg = MC_ERR_SEC_STATUS;
|
||||
addr_reg = MC_ERR_SEC_ADR;
|
||||
break;
|
||||
|
||||
case MC_INT_DECERR_MTS:
|
||||
status_reg = MC_ERR_MTS_STATUS;
|
||||
addr_reg = MC_ERR_MTS_ADR;
|
||||
break;
|
||||
|
||||
case MC_INT_DECERR_GENERALIZED_CARVEOUT:
|
||||
status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS;
|
||||
addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR;
|
||||
break;
|
||||
|
||||
case MC_INT_DECERR_ROUTE_SANITY:
|
||||
status_reg = MC_ERR_ROUTE_SANITY_STATUS;
|
||||
addr_reg = MC_ERR_ROUTE_SANITY_ADR;
|
||||
break;
|
||||
|
||||
default:
|
||||
status_reg = MC_ERR_STATUS;
|
||||
addr_reg = MC_ERR_ADR;
|
||||
|
||||
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||
if (mc->soc->has_addr_hi_reg)
|
||||
addr_hi_reg = MC_ERR_ADR_HI;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (mc->soc->num_channels)
|
||||
value = mc_ch_readl(mc, channel, status_reg);
|
||||
else
|
||||
value = mc_readl(mc, status_reg);
|
||||
|
||||
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||
if (mc->soc->num_address_bits > 32) {
|
||||
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
|
||||
MC_ERR_STATUS_ADR_HI_MASK);
|
||||
if (addr_hi_reg) {
|
||||
if (mc->soc->num_channels)
|
||||
addr = mc_ch_readl(mc, channel, addr_hi_reg);
|
||||
else
|
||||
addr = mc_readl(mc, addr_hi_reg);
|
||||
} else {
|
||||
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
|
||||
MC_ERR_STATUS_ADR_HI_MASK);
|
||||
}
|
||||
addr <<= 32;
|
||||
}
|
||||
#endif
|
||||
@ -588,7 +683,10 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
break;
|
||||
}
|
||||
|
||||
value = mc_readl(mc, MC_ERR_ADR);
|
||||
if (mc->soc->num_channels)
|
||||
value = mc_ch_readl(mc, channel, addr_reg);
|
||||
else
|
||||
value = mc_readl(mc, addr_reg);
|
||||
addr |= value;
|
||||
|
||||
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n",
|
||||
@ -597,17 +695,18 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
}
|
||||
|
||||
/* clear interrupts */
|
||||
mc_writel(mc, status, MC_INTSTATUS);
|
||||
if (mc->soc->num_channels) {
|
||||
mc_ch_writel(mc, channel, status, MC_INTSTATUS);
|
||||
mc_ch_writel(mc, MC_BROADCAST_CHANNEL,
|
||||
mc_channel_to_global_intstatus(mc, channel),
|
||||
MC_GLOBAL_INTSTATUS);
|
||||
} else {
|
||||
mc_writel(mc, status, MC_INTSTATUS);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
const struct tegra_mc_ops tegra30_mc_ops = {
|
||||
.probe = tegra30_mc_probe,
|
||||
.handle_irq = tegra30_mc_handle_irq,
|
||||
};
|
||||
#endif
|
||||
|
||||
const char *const tegra_mc_status_names[32] = {
|
||||
[ 1] = "External interrupt",
|
||||
[ 6] = "EMEM address decode error",
|
||||
@ -619,6 +718,8 @@ const char *const tegra_mc_status_names[32] = {
|
||||
[12] = "VPR violation",
|
||||
[13] = "Secure carveout violation",
|
||||
[16] = "MTS carveout violation",
|
||||
[17] = "Generalized carveout violation",
|
||||
[20] = "Route Sanity error",
|
||||
};
|
||||
|
||||
const char *const tegra_mc_error_names[8] = {
|
||||
@ -716,7 +817,6 @@ del_provider:
|
||||
|
||||
static int tegra_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct tegra_mc *mc;
|
||||
u64 mask;
|
||||
int err;
|
||||
@ -741,8 +841,7 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
/* length of MC tick in nanoseconds */
|
||||
mc->tick = 30;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mc->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
mc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(mc->regs))
|
||||
return PTR_ERR(mc->regs);
|
||||
|
||||
@ -761,7 +860,11 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
|
||||
WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n");
|
||||
|
||||
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
|
||||
if (mc->soc->num_channels)
|
||||
mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask,
|
||||
MC_INTMASK);
|
||||
else
|
||||
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
|
||||
|
||||
err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0,
|
||||
dev_name(&pdev->dev), mc);
|
||||
|
@ -43,7 +43,21 @@
|
||||
#define MC_EMEM_ARB_OVERRIDE 0xe8
|
||||
#define MC_TIMING_CONTROL_DBG 0xf8
|
||||
#define MC_TIMING_CONTROL 0xfc
|
||||
#define MC_ERR_VPR_STATUS 0x654
|
||||
#define MC_ERR_VPR_ADR 0x658
|
||||
#define MC_ERR_SEC_STATUS 0x67c
|
||||
#define MC_ERR_SEC_ADR 0x680
|
||||
#define MC_ERR_MTS_STATUS 0x9b0
|
||||
#define MC_ERR_MTS_ADR 0x9b4
|
||||
#define MC_ERR_ROUTE_SANITY_STATUS 0x9c0
|
||||
#define MC_ERR_ROUTE_SANITY_ADR 0x9c4
|
||||
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00
|
||||
#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04
|
||||
#define MC_GLOBAL_INTSTATUS 0xf24
|
||||
#define MC_ERR_ADR_HI 0x11fc
|
||||
|
||||
#define MC_INT_DECERR_ROUTE_SANITY BIT(20)
|
||||
#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17)
|
||||
#define MC_INT_DECERR_MTS BIT(16)
|
||||
#define MC_INT_SECERR_SEC BIT(13)
|
||||
#define MC_INT_DECERR_VPR BIT(12)
|
||||
@ -78,6 +92,8 @@
|
||||
|
||||
#define MC_TIMING_UPDATE BIT(0)
|
||||
|
||||
#define MC_BROADCAST_CHANNEL ~0
|
||||
|
||||
static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents)
|
||||
{
|
||||
val = val * percents;
|
||||
@ -92,6 +108,30 @@ icc_provider_to_tegra_mc(struct icc_provider *provider)
|
||||
return container_of(provider, struct tegra_mc, provider);
|
||||
}
|
||||
|
||||
static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch,
|
||||
unsigned long offset)
|
||||
{
|
||||
if (!mc->bcast_ch_regs)
|
||||
return 0;
|
||||
|
||||
if (ch == MC_BROADCAST_CHANNEL)
|
||||
return readl_relaxed(mc->bcast_ch_regs + offset);
|
||||
|
||||
return readl_relaxed(mc->ch_regs[ch] + offset);
|
||||
}
|
||||
|
||||
static inline void mc_ch_writel(const struct tegra_mc *mc, int ch,
|
||||
u32 value, unsigned long offset)
|
||||
{
|
||||
if (!mc->bcast_ch_regs)
|
||||
return;
|
||||
|
||||
if (ch == MC_BROADCAST_CHANNEL)
|
||||
writel_relaxed(value, mc->bcast_ch_regs + offset);
|
||||
else
|
||||
writel_relaxed(value, mc->ch_regs[ch] + offset);
|
||||
}
|
||||
|
||||
static inline u32 mc_readl(const struct tegra_mc *mc, unsigned long offset)
|
||||
{
|
||||
return readl_relaxed(mc->regs + offset);
|
||||
@ -137,6 +177,10 @@ extern const struct tegra_mc_soc tegra186_mc_soc;
|
||||
extern const struct tegra_mc_soc tegra194_mc_soc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_234_SOC
|
||||
extern const struct tegra_mc_soc tegra234_mc_soc;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_114_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_124_SOC) || \
|
||||
@ -147,10 +191,12 @@ extern const struct tegra_mc_ops tegra30_mc_ops;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
defined(CONFIG_ARCH_TEGRA_194_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
extern const struct tegra_mc_ops tegra186_mc_ops;
|
||||
#endif
|
||||
|
||||
irqreturn_t tegra30_mc_handle_irq(int irq, void *data);
|
||||
extern const char * const tegra_mc_status_names[32];
|
||||
extern const char * const tegra_mc_error_names[8];
|
||||
|
||||
|
@ -272,6 +272,9 @@ static const struct of_device_id tegra186_emc_of_match[] = {
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
{ .compatible = "nvidia,tegra194-emc" },
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
{ .compatible = "nvidia,tegra234-emc" },
|
||||
#endif
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <dt-bindings/memory/tegra186-mc.h>
|
||||
#endif
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
#define MC_SID_STREAMID_OVERRIDE_MASK GENMASK(7, 0)
|
||||
#define MC_SID_STREAMID_SECURITY_WRITE_ACCESS_DISABLED BIT(16)
|
||||
#define MC_SID_STREAMID_SECURITY_OVERRIDE BIT(8)
|
||||
@ -48,8 +50,37 @@ static void tegra186_mc_program_sid(struct tegra_mc *mc)
|
||||
|
||||
static int tegra186_mc_probe(struct tegra_mc *mc)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mc->dev);
|
||||
unsigned int i;
|
||||
char name[8];
|
||||
int err;
|
||||
|
||||
mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast");
|
||||
if (IS_ERR(mc->bcast_ch_regs)) {
|
||||
if (PTR_ERR(mc->bcast_ch_regs) == -EINVAL) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Broadcast channel is missing, please update your device-tree\n");
|
||||
mc->bcast_ch_regs = NULL;
|
||||
goto populate;
|
||||
}
|
||||
|
||||
return PTR_ERR(mc->bcast_ch_regs);
|
||||
}
|
||||
|
||||
mc->ch_regs = devm_kcalloc(mc->dev, mc->soc->num_channels, sizeof(*mc->ch_regs),
|
||||
GFP_KERNEL);
|
||||
if (!mc->ch_regs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < mc->soc->num_channels; i++) {
|
||||
snprintf(name, sizeof(name), "ch%u", i);
|
||||
|
||||
mc->ch_regs[i] = devm_platform_ioremap_resource_byname(pdev, name);
|
||||
if (IS_ERR(mc->ch_regs[i]))
|
||||
return PTR_ERR(mc->ch_regs[i]);
|
||||
}
|
||||
|
||||
populate:
|
||||
err = of_platform_populate(mc->dev->of_node, NULL, NULL, mc->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@ -144,6 +175,7 @@ const struct tegra_mc_ops tegra186_mc_ops = {
|
||||
.remove = tegra186_mc_remove,
|
||||
.resume = tegra186_mc_resume,
|
||||
.probe_device = tegra186_mc_probe_device,
|
||||
.handle_irq = tegra30_mc_handle_irq,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
|
||||
@ -875,6 +907,13 @@ const struct tegra_mc_soc tegra186_mc_soc = {
|
||||
.num_clients = ARRAY_SIZE(tegra186_mc_clients),
|
||||
.clients = tegra186_mc_clients,
|
||||
.num_address_bits = 40,
|
||||
.num_channels = 4,
|
||||
.client_id_mask = 0xff,
|
||||
.intmask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
|
||||
MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
|
||||
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
|
||||
.ops = &tegra186_mc_ops,
|
||||
.ch_intmask = 0x0000000f,
|
||||
.global_intstatus_channel_shift = 0,
|
||||
};
|
||||
#endif
|
||||
|
@ -1347,5 +1347,14 @@ const struct tegra_mc_soc tegra194_mc_soc = {
|
||||
.num_clients = ARRAY_SIZE(tegra194_mc_clients),
|
||||
.clients = tegra194_mc_clients,
|
||||
.num_address_bits = 40,
|
||||
.num_channels = 16,
|
||||
.client_id_mask = 0xff,
|
||||
.intmask = MC_INT_DECERR_ROUTE_SANITY |
|
||||
MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
|
||||
MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
|
||||
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
|
||||
.has_addr_hi_reg = true,
|
||||
.ops = &tegra186_mc_ops,
|
||||
.ch_intmask = 0x00000f00,
|
||||
.global_intstatus_channel_shift = 8,
|
||||
};
|
||||
|
110
drivers/memory/tegra/tegra234.c
Normal file
110
drivers/memory/tegra/tegra234.c
Normal file
@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <soc/tegra/mc.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra234-mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
{
|
||||
.id = TEGRA234_MEMORY_CLIENT_SDMMCRAB,
|
||||
.name = "sdmmcrab",
|
||||
.sid = TEGRA234_SID_SDMMC4,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x318,
|
||||
.security = 0x31c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_SDMMCWAB,
|
||||
.name = "sdmmcwab",
|
||||
.sid = TEGRA234_SID_SDMMC4,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x338,
|
||||
.security = 0x33c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPR,
|
||||
.name = "bpmpr",
|
||||
.sid = TEGRA234_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x498,
|
||||
.security = 0x49c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPW,
|
||||
.name = "bpmpw",
|
||||
.sid = TEGRA234_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4a0,
|
||||
.security = 0x4a4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPDMAR,
|
||||
.name = "bpmpdmar",
|
||||
.sid = TEGRA234_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4a8,
|
||||
.security = 0x4ac,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPDMAW,
|
||||
.name = "bpmpdmaw",
|
||||
.sid = TEGRA234_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4b0,
|
||||
.security = 0x4b4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_APEDMAR,
|
||||
.name = "apedmar",
|
||||
.sid = TEGRA234_SID_APE,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4f8,
|
||||
.security = 0x4fc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_APEDMAW,
|
||||
.name = "apedmaw",
|
||||
.sid = TEGRA234_SID_APE,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x500,
|
||||
.security = 0x504,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra234_mc_soc = {
|
||||
.num_clients = ARRAY_SIZE(tegra234_mc_clients),
|
||||
.clients = tegra234_mc_clients,
|
||||
.num_address_bits = 40,
|
||||
.num_channels = 16,
|
||||
.client_id_mask = 0x1ff,
|
||||
.intmask = MC_INT_DECERR_ROUTE_SANITY |
|
||||
MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
|
||||
MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
|
||||
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
|
||||
.has_addr_hi_reg = true,
|
||||
.ops = &tegra186_mc_ops,
|
||||
.ch_intmask = 0x0000ff00,
|
||||
.global_intstatus_channel_shift = 8,
|
||||
};
|
@ -328,7 +328,6 @@ static int aemif_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
int ret = -ENODEV;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child_np;
|
||||
@ -362,8 +361,7 @@ static int aemif_probe(struct platform_device *pdev)
|
||||
else if (pdata)
|
||||
aemif->cs_offset = pdata->cs_offset;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
aemif->base = devm_ioremap_resource(dev, res);
|
||||
aemif->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(aemif->base)) {
|
||||
ret = PTR_ERR(aemif->base);
|
||||
goto error;
|
||||
|
@ -290,9 +290,9 @@ static int ti_emif_probe(struct platform_device *pdev)
|
||||
|
||||
emif_data->pm_data.ti_emif_sram_config = (unsigned long)match->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
emif_data->pm_data.ti_emif_base_addr_virt = devm_ioremap_resource(dev,
|
||||
res);
|
||||
emif_data->pm_data.ti_emif_base_addr_virt = devm_platform_get_and_ioremap_resource(pdev,
|
||||
0,
|
||||
&res);
|
||||
if (IS_ERR(emif_data->pm_data.ti_emif_base_addr_virt)) {
|
||||
ret = PTR_ERR(emif_data->pm_data.ti_emif_base_addr_virt);
|
||||
return ret;
|
||||
|
@ -91,3 +91,16 @@ config NVME_TCP
|
||||
from https://github.com/linux-nvme/nvme-cli.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config NVME_APPLE
|
||||
tristate "Apple ANS2 NVM Express host driver"
|
||||
depends on OF && BLOCK
|
||||
depends on APPLE_RTKIT && APPLE_SART
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
select NVME_CORE
|
||||
help
|
||||
This provides support for the NVMe controller embedded in Apple SoCs
|
||||
such as the M1.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nvme-apple.
|
||||
|
@ -8,6 +8,7 @@ obj-$(CONFIG_NVME_FABRICS) += nvme-fabrics.o
|
||||
obj-$(CONFIG_NVME_RDMA) += nvme-rdma.o
|
||||
obj-$(CONFIG_NVME_FC) += nvme-fc.o
|
||||
obj-$(CONFIG_NVME_TCP) += nvme-tcp.o
|
||||
obj-$(CONFIG_NVME_APPLE) += nvme-apple.o
|
||||
|
||||
nvme-core-y := core.o ioctl.o constants.o
|
||||
nvme-core-$(CONFIG_TRACING) += trace.o
|
||||
@ -25,3 +26,5 @@ nvme-rdma-y += rdma.o
|
||||
nvme-fc-y += fc.o
|
||||
|
||||
nvme-tcp-y += tcp.o
|
||||
|
||||
nvme-apple-y += apple.o
|
||||
|
1593
drivers/nvme/host/apple.c
Normal file
1593
drivers/nvme/host/apple.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -183,7 +183,7 @@ config RESET_RASPBERRYPI
|
||||
|
||||
config RESET_RZG2L_USBPHY_CTRL
|
||||
tristate "Renesas RZ/G2L USBPHY control driver"
|
||||
depends on ARCH_R9A07G044 || COMPILE_TEST
|
||||
depends on ARCH_RZG2L || COMPILE_TEST
|
||||
help
|
||||
Support for USBPHY Control found on RZ/G2L family. It mainly
|
||||
controls reset and power down of the USB/PHY.
|
||||
@ -240,7 +240,7 @@ config RESET_SUNXI
|
||||
|
||||
config RESET_TI_SCI
|
||||
tristate "TI System Control Interface (TI-SCI) reset driver"
|
||||
depends on TI_SCI_PROTOCOL
|
||||
depends on TI_SCI_PROTOCOL || COMPILE_TEST
|
||||
help
|
||||
This enables the reset driver support over TI System Control Interface
|
||||
available on some new TI's SoCs. If you wish to use reset resources
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/kref.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
@ -1100,13 +1101,25 @@ EXPORT_SYMBOL_GPL(__devm_reset_control_bulk_get);
|
||||
*
|
||||
* Convenience wrapper for __reset_control_get() and reset_control_reset().
|
||||
* This is useful for the common case of devices with single, dedicated reset
|
||||
* lines.
|
||||
* lines. _RST firmware method will be called for devices with ACPI.
|
||||
*/
|
||||
int __device_reset(struct device *dev, bool optional)
|
||||
{
|
||||
struct reset_control *rstc;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
|
||||
if (handle) {
|
||||
if (!acpi_has_method(handle, "_RST"))
|
||||
return optional ? 0 : -ENOENT;
|
||||
if (ACPI_FAILURE(acpi_evaluate_object(handle, "_RST", NULL,
|
||||
NULL)))
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
rstc = __reset_control_get(dev, NULL, 0, 0, optional, true);
|
||||
if (IS_ERR(rstc))
|
||||
return PTR_ERR(rstc);
|
||||
|
@ -98,11 +98,17 @@ static const struct meson_reset_param meson_a1_param = {
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param meson_s4_param = {
|
||||
.reg_count = 6,
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_reset_dt_ids[] = {
|
||||
{ .compatible = "amlogic,meson8b-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-axg-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param},
|
||||
{ .compatible = "amlogic,meson-s4-reset", .data = &meson_s4_param},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
|
||||
|
@ -144,6 +144,7 @@ static const struct of_device_id reset_simple_dt_ids[] = {
|
||||
.data = &reset_simple_active_low },
|
||||
{ .compatible = "aspeed,ast2400-lpc-reset" },
|
||||
{ .compatible = "aspeed,ast2500-lpc-reset" },
|
||||
{ .compatible = "aspeed,ast2600-lpc-reset" },
|
||||
{ .compatible = "bitmain,bm1880-reset",
|
||||
.data = &reset_simple_active_low },
|
||||
{ .compatible = "brcm,bcm4908-misc-pcie-reset",
|
||||
|
@ -23,19 +23,32 @@ struct uniphier_glue_reset_soc_data {
|
||||
|
||||
struct uniphier_glue_reset_priv {
|
||||
struct clk_bulk_data clk[MAX_CLKS];
|
||||
struct reset_control *rst[MAX_RSTS];
|
||||
struct reset_control_bulk_data rst[MAX_RSTS];
|
||||
struct reset_simple_data rdata;
|
||||
const struct uniphier_glue_reset_soc_data *data;
|
||||
};
|
||||
|
||||
static void uniphier_clk_disable(void *_priv)
|
||||
{
|
||||
struct uniphier_glue_reset_priv *priv = _priv;
|
||||
|
||||
clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
|
||||
}
|
||||
|
||||
static void uniphier_rst_assert(void *_priv)
|
||||
{
|
||||
struct uniphier_glue_reset_priv *priv = _priv;
|
||||
|
||||
reset_control_bulk_assert(priv->data->nrsts, priv->rst);
|
||||
}
|
||||
|
||||
static int uniphier_glue_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct uniphier_glue_reset_priv *priv;
|
||||
struct resource *res;
|
||||
resource_size_t size;
|
||||
const char *name;
|
||||
int i, ret, nr;
|
||||
int i, ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@ -58,22 +71,28 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < priv->data->nrsts; i++) {
|
||||
name = priv->data->reset_names[i];
|
||||
priv->rst[i] = devm_reset_control_get_shared(dev, name);
|
||||
if (IS_ERR(priv->rst[i]))
|
||||
return PTR_ERR(priv->rst[i]);
|
||||
}
|
||||
for (i = 0; i < priv->data->nrsts; i++)
|
||||
priv->rst[i].id = priv->data->reset_names[i];
|
||||
ret = devm_reset_control_bulk_get_shared(dev, priv->data->nrsts,
|
||||
priv->rst);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (nr = 0; nr < priv->data->nrsts; nr++) {
|
||||
ret = reset_control_deassert(priv->rst[nr]);
|
||||
if (ret)
|
||||
goto out_rst_assert;
|
||||
}
|
||||
ret = devm_add_action_or_reset(dev, uniphier_clk_disable, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_bulk_deassert(priv->data->nrsts, priv->rst);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, uniphier_rst_assert, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_init(&priv->rdata.lock);
|
||||
priv->rdata.rcdev.owner = THIS_MODULE;
|
||||
@ -84,32 +103,7 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = devm_reset_controller_register(dev, &priv->rdata.rcdev);
|
||||
if (ret)
|
||||
goto out_rst_assert;
|
||||
|
||||
return 0;
|
||||
|
||||
out_rst_assert:
|
||||
while (nr--)
|
||||
reset_control_assert(priv->rst[nr]);
|
||||
|
||||
clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_glue_reset_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uniphier_glue_reset_priv *priv = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->data->nrsts; i++)
|
||||
reset_control_assert(priv->rst[i]);
|
||||
|
||||
clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
|
||||
|
||||
return 0;
|
||||
return devm_reset_controller_register(dev, &priv->rdata.rcdev);
|
||||
}
|
||||
|
||||
static const char * const uniphier_pro4_clock_reset_names[] = {
|
||||
@ -177,7 +171,6 @@ MODULE_DEVICE_TABLE(of, uniphier_glue_reset_match);
|
||||
|
||||
static struct platform_driver uniphier_glue_reset_driver = {
|
||||
.probe = uniphier_glue_reset_probe,
|
||||
.remove = uniphier_glue_reset_remove,
|
||||
.driver = {
|
||||
.name = "uniphier-glue-reset",
|
||||
.of_match_table = uniphier_glue_reset_match,
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ARCH_ACTIONS) += actions/
|
||||
obj-$(CONFIG_ARCH_APPLE) += apple/
|
||||
obj-y += apple/
|
||||
obj-y += aspeed/
|
||||
obj-$(CONFIG_ARCH_AT91) += atmel/
|
||||
obj-y += bcm/
|
||||
@ -22,7 +22,7 @@ obj-y += microchip/
|
||||
obj-y += amlogic/
|
||||
obj-y += qcom/
|
||||
obj-y += renesas/
|
||||
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
||||
obj-y += rockchip/
|
||||
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_SOC_SIFIVE) += sifive/
|
||||
obj-y += sunxi/
|
||||
|
@ -17,6 +17,30 @@ config APPLE_PMGR_PWRSTATE
|
||||
controls for SoC devices. This driver manages them through the
|
||||
generic power domain framework, and also provides reset support.
|
||||
|
||||
config APPLE_RTKIT
|
||||
tristate "Apple RTKit co-processor IPC protocol"
|
||||
depends on MAILBOX
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
default ARCH_APPLE
|
||||
help
|
||||
Apple SoCs such as the M1 come with various co-processors running
|
||||
their proprietary RTKit operating system. This option enables support
|
||||
for the protocol library used to communicate with those. It is used
|
||||
by various client drivers.
|
||||
|
||||
Say 'y' here if you have an Apple SoC.
|
||||
|
||||
config APPLE_SART
|
||||
tristate "Apple SART DMA address filter"
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
default ARCH_APPLE
|
||||
help
|
||||
Apple SART is a simple DMA address filter used on Apple SoCs such
|
||||
as the M1. It is usually required for the NVMe coprocessor which does
|
||||
not use a proper IOMMU.
|
||||
|
||||
Say 'y' here if you have an Apple SoC.
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
@ -1,2 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += apple-pmgr-pwrstate.o
|
||||
|
||||
obj-$(CONFIG_APPLE_RTKIT) += apple-rtkit.o
|
||||
apple-rtkit-y = rtkit.o rtkit-crashlog.o
|
||||
|
||||
obj-$(CONFIG_APPLE_SART) += apple-sart.o
|
||||
apple-sart-y = sart.o
|
||||
|
154
drivers/soc/apple/rtkit-crashlog.c
Normal file
154
drivers/soc/apple/rtkit-crashlog.c
Normal file
@ -0,0 +1,154 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/*
|
||||
* Apple RTKit IPC library
|
||||
* Copyright (C) The Asahi Linux Contributors
|
||||
*/
|
||||
#include "rtkit-internal.h"
|
||||
|
||||
#define FOURCC(a, b, c, d) \
|
||||
(((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d)))
|
||||
|
||||
#define APPLE_RTKIT_CRASHLOG_HEADER FOURCC('C', 'L', 'H', 'E')
|
||||
#define APPLE_RTKIT_CRASHLOG_STR FOURCC('C', 's', 't', 'r')
|
||||
#define APPLE_RTKIT_CRASHLOG_VERSION FOURCC('C', 'v', 'e', 'r')
|
||||
#define APPLE_RTKIT_CRASHLOG_MBOX FOURCC('C', 'm', 'b', 'x')
|
||||
#define APPLE_RTKIT_CRASHLOG_TIME FOURCC('C', 't', 'i', 'm')
|
||||
|
||||
struct apple_rtkit_crashlog_header {
|
||||
u32 fourcc;
|
||||
u32 version;
|
||||
u32 size;
|
||||
u32 flags;
|
||||
u8 _unk[16];
|
||||
};
|
||||
static_assert(sizeof(struct apple_rtkit_crashlog_header) == 0x20);
|
||||
|
||||
struct apple_rtkit_crashlog_mbox_entry {
|
||||
u64 msg0;
|
||||
u64 msg1;
|
||||
u32 timestamp;
|
||||
u8 _unk[4];
|
||||
};
|
||||
static_assert(sizeof(struct apple_rtkit_crashlog_mbox_entry) == 0x18);
|
||||
|
||||
static void apple_rtkit_crashlog_dump_str(struct apple_rtkit *rtk, u8 *bfr,
|
||||
size_t size)
|
||||
{
|
||||
u32 idx;
|
||||
u8 *ptr, *end;
|
||||
|
||||
memcpy(&idx, bfr, 4);
|
||||
|
||||
ptr = bfr + 4;
|
||||
end = bfr + size;
|
||||
while (ptr < end) {
|
||||
u8 *newline = memchr(ptr, '\n', end - ptr);
|
||||
|
||||
if (newline) {
|
||||
u8 tmp = *newline;
|
||||
*newline = '\0';
|
||||
dev_warn(rtk->dev, "RTKit: Message (id=%x): %s\n", idx,
|
||||
ptr);
|
||||
*newline = tmp;
|
||||
ptr = newline + 1;
|
||||
} else {
|
||||
dev_warn(rtk->dev, "RTKit: Message (id=%x): %s", idx,
|
||||
ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_rtkit_crashlog_dump_version(struct apple_rtkit *rtk, u8 *bfr,
|
||||
size_t size)
|
||||
{
|
||||
dev_warn(rtk->dev, "RTKit: Version: %s", bfr + 16);
|
||||
}
|
||||
|
||||
static void apple_rtkit_crashlog_dump_time(struct apple_rtkit *rtk, u8 *bfr,
|
||||
size_t size)
|
||||
{
|
||||
u64 crash_time;
|
||||
|
||||
memcpy(&crash_time, bfr, 8);
|
||||
dev_warn(rtk->dev, "RTKit: Crash time: %lld", crash_time);
|
||||
}
|
||||
|
||||
static void apple_rtkit_crashlog_dump_mailbox(struct apple_rtkit *rtk, u8 *bfr,
|
||||
size_t size)
|
||||
{
|
||||
u32 type, index, i;
|
||||
size_t n_messages;
|
||||
struct apple_rtkit_crashlog_mbox_entry entry;
|
||||
|
||||
memcpy(&type, bfr + 16, 4);
|
||||
memcpy(&index, bfr + 24, 4);
|
||||
n_messages = (size - 28) / sizeof(entry);
|
||||
|
||||
dev_warn(rtk->dev, "RTKit: Mailbox history (type = %d, index = %d)",
|
||||
type, index);
|
||||
for (i = 0; i < n_messages; ++i) {
|
||||
memcpy(&entry, bfr + 28 + i * sizeof(entry), sizeof(entry));
|
||||
dev_warn(rtk->dev, "RTKit: #%03d@%08x: %016llx %016llx", i,
|
||||
entry.timestamp, entry.msg0, entry.msg1);
|
||||
}
|
||||
}
|
||||
|
||||
void apple_rtkit_crashlog_dump(struct apple_rtkit *rtk, u8 *bfr, size_t size)
|
||||
{
|
||||
size_t offset;
|
||||
u32 section_fourcc, section_size;
|
||||
struct apple_rtkit_crashlog_header header;
|
||||
|
||||
memcpy(&header, bfr, sizeof(header));
|
||||
if (header.fourcc != APPLE_RTKIT_CRASHLOG_HEADER) {
|
||||
dev_warn(rtk->dev, "RTKit: Expected crashlog header but got %x",
|
||||
header.fourcc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.size > size) {
|
||||
dev_warn(rtk->dev, "RTKit: Crashlog size (%x) is too large",
|
||||
header.size);
|
||||
return;
|
||||
}
|
||||
|
||||
size = header.size;
|
||||
offset = sizeof(header);
|
||||
|
||||
while (offset < size) {
|
||||
memcpy(§ion_fourcc, bfr + offset, 4);
|
||||
memcpy(§ion_size, bfr + offset + 12, 4);
|
||||
|
||||
switch (section_fourcc) {
|
||||
case APPLE_RTKIT_CRASHLOG_HEADER:
|
||||
dev_dbg(rtk->dev, "RTKit: End of crashlog reached");
|
||||
return;
|
||||
case APPLE_RTKIT_CRASHLOG_STR:
|
||||
apple_rtkit_crashlog_dump_str(rtk, bfr + offset + 16,
|
||||
section_size);
|
||||
break;
|
||||
case APPLE_RTKIT_CRASHLOG_VERSION:
|
||||
apple_rtkit_crashlog_dump_version(
|
||||
rtk, bfr + offset + 16, section_size);
|
||||
break;
|
||||
case APPLE_RTKIT_CRASHLOG_MBOX:
|
||||
apple_rtkit_crashlog_dump_mailbox(
|
||||
rtk, bfr + offset + 16, section_size);
|
||||
break;
|
||||
case APPLE_RTKIT_CRASHLOG_TIME:
|
||||
apple_rtkit_crashlog_dump_time(rtk, bfr + offset + 16,
|
||||
section_size);
|
||||
break;
|
||||
default:
|
||||
dev_warn(rtk->dev,
|
||||
"RTKit: Unknown crashlog section: %x",
|
||||
section_fourcc);
|
||||
}
|
||||
|
||||
offset += section_size;
|
||||
}
|
||||
|
||||
dev_warn(rtk->dev,
|
||||
"RTKit: End of crashlog reached but no footer present");
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user