mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
ARM: SoC drivers for 6.9
This is the usual mix of updates for drivers that are used on (mostly ARM) SoCs with no other top-level subsystem tree, including: - The SCMI firmware subsystem gains support for version 3.2 of the specification and updates to the notification code. - Feature updates for Tegra and Qualcomm platforms for added hardware support. - A number of platforms get soc_device additions for identifying newly added chips from Renesas, Qualcomm, Mediatek and Google. - Trivial improvements for firmware and memory drivers amongst others, in particular 'const' annotations throughout multiple subsystems. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmXvgbsACgkQYKtH/8kJ UieH8Q/+LRzESrScIwFq0/V7lE1AadmhwMwcEf1Fsq8aMrelvPm/SWvHgIWIHTvV IZ/g3XS/CnBxr1JG3nbyMMe/2otEY7JxsUOOqixIuZ2gdzJvzZOBHMi54xDwbFRx 4NbP0CRTy8K35XNnOkJO3TnwBFP+q2Fu6qHY90as8M2GIxQpWb8OONJHh8N2qPq+ Hi3H0jjKXMInnOKpNIEQI60N4F2djGMHWkDySwFtHu40RaJjCIfmVd3PWQGz7RHl WQHjZ6CB+/BDgqfG0ccQ7Cikc4BLorZsjKCn8bsaLtdp4HvRCTp2ZpuFFTRq6vay IxqJCXrgpKjM1k9plehObEhMv4lNMbD1djG8Y6hqC+PPKbDfOLvlcat3xUK2AGgb ROJtKDQMXfAeSnLpw9n4Ox+BZRmwMIOcTU/20N72hlcZKY1jq/KuSqQn+LPVKIrW pJIhWd1B8R+2O1TewuIe3fjvfQwgATMBHBUVNRkSrzqkpcZNGQ3M5koMpClVvY6T Z/+hdAg58EQw0K6ukJLyrevxs1pHHhYXLCECIoU/xPs4NX4hDk7rKTFv6fdLS4Y2 24qzjhIGYdhRXmhRQdVq+06cr3cvtm1z7Fqna3tW1+J6wtBnHO/xZ63M9n5saPcm NgKMAN7YLLMYuUNrd39W7U2wLGQCgknjhrbH8ZmxPypk467v08k= =bV/K -----END PGP SIGNATURE----- Merge tag 'soc-drivers-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull ARM SoC driver updates from Arnd Bergmann: "This is the usual mix of updates for drivers that are used on (mostly ARM) SoCs with no other top-level subsystem tree, including: - The SCMI firmware subsystem gains support for version 3.2 of the specification and updates to the notification code - Feature updates for Tegra and Qualcomm platforms for added hardware support - A number of platforms get soc_device additions for identifying newly added chips from Renesas, Qualcomm, Mediatek and Google - Trivial improvements for firmware and memory drivers amongst others, in particular 'const' annotations throughout multiple subsystems" * tag 'soc-drivers-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (96 commits) tee: make tee_bus_type const soc: qcom: aoss: add missing kerneldoc for qmp members soc: qcom: geni-se: drop unused kerneldoc struct geni_wrapper param soc: qcom: spm: fix building with CONFIG_REGULATOR=n bus: ti-sysc: constify the struct device_type usage memory: stm32-fmc2-ebi: keep power domain on memory: stm32-fmc2-ebi: add MP25 RIF support memory: stm32-fmc2-ebi: add MP25 support memory: stm32-fmc2-ebi: check regmap_read return value dt-bindings: memory-controller: st,stm32: add MP25 support dt-bindings: bus: imx-weim: convert to YAML watchdog: s3c2410_wdt: use exynos_get_pmu_regmap_by_phandle() for PMU regs soc: samsung: exynos-pmu: Add regmap support for SoCs that protect PMU regs MAINTAINERS: Update SCMI entry with HWMON driver MAINTAINERS: samsung: gs101: match patches touching Google Tensor SoC memory: tegra: Fix indentation memory: tegra: Add BPMP and ICC info for DLA clients memory: tegra: Correct DLA client names dt-bindings: memory: renesas,rpc-if: Document R-Car V4M support firmware: arm_scmi: Update the supported clock protocol version ...
This commit is contained in:
commit
2184dbcde4
@ -1,58 +0,0 @@
|
||||
SPM AVS Wrapper 2 (SAW2)
|
||||
|
||||
The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
|
||||
Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
|
||||
power-controller that transitions a piece of hardware (like a processor or
|
||||
subsystem) into and out of low power modes via a direct connection to
|
||||
the PMIC. It can also be wired up to interact with other processors in the
|
||||
system, notifying them when a low power state is entered or exited.
|
||||
|
||||
Multiple revisions of the SAW hardware are supported using these Device Nodes.
|
||||
SAW2 revisions differ in the register offset and configuration data. Also, the
|
||||
same revision of the SAW in different SoCs may have different configuration
|
||||
data due the differences in hardware capabilities. Hence the SoC name, the
|
||||
version of the SAW hardware in that SoC and the distinction between cpu (big
|
||||
or Little) or cache, may be needed to uniquely identify the SAW register
|
||||
configuration and initialization data. The compatible string is used to
|
||||
indicate this parameter.
|
||||
|
||||
PROPERTIES
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Must have
|
||||
"qcom,saw2"
|
||||
A more specific value could be one of:
|
||||
"qcom,apq8064-saw2-v1.1-cpu"
|
||||
"qcom,msm8226-saw2-v2.1-cpu"
|
||||
"qcom,msm8974-saw2-v2.1-cpu"
|
||||
"qcom,apq8084-saw2-v2.1-cpu"
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: the first element specifies the base address and size of
|
||||
the register region. An optional second element specifies
|
||||
the base address and size of the alias register region.
|
||||
|
||||
- regulator:
|
||||
Usage: optional
|
||||
Value type: boolean
|
||||
Definition: Indicates that this SPM device acts as a regulator device
|
||||
device for the core (CPU or Cache) the SPM is attached
|
||||
to.
|
||||
|
||||
Example 1:
|
||||
|
||||
power-controller@2099000 {
|
||||
compatible = "qcom,saw2";
|
||||
reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
|
||||
regulator;
|
||||
};
|
||||
|
||||
Example 2:
|
||||
saw0: power-controller@f9089000 {
|
||||
compatible = "qcom,apq8084-saw2-v2.1-cpu", "qcom,saw2";
|
||||
reg = <0xf9089000 0x1000>, <0xf9009000 0x1000>;
|
||||
};
|
@ -1,117 +0,0 @@
|
||||
Device tree bindings for i.MX Wireless External Interface Module (WEIM)
|
||||
|
||||
The term "wireless" does not imply that the WEIM is literally an interface
|
||||
without wires. It simply means that this module was originally designed for
|
||||
wireless and mobile applications that use low-power technology.
|
||||
|
||||
The actual devices are instantiated from the child nodes of a WEIM node.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should contain one of the following:
|
||||
"fsl,imx1-weim"
|
||||
"fsl,imx27-weim"
|
||||
"fsl,imx51-weim"
|
||||
"fsl,imx50-weim"
|
||||
"fsl,imx6q-weim"
|
||||
- reg: A resource specifier for the register space
|
||||
(see the example below)
|
||||
- clocks: the clock, see the example below.
|
||||
- #address-cells: Must be set to 2 to allow memory address translation
|
||||
- #size-cells: Must be set to 1 to allow CS address passing
|
||||
- ranges: Must be set up to reflect the memory layout with four
|
||||
integer values for each chip-select line in use:
|
||||
|
||||
<cs-number> 0 <physical address of mapping> <size>
|
||||
|
||||
Optional properties:
|
||||
|
||||
- fsl,weim-cs-gpr: For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
|
||||
devices, it should be the phandle to the system General
|
||||
Purpose Register controller that contains WEIM CS GPR
|
||||
register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0]
|
||||
should be set up as one of the following 4 possible
|
||||
values depending on the CS space configuration.
|
||||
|
||||
IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3
|
||||
---------------------------------------------
|
||||
05 128M 0M 0M 0M
|
||||
033 64M 64M 0M 0M
|
||||
0113 64M 32M 32M 0M
|
||||
01111 32M 32M 32M 32M
|
||||
|
||||
In case that the property is absent, the reset value or
|
||||
what bootloader sets up in IOMUXC_GPR1[11:0] will be
|
||||
used.
|
||||
|
||||
- fsl,burst-clk-enable For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
|
||||
devices, the presence of this property indicates that
|
||||
the weim bus should operate in Burst Clock Mode.
|
||||
|
||||
- fsl,continuous-burst-clk Make Burst Clock to output continuous clock.
|
||||
Without this option Burst Clock will output clock
|
||||
only when necessary. This takes effect only if
|
||||
"fsl,burst-clk-enable" is set.
|
||||
|
||||
Timing property for child nodes. It is mandatory, not optional.
|
||||
|
||||
- fsl,weim-cs-timing: The timing array, contains timing values for the
|
||||
child node. We get the CS indexes from the address
|
||||
ranges in the child node's "reg" property.
|
||||
The number of registers depends on the selected chip:
|
||||
For i.MX1, i.MX21 ("fsl,imx1-weim") there are two
|
||||
registers: CSxU, CSxL.
|
||||
For i.MX25, i.MX27, i.MX31 and i.MX35 ("fsl,imx27-weim")
|
||||
there are three registers: CSCRxU, CSCRxL, CSCRxA.
|
||||
For i.MX50, i.MX53 ("fsl,imx50-weim"),
|
||||
i.MX51 ("fsl,imx51-weim") and i.MX6Q ("fsl,imx6q-weim")
|
||||
there are six registers: CSxGCR1, CSxGCR2, CSxRCR1,
|
||||
CSxRCR2, CSxWCR1, CSxWCR2.
|
||||
|
||||
Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM:
|
||||
|
||||
weim: weim@21b8000 {
|
||||
compatible = "fsl,imx6q-weim";
|
||||
reg = <0x021b8000 0x4000>;
|
||||
clocks = <&clks 196>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0x08000000 0x08000000>;
|
||||
fsl,weim-cs-gpr = <&gpr>;
|
||||
|
||||
nor@0,0 {
|
||||
compatible = "cfi-flash";
|
||||
reg = <0 0 0x02000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
bank-width = <2>;
|
||||
fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000
|
||||
0x0000c000 0x1404a38e 0x00000000>;
|
||||
};
|
||||
};
|
||||
|
||||
Example for an imx6q-based board, a multi-chipselect device connected to WEIM:
|
||||
|
||||
In this case, both chip select 0 and 1 will be configured with the same timing
|
||||
array values.
|
||||
|
||||
weim: weim@21b8000 {
|
||||
compatible = "fsl,imx6q-weim";
|
||||
reg = <0x021b8000 0x4000>;
|
||||
clocks = <&clks 196>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0x08000000 0x02000000
|
||||
1 0 0x0a000000 0x02000000
|
||||
2 0 0x0c000000 0x02000000
|
||||
3 0 0x0e000000 0x02000000>;
|
||||
fsl,weim-cs-gpr = <&gpr>;
|
||||
|
||||
acme@0 {
|
||||
compatible = "acme,whatever";
|
||||
reg = <0 0 0x100>, <0 0x400000 0x800>,
|
||||
<1 0x400000 0x800>;
|
||||
fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100
|
||||
0x00000000 0xa0000240 0x00000000>;
|
||||
};
|
||||
};
|
@ -33,6 +33,7 @@ properties:
|
||||
- const: samsung,exynos7-hsi2c
|
||||
- items:
|
||||
- enum:
|
||||
- google,gs101-hsi2c
|
||||
- samsung,exynos850-hsi2c
|
||||
- const: samsung,exynosautov9-hsi2c
|
||||
- const: samsung,exynos5-hsi2c # Exynos5250 and Exynos5420
|
||||
|
@ -0,0 +1,31 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/fsl/fsl,imx-weim-peripherals.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: i.MX WEIM Bus Peripheral Nodes
|
||||
|
||||
maintainers:
|
||||
- Shawn Guo <shawnguo@kernel.org>
|
||||
- Sascha Hauer <s.hauer@pengutronix.de>
|
||||
|
||||
description:
|
||||
This binding is meant for the child nodes of the WEIM node. The node
|
||||
represents any device connected to the WEIM bus. It may be a Flash chip,
|
||||
RAM chip or Ethernet controller, etc. These properties are meant for
|
||||
configuring the WEIM settings/timings and will accompany the bindings
|
||||
supported by the respective device.
|
||||
|
||||
properties:
|
||||
reg: true
|
||||
|
||||
fsl,weim-cs-timing:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description:
|
||||
Timing values for the child node.
|
||||
minItems: 2
|
||||
maxItems: 6
|
||||
|
||||
# the WEIM child will have its own native properties
|
||||
additionalProperties: true
|
@ -0,0 +1,204 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/fsl/fsl,imx-weim.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: i.MX Wireless External Interface Module (WEIM)
|
||||
|
||||
maintainers:
|
||||
- Shawn Guo <shawnguo@kernel.org>
|
||||
- Sascha Hauer <s.hauer@pengutronix.de>
|
||||
|
||||
description:
|
||||
The term "wireless" does not imply that the WEIM is literally an interface
|
||||
without wires. It simply means that this module was originally designed for
|
||||
wireless and mobile applications that use low-power technology. The actual
|
||||
devices are instantiated from the child nodes of a WEIM node.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^memory-controller@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,imx1-weim
|
||||
- fsl,imx27-weim
|
||||
- fsl,imx50-weim
|
||||
- fsl,imx51-weim
|
||||
- fsl,imx6q-weim
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx31-weim
|
||||
- fsl,imx35-weim
|
||||
- const: fsl,imx27-weim
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx6sx-weim
|
||||
- fsl,imx6ul-weim
|
||||
- const: fsl,imx6q-weim
|
||||
|
||||
"#address-cells":
|
||||
const: 2
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
ranges: true
|
||||
|
||||
fsl,weim-cs-gpr:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: |
|
||||
Phandle to the system General Purpose Register controller that contains
|
||||
WEIM CS GPR register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0]
|
||||
should be set up as one of the following 4 possible values depending on
|
||||
the CS space configuration.
|
||||
|
||||
IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3
|
||||
---------------------------------------------
|
||||
05 128M 0M 0M 0M
|
||||
033 64M 64M 0M 0M
|
||||
0113 64M 32M 32M 0M
|
||||
01111 32M 32M 32M 32M
|
||||
|
||||
In case that the property is absent, the reset value or what bootloader
|
||||
sets up in IOMUXC_GPR1[11:0] will be used.
|
||||
|
||||
fsl,burst-clk-enable:
|
||||
type: boolean
|
||||
description:
|
||||
The presence of this property indicates that the weim bus should operate
|
||||
in Burst Clock Mode.
|
||||
|
||||
fsl,continuous-burst-clk:
|
||||
type: boolean
|
||||
description:
|
||||
Make Burst Clock to output continuous clock. Without this option Burst
|
||||
Clock will output clock only when necessary.
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-7],[0-9a-f]+$":
|
||||
type: object
|
||||
description: Devices attached to chip selects are represented as subnodes.
|
||||
$ref: fsl,imx-weim-peripherals.yaml
|
||||
additionalProperties: true
|
||||
required:
|
||||
- fsl,weim-cs-timing
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- ranges
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
not:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx50-weim
|
||||
- fsl,imx6q-weim
|
||||
then:
|
||||
properties:
|
||||
fsl,weim-cs-gpr: false
|
||||
fsl,burst-clk-enable: false
|
||||
- if:
|
||||
not:
|
||||
required:
|
||||
- fsl,burst-clk-enable
|
||||
then:
|
||||
properties:
|
||||
fsl,continuous-burst-clk: false
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: fsl,imx1-weim
|
||||
then:
|
||||
patternProperties:
|
||||
"^.*@[0-7],[0-9a-f]+$":
|
||||
properties:
|
||||
fsl,weim-cs-timing:
|
||||
items:
|
||||
items:
|
||||
- description: CSxU
|
||||
- description: CSxL
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx27-weim
|
||||
- fsl,imx31-weim
|
||||
- fsl,imx35-weim
|
||||
then:
|
||||
patternProperties:
|
||||
"^.*@[0-7],[0-9a-f]+$":
|
||||
properties:
|
||||
fsl,weim-cs-timing:
|
||||
items:
|
||||
items:
|
||||
- description: CSCRxU
|
||||
- description: CSCRxL
|
||||
- description: CSCRxA
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx50-weim
|
||||
- fsl,imx51-weim
|
||||
- fsl,imx6q-weim
|
||||
- fsl,imx6sx-weim
|
||||
- fsl,imx6ul-weim
|
||||
then:
|
||||
patternProperties:
|
||||
"^.*@[0-7],[0-9a-f]+$":
|
||||
properties:
|
||||
fsl,weim-cs-timing:
|
||||
items:
|
||||
items:
|
||||
- description: CSxGCR1
|
||||
- description: CSxGCR2
|
||||
- description: CSxRCR1
|
||||
- description: CSxRCR2
|
||||
- description: CSxWCR1
|
||||
- description: CSxWCR2
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
memory-controller@21b8000 {
|
||||
compatible = "fsl,imx6q-weim";
|
||||
reg = <0x021b8000 0x4000>;
|
||||
clocks = <&clks 196>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0 0x08000000 0x08000000>;
|
||||
fsl,weim-cs-gpr = <&gpr>;
|
||||
|
||||
flash@0,0 {
|
||||
compatible = "cfi-flash";
|
||||
reg = <0 0 0x02000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
bank-width = <2>;
|
||||
fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000
|
||||
0x0000c000 0x1404a38e 0x00000000>;
|
||||
};
|
||||
};
|
@ -37,5 +37,6 @@ allOf:
|
||||
- $ref: ingenic,nemc-peripherals.yaml#
|
||||
- $ref: intel,ixp4xx-expansion-peripheral-props.yaml#
|
||||
- $ref: ti,gpmc-child.yaml#
|
||||
- $ref: fsl/fsl,imx-weim-peripherals.yaml
|
||||
|
||||
additionalProperties: true
|
||||
|
@ -145,7 +145,7 @@ patternProperties:
|
||||
"^emc-table@[0-9]+$":
|
||||
$ref: "#/$defs/emc-table"
|
||||
|
||||
"^emc-tables@[a-z0-9-]+$":
|
||||
"^emc-tables@[a-f0-9-]+$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
|
@ -45,6 +45,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a779g0-rpc-if # R-Car V4H
|
||||
- renesas,r8a779h0-rpc-if # R-Car V4M
|
||||
- const: renesas,rcar-gen4-rpc-if # a generic R-Car gen4 device
|
||||
|
||||
- items:
|
||||
|
@ -23,7 +23,9 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32mp1-fmc2-ebi
|
||||
enum:
|
||||
- st,stm32mp1-fmc2-ebi
|
||||
- st,stm32mp25-fmc2-ebi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -34,6 +36,9 @@ properties:
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 2
|
||||
|
||||
|
46
Documentation/devicetree/bindings/soc/qcom/qcom,pbs.yaml
Normal file
46
Documentation/devicetree/bindings/soc/qcom/qcom,pbs.yaml
Normal file
@ -0,0 +1,46 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,pbs.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Technologies, Inc. Programmable Boot Sequencer
|
||||
|
||||
maintainers:
|
||||
- Anjelique Melendez <quic_amelende@quicinc.com>
|
||||
|
||||
description: |
|
||||
The Qualcomm Technologies, Inc. Programmable Boot Sequencer (PBS)
|
||||
supports triggering power up and power down sequences for clients
|
||||
upon request.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,pmi632-pbs
|
||||
- const: qcom,pbs
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/spmi/spmi.h>
|
||||
|
||||
pmic@0 {
|
||||
reg = <0x0 SPMI_USID>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pbs@7400 {
|
||||
compatible = "qcom,pmi632-pbs", "qcom,pbs";
|
||||
reg = <0x7400>;
|
||||
};
|
||||
};
|
@ -32,6 +32,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm8650-pmic-glink
|
||||
- qcom,x1e80100-pmic-glink
|
||||
- const: qcom,sm8550-pmic-glink
|
||||
- const: qcom,pmic-glink
|
||||
|
||||
@ -65,6 +66,7 @@ allOf:
|
||||
enum:
|
||||
- qcom,sm8450-pmic-glink
|
||||
- qcom,sm8550-pmic-glink
|
||||
- qcom,x1e80100-pmic-glink
|
||||
then:
|
||||
properties:
|
||||
orientation-gpios: false
|
||||
|
@ -35,6 +35,8 @@ properties:
|
||||
description: Phandle to an RPM MSG RAM slice containing the master stats
|
||||
minItems: 1
|
||||
maxItems: 5
|
||||
items:
|
||||
maxItems: 1
|
||||
|
||||
qcom,master-names:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
|
@ -1,23 +1,33 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,spm.yaml#
|
||||
$id: http://devicetree.org/schemas/soc/qcom/qcom,saw2.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Subsystem Power Manager
|
||||
title: Qualcomm Subsystem Power Manager / SPM AVS Wrapper 2 (SAW2)
|
||||
|
||||
maintainers:
|
||||
- Andy Gross <agross@kernel.org>
|
||||
- Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
|
||||
description: |
|
||||
This binding describes the Qualcomm Subsystem Power Manager, used to control
|
||||
the peripheral logic surrounding the application cores in Qualcomm platforms.
|
||||
The Qualcomm Subsystem Power Manager is used to control the peripheral logic
|
||||
surrounding the application cores in Qualcomm platforms.
|
||||
|
||||
The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
|
||||
Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
|
||||
power-controller that transitions a piece of hardware (like a processor or
|
||||
subsystem) into and out of low power modes via a direct connection to
|
||||
the PMIC. It can also be wired up to interact with other processors in the
|
||||
system, notifying them when a low power state is entered or exited.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,ipq4019-saw2-cpu
|
||||
- qcom,ipq4019-saw2-l2
|
||||
- qcom,ipq8064-saw2-cpu
|
||||
- qcom,sdm660-gold-saw2-v4.1-l2
|
||||
- qcom,sdm660-silver-saw2-v4.1-l2
|
||||
- qcom,msm8998-gold-saw2-v4.1-l2
|
||||
@ -26,16 +36,27 @@ properties:
|
||||
- qcom,msm8916-saw2-v3.0-cpu
|
||||
- qcom,msm8939-saw2-v3.0-cpu
|
||||
- qcom,msm8226-saw2-v2.1-cpu
|
||||
- qcom,msm8226-saw2-v2.1-l2
|
||||
- qcom,msm8960-saw2-cpu
|
||||
- qcom,msm8974-saw2-v2.1-cpu
|
||||
- qcom,msm8974-saw2-v2.1-l2
|
||||
- qcom,msm8976-gold-saw2-v2.3-l2
|
||||
- qcom,msm8976-silver-saw2-v2.3-l2
|
||||
- qcom,apq8084-saw2-v2.1-cpu
|
||||
- qcom,apq8084-saw2-v2.1-l2
|
||||
- qcom,apq8064-saw2-v1.1-cpu
|
||||
- const: qcom,saw2
|
||||
|
||||
reg:
|
||||
description: Base address and size of the SPM register region
|
||||
maxItems: 1
|
||||
items:
|
||||
- description: Base address and size of the SPM register region
|
||||
- description: Base address and size of the alias register region
|
||||
minItems: 1
|
||||
|
||||
regulator:
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
description: Indicates that this SPM device acts as a regulator device
|
||||
device for the core (CPU or Cache) the SPM is attached to.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
@ -82,4 +103,17 @@ examples:
|
||||
reg = <0x17912000 0x1000>;
|
||||
};
|
||||
|
||||
- |
|
||||
/*
|
||||
* Example 3: SAW2 with the bundled regulator definition.
|
||||
*/
|
||||
power-manager@2089000 {
|
||||
compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2";
|
||||
reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
|
||||
|
||||
regulator {
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
};
|
||||
};
|
||||
...
|
@ -72,6 +72,8 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- google,gs101-peric0-sysreg
|
||||
- google,gs101-peric1-sysreg
|
||||
- samsung,exynos850-cmgp-sysreg
|
||||
- samsung,exynos850-peri-sysreg
|
||||
- samsung,exynos850-sysreg
|
||||
|
@ -9091,6 +9091,7 @@ F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
|
||||
F: arch/arm64/boot/dts/exynos/google/
|
||||
F: drivers/clk/samsung/clk-gs101.c
|
||||
F: include/dt-bindings/clock/google,gs101.h
|
||||
K: [gG]oogle.?[tT]ensor
|
||||
|
||||
GPD POCKET FAN DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
@ -17369,7 +17370,6 @@ F: Documentation/devicetree/bindings/pinctrl/renesas,*
|
||||
F: drivers/pinctrl/renesas/
|
||||
|
||||
PIN CONTROLLER - SAMSUNG
|
||||
M: Tomasz Figa <tomasz.figa@gmail.com>
|
||||
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
R: Alim Akhtar <alim.akhtar@samsung.com>
|
||||
@ -19428,7 +19428,6 @@ F: drivers/media/platform/samsung/exynos4-is/
|
||||
SAMSUNG SOC CLOCK DRIVERS
|
||||
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
M: Tomasz Figa <tomasz.figa@gmail.com>
|
||||
M: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
R: Alim Akhtar <alim.akhtar@samsung.com>
|
||||
L: linux-samsung-soc@vger.kernel.org
|
||||
@ -21348,6 +21347,7 @@ F: drivers/clk/clk-sc[mp]i.c
|
||||
F: drivers/cpufreq/sc[mp]i-cpufreq.c
|
||||
F: drivers/firmware/arm_scmi/
|
||||
F: drivers/firmware/arm_scpi.c
|
||||
F: drivers/hwmon/scmi-hwmon.c
|
||||
F: drivers/pmdomain/arm/
|
||||
F: drivers/powercap/arm_scmi_powercap.c
|
||||
F: drivers/regulator/scmi-regulator.c
|
||||
|
@ -186,11 +186,12 @@ config SUNXI_RSB
|
||||
|
||||
config TEGRA_ACONNECT
|
||||
tristate "Tegra ACONNECT Bus Driver"
|
||||
depends on ARCH_TEGRA_210_SOC
|
||||
depends on ARCH_TEGRA
|
||||
depends on OF && PM
|
||||
help
|
||||
Driver for the Tegra ACONNECT bus which is used to interface with
|
||||
the devices inside the Audio Processing Engine (APE) for Tegra210.
|
||||
the devices inside the Audio Processing Engine (APE) for
|
||||
Tegra210 and later.
|
||||
|
||||
config TEGRA_GMI
|
||||
tristate "Tegra Generic Memory Interface bus driver"
|
||||
|
@ -128,7 +128,7 @@ struct sunxi_rsb {
|
||||
};
|
||||
|
||||
/* bus / slave device related functions */
|
||||
static struct bus_type sunxi_rsb_bus;
|
||||
static const struct bus_type sunxi_rsb_bus;
|
||||
|
||||
static int sunxi_rsb_device_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
@ -177,7 +177,7 @@ static int sunxi_rsb_device_modalias(const struct device *dev, struct kobj_ueven
|
||||
return of_device_uevent_modalias(dev, env);
|
||||
}
|
||||
|
||||
static struct bus_type sunxi_rsb_bus = {
|
||||
static const struct bus_type sunxi_rsb_bus = {
|
||||
.name = RSB_CTRL_NAME,
|
||||
.match = sunxi_rsb_device_match,
|
||||
.probe = sunxi_rsb_device_probe,
|
||||
|
@ -2400,7 +2400,7 @@ static int sysc_child_add_clocks(struct sysc *ddata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_type sysc_device_type = {
|
||||
static const struct device_type sysc_device_type = {
|
||||
};
|
||||
|
||||
static struct sysc *sysc_child_to_parent(struct device *dev)
|
||||
|
@ -105,7 +105,7 @@ static struct attribute *ffa_device_attributes_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ffa_device_attributes);
|
||||
|
||||
struct bus_type ffa_bus_type = {
|
||||
const struct bus_type ffa_bus_type = {
|
||||
.name = "arm_ffa",
|
||||
.match = ffa_device_match,
|
||||
.probe = ffa_device_probe,
|
||||
|
@ -141,6 +141,17 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_protocol_table_register(const struct scmi_device_id *id_table)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct scmi_device_id *entry;
|
||||
|
||||
for (entry = id_table; entry->name && ret == 0; entry++)
|
||||
ret = scmi_protocol_device_request(entry);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_protocol_device_unrequest - Helper to unrequest a device
|
||||
*
|
||||
@ -186,6 +197,15 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table
|
||||
mutex_unlock(&scmi_requested_devices_mtx);
|
||||
}
|
||||
|
||||
static void
|
||||
scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
|
||||
{
|
||||
const struct scmi_device_id *entry;
|
||||
|
||||
for (entry = id_table; entry->name; entry++)
|
||||
scmi_protocol_device_unrequest(entry);
|
||||
}
|
||||
|
||||
static const struct scmi_device_id *
|
||||
scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
|
||||
{
|
||||
@ -263,7 +283,7 @@ static void scmi_dev_remove(struct device *dev)
|
||||
scmi_drv->remove(scmi_dev);
|
||||
}
|
||||
|
||||
struct bus_type scmi_bus_type = {
|
||||
const struct bus_type scmi_bus_type = {
|
||||
.name = "scmi_protocol",
|
||||
.match = scmi_dev_match,
|
||||
.probe = scmi_dev_probe,
|
||||
@ -279,7 +299,7 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
|
||||
if (!driver->probe)
|
||||
return -EINVAL;
|
||||
|
||||
retval = scmi_protocol_device_request(driver->id_table);
|
||||
retval = scmi_protocol_table_register(driver->id_table);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
@ -299,7 +319,7 @@ EXPORT_SYMBOL_GPL(scmi_driver_register);
|
||||
void scmi_driver_unregister(struct scmi_driver *driver)
|
||||
{
|
||||
driver_unregister(&driver->driver);
|
||||
scmi_protocol_device_unrequest(driver->id_table);
|
||||
scmi_protocol_table_unregister(driver->id_table);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scmi_driver_unregister);
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "notify.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
|
||||
|
||||
enum scmi_clock_protocol_cmd {
|
||||
CLOCK_ATTRIBUTES = 0x3,
|
||||
@ -28,8 +28,13 @@ enum scmi_clock_protocol_cmd {
|
||||
CLOCK_POSSIBLE_PARENTS_GET = 0xC,
|
||||
CLOCK_PARENT_SET = 0xD,
|
||||
CLOCK_PARENT_GET = 0xE,
|
||||
CLOCK_GET_PERMISSIONS = 0xF,
|
||||
};
|
||||
|
||||
#define CLOCK_STATE_CONTROL_ALLOWED BIT(31)
|
||||
#define CLOCK_PARENT_CONTROL_ALLOWED BIT(30)
|
||||
#define CLOCK_RATE_CONTROL_ALLOWED BIT(29)
|
||||
|
||||
enum clk_state {
|
||||
CLK_STATE_DISABLE,
|
||||
CLK_STATE_ENABLE,
|
||||
@ -49,6 +54,8 @@ struct scmi_msg_resp_clock_attributes {
|
||||
#define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
|
||||
#define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28))
|
||||
#define SUPPORTS_EXTENDED_CONFIG(x) ((x) & BIT(27))
|
||||
#define SUPPORTS_GET_PERMISSIONS(x) ((x) & BIT(1))
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
__le32 clock_enable_latency;
|
||||
};
|
||||
@ -152,14 +159,18 @@ struct clock_info {
|
||||
u32 version;
|
||||
int num_clocks;
|
||||
int max_async_req;
|
||||
bool notify_rate_changed_cmd;
|
||||
bool notify_rate_change_requested_cmd;
|
||||
atomic_t cur_async_req;
|
||||
struct scmi_clock_info *clk;
|
||||
int (*clock_config_set)(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, enum clk_state state,
|
||||
u8 oem_type, u32 oem_val, bool atomic);
|
||||
enum scmi_clock_oem_config oem_type,
|
||||
u32 oem_val, bool atomic);
|
||||
int (*clock_config_get)(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, u8 oem_type, u32 *attributes,
|
||||
bool *enabled, u32 *oem_val, bool atomic);
|
||||
u32 clk_id, enum scmi_clock_oem_config oem_type,
|
||||
u32 *attributes, bool *enabled, u32 *oem_val,
|
||||
bool atomic);
|
||||
};
|
||||
|
||||
static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
|
||||
@ -167,6 +178,15 @@ static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
|
||||
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
|
||||
};
|
||||
|
||||
static inline struct scmi_clock_info *
|
||||
scmi_clock_domain_lookup(struct clock_info *ci, u32 clk_id)
|
||||
{
|
||||
if (clk_id >= ci->num_clocks)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return ci->clk + clk_id;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct clock_info *ci)
|
||||
@ -189,6 +209,17 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
if (!ret) {
|
||||
if (!ph->hops->protocol_msg_check(ph, CLOCK_RATE_NOTIFY, NULL))
|
||||
ci->notify_rate_changed_cmd = true;
|
||||
|
||||
if (!ph->hops->protocol_msg_check(ph,
|
||||
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
|
||||
NULL))
|
||||
ci->notify_rate_change_requested_cmd = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -284,14 +315,44 @@ static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
struct scmi_clock_info *clk)
|
||||
{
|
||||
struct scmi_xfer *t;
|
||||
u32 perm;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_GET_PERMISSIONS,
|
||||
sizeof(clk_id), sizeof(perm), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(clk_id, t->tx.buf);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
perm = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
clk->state_ctrl_forbidden = !(perm & CLOCK_STATE_CONTROL_ALLOWED);
|
||||
clk->rate_ctrl_forbidden = !(perm & CLOCK_RATE_CONTROL_ALLOWED);
|
||||
clk->parent_ctrl_forbidden = !(perm & CLOCK_PARENT_CONTROL_ALLOWED);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, struct scmi_clock_info *clk,
|
||||
u32 clk_id, struct clock_info *cinfo,
|
||||
u32 version)
|
||||
{
|
||||
int ret;
|
||||
u32 attributes;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_clock_attributes *attr;
|
||||
struct scmi_clock_info *clk = cinfo->clk + clk_id;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
|
||||
sizeof(clk_id), sizeof(*attr), &t);
|
||||
@ -324,12 +385,20 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
NULL, clk->name,
|
||||
SCMI_MAX_STR_SIZE);
|
||||
|
||||
if (SUPPORTS_RATE_CHANGED_NOTIF(attributes))
|
||||
if (cinfo->notify_rate_changed_cmd &&
|
||||
SUPPORTS_RATE_CHANGED_NOTIF(attributes))
|
||||
clk->rate_changed_notifications = true;
|
||||
if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
|
||||
if (cinfo->notify_rate_change_requested_cmd &&
|
||||
SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
|
||||
clk->rate_change_requested_notifications = true;
|
||||
if (SUPPORTS_PARENT_CLOCK(attributes))
|
||||
scmi_clock_possible_parents(ph, clk_id, clk);
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
|
||||
if (SUPPORTS_PARENT_CLOCK(attributes))
|
||||
scmi_clock_possible_parents(ph, clk_id, clk);
|
||||
if (SUPPORTS_GET_PERMISSIONS(attributes))
|
||||
scmi_clock_get_permissions(ph, clk_id, clk);
|
||||
if (SUPPORTS_EXTENDED_CONFIG(attributes))
|
||||
clk->extended_config = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -502,6 +571,14 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_clock_set_rate *cfg;
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (clk->rate_ctrl_forbidden)
|
||||
return -EACCES;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
@ -543,7 +620,8 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
|
||||
|
||||
static int
|
||||
scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
enum clk_state state, u8 __unused0, u32 __unused1,
|
||||
enum clk_state state,
|
||||
enum scmi_clock_oem_config __unused0, u32 __unused1,
|
||||
bool atomic)
|
||||
{
|
||||
int ret;
|
||||
@ -580,14 +658,16 @@ scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
if (clk_id >= ci->num_clocks)
|
||||
return -EINVAL;
|
||||
|
||||
clk = ci->clk + clk_id;
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (parent_id >= clk->num_parents)
|
||||
return -EINVAL;
|
||||
|
||||
if (clk->parent_ctrl_forbidden)
|
||||
return -EACCES;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET,
|
||||
sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
@ -628,10 +708,11 @@ scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* For SCMI clock v2.1 and onwards */
|
||||
/* For SCMI clock v3.0 and onwards */
|
||||
static int
|
||||
scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
enum clk_state state, u8 oem_type, u32 oem_val,
|
||||
enum clk_state state,
|
||||
enum scmi_clock_oem_config oem_type, u32 oem_val,
|
||||
bool atomic)
|
||||
{
|
||||
int ret;
|
||||
@ -671,6 +752,14 @@ static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (clk->state_ctrl_forbidden)
|
||||
return -EACCES;
|
||||
|
||||
return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE,
|
||||
NULL_OEM_TYPE, 0, atomic);
|
||||
@ -680,16 +769,24 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (clk->state_ctrl_forbidden)
|
||||
return -EACCES;
|
||||
|
||||
return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE,
|
||||
NULL_OEM_TYPE, 0, atomic);
|
||||
}
|
||||
|
||||
/* For SCMI clock v2.1 and onwards */
|
||||
/* For SCMI clock v3.0 and onwards */
|
||||
static int
|
||||
scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u8 oem_type, u32 *attributes, bool *enabled,
|
||||
u32 *oem_val, bool atomic)
|
||||
enum scmi_clock_oem_config oem_type, u32 *attributes,
|
||||
bool *enabled, u32 *oem_val, bool atomic)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
@ -730,8 +827,8 @@ scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
|
||||
static int
|
||||
scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u8 oem_type, u32 *attributes, bool *enabled,
|
||||
u32 *oem_val, bool atomic)
|
||||
enum scmi_clock_oem_config oem_type, u32 *attributes,
|
||||
bool *enabled, u32 *oem_val, bool atomic)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
@ -768,20 +865,38 @@ static int scmi_clock_state_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, u8 oem_type, u32 oem_val,
|
||||
bool atomic)
|
||||
u32 clk_id,
|
||||
enum scmi_clock_oem_config oem_type,
|
||||
u32 oem_val, bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (!clk->extended_config)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ci->clock_config_set(ph, clk_id, CLK_STATE_UNCHANGED,
|
||||
oem_type, oem_val, atomic);
|
||||
}
|
||||
|
||||
static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, u8 oem_type, u32 *oem_val,
|
||||
u32 *attributes, bool atomic)
|
||||
u32 clk_id,
|
||||
enum scmi_clock_oem_config oem_type,
|
||||
u32 *oem_val, u32 *attributes, bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (!clk->extended_config)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ci->clock_config_get(ph, clk_id, oem_type, attributes,
|
||||
NULL, oem_val, atomic);
|
||||
@ -800,10 +915,10 @@ scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id)
|
||||
struct scmi_clock_info *clk;
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
if (clk_id >= ci->num_clocks)
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return NULL;
|
||||
|
||||
clk = ci->clk + clk_id;
|
||||
if (!clk->name[0])
|
||||
return NULL;
|
||||
|
||||
@ -824,6 +939,28 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
|
||||
.parent_get = scmi_clock_get_parent,
|
||||
};
|
||||
|
||||
static bool scmi_clk_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
bool supported;
|
||||
struct scmi_clock_info *clk;
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
if (evt_id >= ARRAY_SIZE(evt_2_cmd))
|
||||
return false;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, src_id);
|
||||
if (IS_ERR(clk))
|
||||
return false;
|
||||
|
||||
if (evt_id == SCMI_EVENT_CLOCK_RATE_CHANGED)
|
||||
supported = clk->rate_changed_notifications;
|
||||
else
|
||||
supported = clk->rate_change_requested_notifications;
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, int message_id, bool enable)
|
||||
{
|
||||
@ -908,6 +1045,7 @@ static const struct scmi_event clk_events[] = {
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops clk_event_ops = {
|
||||
.is_notify_supported = scmi_clk_notify_supported,
|
||||
.get_num_sources = scmi_clk_get_num_sources,
|
||||
.set_notify_enabled = scmi_clk_set_notify_enabled,
|
||||
.fill_custom_report = scmi_clk_fill_custom_report,
|
||||
@ -949,7 +1087,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, version);
|
||||
ret = scmi_clock_attributes_get(ph, clkid, cinfo, version);
|
||||
if (!ret)
|
||||
scmi_clock_describe_rates_get(ph, clkid, clk);
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph);
|
||||
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
|
||||
u8 *prot_imp);
|
||||
|
||||
extern struct bus_type scmi_bus_type;
|
||||
extern const struct bus_type scmi_bus_type;
|
||||
|
||||
#define SCMI_BUS_NOTIFY_DEVICE_REQUEST 0
|
||||
#define SCMI_BUS_NOTIFY_DEVICE_UNREQUEST 1
|
||||
|
@ -86,6 +86,12 @@ struct scmi_xfers_info {
|
||||
* @users: A refcount to track effective users of this protocol.
|
||||
* @priv: Reference for optional protocol private data.
|
||||
* @version: Protocol version supported by the platform as detected at runtime.
|
||||
* @negotiated_version: When the platform supports a newer protocol version,
|
||||
* the agent will try to negotiate with the platform the
|
||||
* usage of the newest version known to it, since
|
||||
* backward compatibility is NOT automatically assured.
|
||||
* This field is NON-zero when a successful negotiation
|
||||
* has completed.
|
||||
* @ph: An embedded protocol handle that will be passed down to protocol
|
||||
* initialization code to identify this instance.
|
||||
*
|
||||
@ -99,6 +105,7 @@ struct scmi_protocol_instance {
|
||||
refcount_t users;
|
||||
void *priv;
|
||||
unsigned int version;
|
||||
unsigned int negotiated_version;
|
||||
struct scmi_protocol_handle ph;
|
||||
};
|
||||
|
||||
@ -1754,10 +1761,44 @@ static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db)
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_protocol_msg_check - Check protocol message attributes
|
||||
*
|
||||
* @ph: A reference to the protocol handle.
|
||||
* @message_id: The ID of the message to check.
|
||||
* @attributes: A parameter to optionally return the retrieved message
|
||||
* attributes, in case of Success.
|
||||
*
|
||||
* An helper to check protocol message attributes for a specific protocol
|
||||
* and message pair.
|
||||
*
|
||||
* Return: 0 on SUCCESS
|
||||
*/
|
||||
static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
|
||||
u32 message_id, u32 *attributes)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES,
|
||||
sizeof(__le32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(message_id, t->tx.buf);
|
||||
ret = do_xfer(ph, t);
|
||||
if (!ret && attributes)
|
||||
*attributes = get_unaligned_le32(t->rx.buf);
|
||||
xfer_put(ph, t);
|
||||
|
||||
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,
|
||||
.protocol_msg_check = scmi_protocol_msg_check,
|
||||
.fastchannel_init = scmi_common_fastchannel_init,
|
||||
.fastchannel_db_ring = scmi_common_fastchannel_db_ring,
|
||||
};
|
||||
@ -1781,6 +1822,44 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph)
|
||||
return pi->handle->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_protocol_version_negotiate - Negotiate protocol version
|
||||
*
|
||||
* @ph: A reference to the protocol handle.
|
||||
*
|
||||
* An helper to negotiate a protocol version different from the latest
|
||||
* advertised as supported from the platform: on Success backward
|
||||
* compatibility is assured by the platform.
|
||||
*
|
||||
* Return: 0 on Success
|
||||
*/
|
||||
static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_protocol_instance *pi = ph_to_pi(ph);
|
||||
|
||||
/* At first check if NEGOTIATE_PROTOCOL_VERSION is supported ... */
|
||||
ret = scmi_protocol_msg_check(ph, NEGOTIATE_PROTOCOL_VERSION, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* ... then attempt protocol version negotiation */
|
||||
ret = xfer_get_init(ph, NEGOTIATE_PROTOCOL_VERSION,
|
||||
sizeof(__le32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(pi->proto->supported_version, t->tx.buf);
|
||||
ret = do_xfer(ph, t);
|
||||
if (!ret)
|
||||
pi->negotiated_version = pi->proto->supported_version;
|
||||
|
||||
xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_alloc_init_protocol_instance - Allocate and initialize a protocol
|
||||
* instance descriptor.
|
||||
@ -1853,11 +1932,21 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
|
||||
devres_close_group(handle->dev, pi->gid);
|
||||
dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
|
||||
|
||||
if (pi->version > proto->supported_version)
|
||||
dev_warn(handle->dev,
|
||||
"Detected UNSUPPORTED higher version 0x%X for protocol 0x%X."
|
||||
"Backward compatibility is NOT assured.\n",
|
||||
pi->version, pi->proto->id);
|
||||
if (pi->version > proto->supported_version) {
|
||||
ret = scmi_protocol_version_negotiate(&pi->ph);
|
||||
if (!ret) {
|
||||
dev_info(handle->dev,
|
||||
"Protocol 0x%X successfully negotiated version 0x%X\n",
|
||||
proto->id, pi->negotiated_version);
|
||||
} else {
|
||||
dev_warn(handle->dev,
|
||||
"Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
|
||||
pi->version, pi->proto->id);
|
||||
dev_warn(handle->dev,
|
||||
"Trying version 0x%X. Backward compatibility is NOT assured.\n",
|
||||
pi->proto->supported_version);
|
||||
}
|
||||
}
|
||||
|
||||
return pi;
|
||||
|
||||
|
@ -99,6 +99,7 @@
|
||||
#define PROTO_ID_MASK GENMASK(31, 24)
|
||||
#define EVT_ID_MASK GENMASK(23, 16)
|
||||
#define SRC_ID_MASK GENMASK(15, 0)
|
||||
#define NOTIF_UNSUPP -1
|
||||
|
||||
/*
|
||||
* Builds an unsigned 32bit key from the given input tuple to be used
|
||||
@ -788,6 +789,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
|
||||
|
||||
pd->ph = ph;
|
||||
for (i = 0; i < ee->num_events; i++, evt++) {
|
||||
int id;
|
||||
struct scmi_registered_event *r_evt;
|
||||
|
||||
r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt),
|
||||
@ -809,6 +811,11 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
|
||||
if (!r_evt->report)
|
||||
return -ENOMEM;
|
||||
|
||||
for (id = 0; id < r_evt->num_sources; id++)
|
||||
if (ee->ops->is_notify_supported &&
|
||||
!ee->ops->is_notify_supported(ph, r_evt->evt->id, id))
|
||||
refcount_set(&r_evt->sources[id], NOTIF_UNSUPP);
|
||||
|
||||
pd->registered_events[i] = r_evt;
|
||||
/* Ensure events are updated */
|
||||
smp_wmb();
|
||||
@ -1166,7 +1173,13 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt,
|
||||
int ret = 0;
|
||||
|
||||
sid = &r_evt->sources[src_id];
|
||||
if (refcount_read(sid) == 0) {
|
||||
if (refcount_read(sid) == NOTIF_UNSUPP) {
|
||||
dev_dbg(r_evt->proto->ph->dev,
|
||||
"Notification NOT supported - proto_id:%d evt_id:%d src_id:%d",
|
||||
r_evt->proto->id, r_evt->evt->id,
|
||||
src_id);
|
||||
ret = -EOPNOTSUPP;
|
||||
} else if (refcount_read(sid) == 0) {
|
||||
ret = REVT_NOTIFY_ENABLE(r_evt, r_evt->evt->id,
|
||||
src_id);
|
||||
if (!ret)
|
||||
@ -1179,6 +1192,8 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt,
|
||||
} else {
|
||||
for (; num_sources; src_id++, num_sources--) {
|
||||
sid = &r_evt->sources[src_id];
|
||||
if (refcount_read(sid) == NOTIF_UNSUPP)
|
||||
continue;
|
||||
if (refcount_dec_and_test(sid))
|
||||
REVT_NOTIFY_DISABLE(r_evt,
|
||||
r_evt->evt->id, src_id);
|
||||
|
@ -35,6 +35,8 @@ struct scmi_protocol_handle;
|
||||
|
||||
/**
|
||||
* struct scmi_event_ops - Protocol helpers called by the notification core.
|
||||
* @is_notify_supported: Return 0 if the specified notification for the
|
||||
* specified resource (src_id) is supported.
|
||||
* @get_num_sources: Returns the number of possible events' sources for this
|
||||
* protocol
|
||||
* @set_notify_enabled: Enable/disable the required evt_id/src_id notifications
|
||||
@ -50,6 +52,8 @@ struct scmi_protocol_handle;
|
||||
* process context.
|
||||
*/
|
||||
struct scmi_event_ops {
|
||||
bool (*is_notify_supported)(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id);
|
||||
int (*get_num_sources)(const struct scmi_protocol_handle *ph);
|
||||
int (*set_notify_enabled)(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enabled);
|
||||
|
@ -109,8 +109,10 @@ enum scmi_optee_pta_cmd {
|
||||
* @rx_len: Response size
|
||||
* @mu: Mutex protection on channel access
|
||||
* @cinfo: SCMI channel information
|
||||
* @shmem: Virtual base address of the shared memory
|
||||
* @req: Shared memory protocol handle for SCMI request and synchronous response
|
||||
* @req: union for SCMI interface
|
||||
* @req.shmem: Virtual base address of the shared memory
|
||||
* @req.msg: 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
|
||||
*/
|
||||
|
@ -182,6 +182,8 @@ struct scmi_perf_info {
|
||||
enum scmi_power_scale power_scale;
|
||||
u64 stats_addr;
|
||||
u32 stats_size;
|
||||
bool notify_lvl_cmd;
|
||||
bool notify_lim_cmd;
|
||||
struct perf_dom_info *dom_info;
|
||||
};
|
||||
|
||||
@ -222,6 +224,15 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
if (!ret) {
|
||||
if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LEVEL, NULL))
|
||||
pi->notify_lvl_cmd = true;
|
||||
|
||||
if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LIMITS, NULL))
|
||||
pi->notify_lim_cmd = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -239,6 +250,7 @@ static void scmi_perf_xa_destroy(void *data)
|
||||
static int
|
||||
scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct perf_dom_info *dom_info,
|
||||
bool notify_lim_cmd, bool notify_lvl_cmd,
|
||||
u32 version)
|
||||
{
|
||||
int ret;
|
||||
@ -260,8 +272,12 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
|
||||
dom_info->info.set_perf = SUPPORTS_SET_PERF_LVL(flags);
|
||||
dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
|
||||
dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
|
||||
if (notify_lim_cmd)
|
||||
dom_info->perf_limit_notify =
|
||||
SUPPORTS_PERF_LIMIT_NOTIFY(flags);
|
||||
if (notify_lvl_cmd)
|
||||
dom_info->perf_level_notify =
|
||||
SUPPORTS_PERF_LEVEL_NOTIFY(flags);
|
||||
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x4)
|
||||
dom_info->level_indexing_mode =
|
||||
@ -270,15 +286,30 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
le32_to_cpu(attr->sustained_freq_khz);
|
||||
dom_info->sustained_perf_level =
|
||||
le32_to_cpu(attr->sustained_perf_level);
|
||||
/*
|
||||
* sustained_freq_khz = mult_factor * sustained_perf_level
|
||||
* mult_factor must be non zero positive integer(not fraction)
|
||||
*/
|
||||
if (!dom_info->sustained_freq_khz ||
|
||||
!dom_info->sustained_perf_level ||
|
||||
dom_info->level_indexing_mode)
|
||||
dom_info->level_indexing_mode) {
|
||||
/* CPUFreq converts to kHz, hence default 1000 */
|
||||
dom_info->mult_factor = 1000;
|
||||
else
|
||||
} else {
|
||||
dom_info->mult_factor =
|
||||
(dom_info->sustained_freq_khz * 1000UL)
|
||||
/ dom_info->sustained_perf_level;
|
||||
if ((dom_info->sustained_freq_khz * 1000UL) %
|
||||
dom_info->sustained_perf_level)
|
||||
dev_warn(ph->dev,
|
||||
"multiplier for domain %d rounded\n",
|
||||
dom_info->id);
|
||||
}
|
||||
if (!dom_info->mult_factor)
|
||||
dev_warn(ph->dev,
|
||||
"Wrong sustained perf/frequency(domain %d)\n",
|
||||
dom_info->id);
|
||||
|
||||
strscpy(dom_info->info.name, attr->name,
|
||||
SCMI_SHORT_NAME_MAX_SIZE);
|
||||
}
|
||||
@ -295,9 +326,9 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
dom_info->id, NULL, dom_info->info.name,
|
||||
SCMI_MAX_STR_SIZE);
|
||||
|
||||
xa_init(&dom_info->opps_by_lvl);
|
||||
if (dom_info->level_indexing_mode) {
|
||||
xa_init(&dom_info->opps_by_idx);
|
||||
xa_init(&dom_info->opps_by_lvl);
|
||||
hash_init(dom_info->opps_by_freq);
|
||||
}
|
||||
|
||||
@ -340,13 +371,21 @@ static int iter_perf_levels_update_state(struct scmi_iterator_state *st,
|
||||
}
|
||||
|
||||
static inline void
|
||||
process_response_opp(struct scmi_opp *opp, unsigned int loop_idx,
|
||||
process_response_opp(struct device *dev, struct perf_dom_info *dom,
|
||||
struct scmi_opp *opp, unsigned int loop_idx,
|
||||
const struct scmi_msg_resp_perf_describe_levels *r)
|
||||
{
|
||||
int ret;
|
||||
|
||||
opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
|
||||
opp->power = le32_to_cpu(r->opp[loop_idx].power);
|
||||
opp->trans_latency_us =
|
||||
le16_to_cpu(r->opp[loop_idx].transition_latency_us);
|
||||
|
||||
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
|
||||
opp->perf, ret);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -354,16 +393,21 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
|
||||
struct scmi_opp *opp, unsigned int loop_idx,
|
||||
const struct scmi_msg_resp_perf_describe_levels_v4 *r)
|
||||
{
|
||||
int ret;
|
||||
|
||||
opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
|
||||
opp->power = le32_to_cpu(r->opp[loop_idx].power);
|
||||
opp->trans_latency_us =
|
||||
le16_to_cpu(r->opp[loop_idx].transition_latency_us);
|
||||
|
||||
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
|
||||
opp->perf, ret);
|
||||
|
||||
/* Note that PERF v4 reports always five 32-bit words */
|
||||
opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq);
|
||||
if (dom->level_indexing_mode) {
|
||||
int ret;
|
||||
|
||||
opp->level_index = le32_to_cpu(r->opp[loop_idx].level_index);
|
||||
|
||||
ret = xa_insert(&dom->opps_by_idx, opp->level_index, opp,
|
||||
@ -373,12 +417,6 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
|
||||
"Failed to add opps_by_idx at %d - ret:%d\n",
|
||||
opp->level_index, ret);
|
||||
|
||||
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
|
||||
if (ret)
|
||||
dev_warn(dev,
|
||||
"Failed to add opps_by_lvl at %d - ret:%d\n",
|
||||
opp->perf, ret);
|
||||
|
||||
hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq);
|
||||
}
|
||||
}
|
||||
@ -393,7 +431,8 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
|
||||
|
||||
opp = &p->perf_dom->opp[st->desc_index + st->loop_idx];
|
||||
if (PROTOCOL_REV_MAJOR(p->version) <= 0x3)
|
||||
process_response_opp(opp, st->loop_idx, response);
|
||||
process_response_opp(ph->dev, p->perf_dom, opp, st->loop_idx,
|
||||
response);
|
||||
else
|
||||
process_response_opp_v4(ph->dev, p->perf_dom, opp, st->loop_idx,
|
||||
response);
|
||||
@ -978,6 +1017,27 @@ static const struct scmi_perf_proto_ops perf_proto_ops = {
|
||||
.power_scale_get = scmi_power_scale_get,
|
||||
};
|
||||
|
||||
static bool scmi_perf_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
bool supported;
|
||||
struct perf_dom_info *dom;
|
||||
|
||||
if (evt_id >= ARRAY_SIZE(evt_2_cmd))
|
||||
return false;
|
||||
|
||||
dom = scmi_perf_domain_lookup(ph, src_id);
|
||||
if (IS_ERR(dom))
|
||||
return false;
|
||||
|
||||
if (evt_id == SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED)
|
||||
supported = dom->perf_limit_notify;
|
||||
else
|
||||
supported = dom->perf_level_notify;
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
@ -995,18 +1055,47 @@ static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_perf_xlate_opp_to_freq(struct perf_dom_info *dom,
|
||||
unsigned int index, unsigned long *freq)
|
||||
{
|
||||
struct scmi_opp *opp;
|
||||
|
||||
if (!dom || !freq)
|
||||
return -EINVAL;
|
||||
|
||||
if (!dom->level_indexing_mode) {
|
||||
opp = xa_load(&dom->opps_by_lvl, index);
|
||||
if (!opp)
|
||||
return -ENODEV;
|
||||
|
||||
*freq = opp->perf * dom->mult_factor;
|
||||
} else {
|
||||
opp = xa_load(&dom->opps_by_idx, index);
|
||||
if (!opp)
|
||||
return -ENODEV;
|
||||
|
||||
*freq = opp->indicative_freq * dom->mult_factor;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *scmi_perf_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)
|
||||
{
|
||||
int ret;
|
||||
void *rep = NULL;
|
||||
struct perf_dom_info *dom;
|
||||
|
||||
switch (evt_id) {
|
||||
case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED:
|
||||
{
|
||||
const struct scmi_perf_limits_notify_payld *p = payld;
|
||||
struct scmi_perf_limits_report *r = report;
|
||||
unsigned long freq_min, freq_max;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
break;
|
||||
@ -1016,14 +1105,36 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
r->range_max = le32_to_cpu(p->range_max);
|
||||
r->range_min = le32_to_cpu(p->range_min);
|
||||
/* Check if the reported domain exist at all */
|
||||
dom = scmi_perf_domain_lookup(ph, r->domain_id);
|
||||
if (IS_ERR(dom))
|
||||
break;
|
||||
/*
|
||||
* Event will be reported from this point on...
|
||||
* ...even if, later, xlated frequencies were not retrieved.
|
||||
*/
|
||||
*src_id = r->domain_id;
|
||||
rep = r;
|
||||
|
||||
ret = scmi_perf_xlate_opp_to_freq(dom, r->range_max, &freq_max);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = scmi_perf_xlate_opp_to_freq(dom, r->range_min, &freq_min);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* Report translated freqs ONLY if both available */
|
||||
r->range_max_freq = freq_max;
|
||||
r->range_min_freq = freq_min;
|
||||
|
||||
break;
|
||||
}
|
||||
case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED:
|
||||
{
|
||||
const struct scmi_perf_level_notify_payld *p = payld;
|
||||
struct scmi_perf_level_report *r = report;
|
||||
unsigned long freq;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
break;
|
||||
@ -1031,9 +1142,27 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
/* Report translated freqs ONLY if available */
|
||||
r->performance_level = le32_to_cpu(p->performance_level);
|
||||
/* Check if the reported domain exist at all */
|
||||
dom = scmi_perf_domain_lookup(ph, r->domain_id);
|
||||
if (IS_ERR(dom))
|
||||
break;
|
||||
/*
|
||||
* Event will be reported from this point on...
|
||||
* ...even if, later, xlated frequencies were not retrieved.
|
||||
*/
|
||||
*src_id = r->domain_id;
|
||||
rep = r;
|
||||
|
||||
/* Report translated freqs ONLY if available */
|
||||
ret = scmi_perf_xlate_opp_to_freq(dom, r->performance_level,
|
||||
&freq);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
r->performance_level_freq = freq;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -1067,6 +1196,7 @@ static const struct scmi_event perf_events[] = {
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops perf_event_ops = {
|
||||
.is_notify_supported = scmi_perf_notify_supported,
|
||||
.get_num_sources = scmi_perf_get_num_sources,
|
||||
.set_notify_enabled = scmi_perf_set_notify_enabled,
|
||||
.fill_custom_report = scmi_perf_fill_custom_report,
|
||||
@ -1111,7 +1241,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
struct perf_dom_info *dom = pinfo->dom_info + domain;
|
||||
|
||||
dom->id = domain;
|
||||
scmi_perf_domain_attributes_get(ph, dom, version);
|
||||
scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd,
|
||||
pinfo->notify_lvl_cmd, version);
|
||||
scmi_perf_describe_levels_get(ph, dom, version);
|
||||
|
||||
if (dom->perf_fastchannels)
|
||||
|
@ -68,6 +68,7 @@ struct power_dom_info {
|
||||
|
||||
struct scmi_power_info {
|
||||
u32 version;
|
||||
bool notify_state_change_cmd;
|
||||
int num_domains;
|
||||
u64 stats_addr;
|
||||
u32 stats_size;
|
||||
@ -97,13 +98,18 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
if (!ret)
|
||||
if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL))
|
||||
pi->notify_state_change_cmd = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct power_dom_info *dom_info,
|
||||
u32 version)
|
||||
u32 version, bool notify_state_change_cmd)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
@ -122,7 +128,9 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
if (!ret) {
|
||||
flags = le32_to_cpu(attr->flags);
|
||||
|
||||
dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
|
||||
if (notify_state_change_cmd)
|
||||
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);
|
||||
strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
|
||||
@ -231,6 +239,20 @@ static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
struct power_dom_info *dom;
|
||||
struct scmi_power_info *pinfo = ph->get_priv(ph);
|
||||
|
||||
if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED ||
|
||||
src_id >= pinfo->num_domains)
|
||||
return false;
|
||||
|
||||
dom = pinfo->dom_info + src_id;
|
||||
return dom->state_set_notify;
|
||||
}
|
||||
|
||||
static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
@ -285,6 +307,7 @@ static const struct scmi_event power_events[] = {
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops power_event_ops = {
|
||||
.is_notify_supported = scmi_power_notify_supported,
|
||||
.get_num_sources = scmi_power_get_num_sources,
|
||||
.set_notify_enabled = scmi_power_set_notify_enabled,
|
||||
.fill_custom_report = scmi_power_fill_custom_report,
|
||||
@ -326,7 +349,8 @@ 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, version);
|
||||
scmi_power_domain_attributes_get(ph, domain, dom, version,
|
||||
pinfo->notify_state_change_cmd);
|
||||
}
|
||||
|
||||
pinfo->version = version;
|
||||
|
@ -124,6 +124,8 @@ struct scmi_powercap_state {
|
||||
struct powercap_info {
|
||||
u32 version;
|
||||
int num_domains;
|
||||
bool notify_cap_cmd;
|
||||
bool notify_measurements_cmd;
|
||||
struct scmi_powercap_state *states;
|
||||
struct scmi_powercap_info *powercaps;
|
||||
};
|
||||
@ -157,6 +159,18 @@ scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
if (!ret) {
|
||||
if (!ph->hops->protocol_msg_check(ph,
|
||||
POWERCAP_CAP_NOTIFY, NULL))
|
||||
pi->notify_cap_cmd = true;
|
||||
|
||||
if (!ph->hops->protocol_msg_check(ph,
|
||||
POWERCAP_MEASUREMENTS_NOTIFY,
|
||||
NULL))
|
||||
pi->notify_measurements_cmd = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -200,10 +214,12 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
flags = le32_to_cpu(resp->attributes);
|
||||
|
||||
dom_info->id = domain;
|
||||
dom_info->notify_powercap_cap_change =
|
||||
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
|
||||
dom_info->notify_powercap_measurement_change =
|
||||
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
|
||||
if (pinfo->notify_cap_cmd)
|
||||
dom_info->notify_powercap_cap_change =
|
||||
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
|
||||
if (pinfo->notify_measurements_cmd)
|
||||
dom_info->notify_powercap_measurement_change =
|
||||
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
|
||||
dom_info->async_powercap_cap_set =
|
||||
SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
|
||||
dom_info->powercap_cap_config =
|
||||
@ -788,6 +804,26 @@ static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
bool supported = false;
|
||||
const struct scmi_powercap_info *dom_info;
|
||||
struct powercap_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
|
||||
return false;
|
||||
|
||||
dom_info = pi->powercaps + src_id;
|
||||
if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED)
|
||||
supported = dom_info->notify_powercap_cap_change;
|
||||
else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED)
|
||||
supported = dom_info->notify_powercap_measurement_change;
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
@ -904,6 +940,7 @@ static const struct scmi_event powercap_events[] = {
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops powercap_event_ops = {
|
||||
.is_notify_supported = scmi_powercap_notify_supported,
|
||||
.get_num_sources = scmi_powercap_get_num_sources,
|
||||
.set_notify_enabled = scmi_powercap_set_notify_enabled,
|
||||
.fill_custom_report = scmi_powercap_fill_custom_report,
|
||||
|
@ -33,6 +33,7 @@ enum scmi_common_cmd {
|
||||
PROTOCOL_VERSION = 0x0,
|
||||
PROTOCOL_ATTRIBUTES = 0x1,
|
||||
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
|
||||
NEGOTIATE_PROTOCOL_VERSION = 0x10,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -251,6 +252,8 @@ struct scmi_fc_info {
|
||||
* provided in @ops.
|
||||
* @iter_response_run: A common helper to trigger the run of a previously
|
||||
* initialized iterator.
|
||||
* @protocol_msg_check: A common helper to check is a specific protocol message
|
||||
* is supported.
|
||||
* @fastchannel_init: A common helper used to initialize FC descriptors by
|
||||
* gathering FC descriptions from the SCMI platform server.
|
||||
* @fastchannel_db_ring: A common helper to ring a FC doorbell.
|
||||
@ -264,6 +267,8 @@ struct scmi_proto_helpers_ops {
|
||||
unsigned int max_resources, u8 msg_id,
|
||||
size_t tx_size, void *priv);
|
||||
int (*iter_response_run)(void *iter);
|
||||
int (*protocol_msg_check)(const struct scmi_protocol_handle *ph,
|
||||
u32 message_id, u32 *attributes);
|
||||
void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
|
||||
u8 describe_id, u32 message_id,
|
||||
u32 valid_size, u32 domain,
|
||||
|
@ -67,6 +67,7 @@ struct reset_dom_info {
|
||||
struct scmi_reset_info {
|
||||
u32 version;
|
||||
int num_domains;
|
||||
bool notify_reset_cmd;
|
||||
struct reset_dom_info *dom_info;
|
||||
};
|
||||
|
||||
@ -89,18 +90,24 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
if (!ret)
|
||||
if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL))
|
||||
pi->notify_reset_cmd = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, struct reset_dom_info *dom_info,
|
||||
u32 version)
|
||||
struct scmi_reset_info *pinfo,
|
||||
u32 domain, u32 version)
|
||||
{
|
||||
int ret;
|
||||
u32 attributes;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_reset_domain_attributes *attr;
|
||||
struct reset_dom_info *dom_info = pinfo->dom_info + domain;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,
|
||||
sizeof(domain), sizeof(*attr), &t);
|
||||
@ -115,7 +122,9 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
attributes = le32_to_cpu(attr->attributes);
|
||||
|
||||
dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
|
||||
dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
|
||||
if (pinfo->notify_reset_cmd)
|
||||
dom_info->reset_notify =
|
||||
SUPPORTS_NOTIFY_RESET(attributes);
|
||||
dom_info->latency_us = le32_to_cpu(attr->latency);
|
||||
if (dom_info->latency_us == U32_MAX)
|
||||
dom_info->latency_us = 0;
|
||||
@ -226,6 +235,20 @@ static const struct scmi_reset_proto_ops reset_proto_ops = {
|
||||
.deassert = scmi_reset_domain_deassert,
|
||||
};
|
||||
|
||||
static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
struct reset_dom_info *dom;
|
||||
struct scmi_reset_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)
|
||||
return false;
|
||||
|
||||
dom = pi->dom_info + src_id;
|
||||
|
||||
return dom->reset_notify;
|
||||
}
|
||||
|
||||
static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
|
||||
u32 domain_id, bool enable)
|
||||
{
|
||||
@ -301,6 +324,7 @@ static const struct scmi_event reset_events[] = {
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops reset_event_ops = {
|
||||
.is_notify_supported = scmi_reset_notify_supported,
|
||||
.get_num_sources = scmi_reset_get_num_sources,
|
||||
.set_notify_enabled = scmi_reset_set_notify_enabled,
|
||||
.fill_custom_report = scmi_reset_fill_custom_report,
|
||||
@ -339,11 +363,8 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (!pinfo->dom_info)
|
||||
return -ENOMEM;
|
||||
|
||||
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, version);
|
||||
}
|
||||
for (domain = 0; domain < pinfo->num_domains; domain++)
|
||||
scmi_reset_domain_attributes_get(ph, pinfo, domain, version);
|
||||
|
||||
pinfo->version = version;
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
|
@ -215,6 +215,8 @@ struct scmi_sensor_update_notify_payld {
|
||||
|
||||
struct sensors_info {
|
||||
u32 version;
|
||||
bool notify_trip_point_cmd;
|
||||
bool notify_continuos_update_cmd;
|
||||
int num_sensors;
|
||||
int max_requests;
|
||||
u64 reg_addr;
|
||||
@ -246,6 +248,18 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
if (!ret) {
|
||||
if (!ph->hops->protocol_msg_check(ph,
|
||||
SENSOR_TRIP_POINT_NOTIFY, NULL))
|
||||
si->notify_trip_point_cmd = true;
|
||||
|
||||
if (!ph->hops->protocol_msg_check(ph,
|
||||
SENSOR_CONTINUOUS_UPDATE_NOTIFY,
|
||||
NULL))
|
||||
si->notify_continuos_update_cmd = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -594,7 +608,8 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
|
||||
* Such bitfields are assumed to be zeroed on non
|
||||
* relevant fw versions...assuming fw not buggy !
|
||||
*/
|
||||
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
|
||||
if (si->notify_continuos_update_cmd)
|
||||
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
|
||||
s->timestamped = SUPPORTS_TIMESTAMP(attrl);
|
||||
if (s->timestamped)
|
||||
s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl));
|
||||
@ -988,6 +1003,25 @@ static const struct scmi_sensor_proto_ops sensor_proto_ops = {
|
||||
.config_set = scmi_sensor_config_set,
|
||||
};
|
||||
|
||||
static bool scmi_sensor_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
bool supported = false;
|
||||
const struct scmi_sensor_info *s;
|
||||
struct sensors_info *sinfo = ph->get_priv(ph);
|
||||
|
||||
s = scmi_sensor_info_get(ph, src_id);
|
||||
if (!s)
|
||||
return false;
|
||||
|
||||
if (evt_id == SCMI_EVENT_SENSOR_TRIP_POINT_EVENT)
|
||||
supported = sinfo->notify_trip_point_cmd;
|
||||
else if (evt_id == SCMI_EVENT_SENSOR_UPDATE)
|
||||
supported = s->update;
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
static int scmi_sensor_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
@ -1099,6 +1133,7 @@ static const struct scmi_event sensor_events[] = {
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops sensor_event_ops = {
|
||||
.is_notify_supported = scmi_sensor_notify_supported,
|
||||
.get_num_sources = scmi_sensor_get_num_sources,
|
||||
.set_notify_enabled = scmi_sensor_set_notify_enabled,
|
||||
.fill_custom_report = scmi_sensor_fill_custom_report,
|
||||
|
@ -214,6 +214,13 @@ static int smc_chan_free(int id, void *p, void *data)
|
||||
struct scmi_chan_info *cinfo = p;
|
||||
struct scmi_smc *scmi_info = cinfo->transport_info;
|
||||
|
||||
/*
|
||||
* Different protocols might share the same chan info, so a previous
|
||||
* smc_chan_free call might have already freed the structure.
|
||||
*/
|
||||
if (!scmi_info)
|
||||
return 0;
|
||||
|
||||
/* Ignore any possible further reception on the IRQ path */
|
||||
if (scmi_info->irq > 0)
|
||||
free_irq(scmi_info->irq, scmi_info);
|
||||
|
@ -36,8 +36,20 @@ struct scmi_system_power_state_notifier_payld {
|
||||
struct scmi_system_info {
|
||||
u32 version;
|
||||
bool graceful_timeout_supported;
|
||||
bool power_state_notify_cmd;
|
||||
};
|
||||
|
||||
static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id)
|
||||
{
|
||||
struct scmi_system_info *pinfo = ph->get_priv(ph);
|
||||
|
||||
if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER)
|
||||
return false;
|
||||
|
||||
return pinfo->power_state_notify_cmd;
|
||||
}
|
||||
|
||||
static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
|
||||
bool enable)
|
||||
{
|
||||
@ -114,6 +126,7 @@ static const struct scmi_event system_events[] = {
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops system_event_ops = {
|
||||
.is_notify_supported = scmi_system_notify_supported,
|
||||
.set_notify_enabled = scmi_system_set_notify_enabled,
|
||||
.fill_custom_report = scmi_system_fill_custom_report,
|
||||
};
|
||||
@ -147,6 +160,9 @@ static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
|
||||
pinfo->graceful_timeout_supported = true;
|
||||
|
||||
if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL))
|
||||
pinfo->power_state_notify_cmd = true;
|
||||
|
||||
return ph->set_priv(ph, pinfo, version);
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ static const char *get_filename(struct tegra_bpmp *bpmp,
|
||||
|
||||
root_path_buf = kzalloc(root_path_buf_len, GFP_KERNEL);
|
||||
if (!root_path_buf)
|
||||
goto out;
|
||||
return NULL;
|
||||
|
||||
root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
|
||||
root_path_buf_len);
|
||||
|
@ -72,7 +72,6 @@ static DEFINE_SPINLOCK(emif_lock);
|
||||
static unsigned long irq_state;
|
||||
static LIST_HEAD(device_list);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
|
||||
struct emif_regs *regs)
|
||||
{
|
||||
@ -140,31 +139,24 @@ static int emif_mr4_show(struct seq_file *s, void *unused)
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(emif_mr4);
|
||||
|
||||
static int __init_or_module emif_debugfs_init(struct emif_data *emif)
|
||||
static void emif_debugfs_init(struct emif_data *emif)
|
||||
{
|
||||
emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
|
||||
debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
|
||||
&emif_regdump_fops);
|
||||
debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
|
||||
&emif_mr4_fops);
|
||||
return 0;
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
|
||||
emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
|
||||
debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
|
||||
&emif_regdump_fops);
|
||||
debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
|
||||
&emif_mr4_fops);
|
||||
}
|
||||
}
|
||||
|
||||
static void __exit emif_debugfs_exit(struct emif_data *emif)
|
||||
static void emif_debugfs_exit(struct emif_data *emif)
|
||||
{
|
||||
debugfs_remove_recursive(emif->debugfs_root);
|
||||
emif->debugfs_root = NULL;
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
|
||||
debugfs_remove_recursive(emif->debugfs_root);
|
||||
emif->debugfs_root = NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline int __init_or_module emif_debugfs_init(struct emif_data *emif)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __exit emif_debugfs_exit(struct emif_data *emif)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get bus width used by EMIF. Note that this may be different from the
|
||||
@ -679,7 +671,7 @@ static void disable_and_clear_all_interrupts(struct emif_data *emif)
|
||||
clear_all_interrupts(emif);
|
||||
}
|
||||
|
||||
static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
|
||||
static int setup_interrupts(struct emif_data *emif, u32 irq)
|
||||
{
|
||||
u32 interrupts, type;
|
||||
void __iomem *base = emif->base;
|
||||
@ -710,7 +702,7 @@ static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
|
||||
|
||||
}
|
||||
|
||||
static void __init_or_module emif_onetime_settings(struct emif_data *emif)
|
||||
static void emif_onetime_settings(struct emif_data *emif)
|
||||
{
|
||||
u32 pwr_mgmt_ctrl, zq, temp_alert_cfg;
|
||||
void __iomem *base = emif->base;
|
||||
@ -834,8 +826,7 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
|
||||
return valid;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
|
||||
static void of_get_custom_configs(struct device_node *np_emif,
|
||||
struct emif_data *emif)
|
||||
{
|
||||
struct emif_custom_configs *cust_cfgs = NULL;
|
||||
@ -884,7 +875,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
|
||||
emif->plat_data->custom_configs = cust_cfgs;
|
||||
}
|
||||
|
||||
static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
|
||||
static void of_get_ddr_info(struct device_node *np_emif,
|
||||
struct device_node *np_ddr,
|
||||
struct ddr_device_info *dev_info)
|
||||
{
|
||||
@ -918,7 +909,7 @@ static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
|
||||
dev_info->io_width = __fls(io_width) - 1;
|
||||
}
|
||||
|
||||
static struct emif_data * __init_or_module of_get_memory_device_details(
|
||||
static struct emif_data *of_get_memory_device_details(
|
||||
struct device_node *np_emif, struct device *dev)
|
||||
{
|
||||
struct emif_data *emif = NULL;
|
||||
@ -991,16 +982,7 @@ out:
|
||||
return emif;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static struct emif_data * __init_or_module of_get_memory_device_details(
|
||||
struct device_node *np_emif, struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct emif_data *__init_or_module get_device_details(
|
||||
static struct emif_data *get_device_details(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
u32 size;
|
||||
@ -1104,7 +1086,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __init_or_module emif_probe(struct platform_device *pdev)
|
||||
static int emif_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct emif_data *emif;
|
||||
int irq, ret;
|
||||
@ -1159,7 +1141,7 @@ error:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void __exit emif_remove(struct platform_device *pdev)
|
||||
static void emif_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct emif_data *emif = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1183,7 +1165,8 @@ MODULE_DEVICE_TABLE(of, emif_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver emif_driver = {
|
||||
.remove_new = __exit_p(emif_remove),
|
||||
.probe = emif_probe,
|
||||
.remove_new = emif_remove,
|
||||
.shutdown = emif_shutdown,
|
||||
.driver = {
|
||||
.name = "emif",
|
||||
@ -1191,7 +1174,7 @@ static struct platform_driver emif_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver_probe(emif_driver, emif_probe);
|
||||
module_platform_driver(emif_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -92,6 +92,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0RDB,
|
||||
.name = "dla0rdb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -102,6 +104,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0RDB1,
|
||||
.name = "dla0rdb1",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -112,6 +116,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0WRB,
|
||||
.name = "dla0wrb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -121,7 +127,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1RDB,
|
||||
.name = "dla0rdb",
|
||||
.name = "dla1rdb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -407,7 +415,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1RDB1,
|
||||
.name = "dla0rdb1",
|
||||
.name = "dla1rdb1",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -417,7 +427,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1WRB,
|
||||
.name = "dla0wrb",
|
||||
.name = "dla1wrb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -539,7 +551,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
.bpmp_id = TEGRA_ICC_BPMP_NVJPG_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVJPG,
|
||||
.regs = {
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x3f8,
|
||||
.security = 0x3fc,
|
||||
@ -660,6 +672,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0RDA,
|
||||
.name = "dla0rda",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -670,6 +684,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0FALRDB,
|
||||
.name = "dla0falrdb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -680,6 +696,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0WRA,
|
||||
.name = "dla0wra",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -690,6 +708,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0FALWRB,
|
||||
.name = "dla0falwrb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -699,7 +719,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1RDA,
|
||||
.name = "dla0rda",
|
||||
.name = "dla1rda",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -709,7 +731,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1FALRDB,
|
||||
.name = "dla0falrdb",
|
||||
.name = "dla1falrdb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -719,7 +743,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1WRA,
|
||||
.name = "dla0wra",
|
||||
.name = "dla1wra",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -729,7 +755,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1FALWRB,
|
||||
.name = "dla0falwrb",
|
||||
.name = "dla1falwrb",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -908,6 +936,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0RDA1,
|
||||
.name = "dla0rda1",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_NVDLA0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
@ -917,7 +947,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA1RDA1,
|
||||
.name = "dla0rda1",
|
||||
.name = "dla1rda1",
|
||||
.sid = TEGRA234_SID_NVDLA1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
|
@ -217,7 +217,6 @@ static struct rpmhpd *sa8540p_rpmhpds[] = {
|
||||
[SC8280XP_CX] = &cx,
|
||||
[SC8280XP_CX_AO] = &cx_ao,
|
||||
[SC8280XP_EBI] = &ebi,
|
||||
[SC8280XP_GFX] = &gfx,
|
||||
[SC8280XP_LCX] = &lcx,
|
||||
[SC8280XP_LMX] = &lmx,
|
||||
[SC8280XP_MMCX] = &mmcx,
|
||||
|
@ -68,4 +68,13 @@ config MTK_SVS
|
||||
chip process corner, temperatures and other factors. Then DVFS
|
||||
driver could apply SVS bank voltage to PMIC/Buck.
|
||||
|
||||
config MTK_SOCINFO
|
||||
tristate "MediaTek SoC Information"
|
||||
default y
|
||||
depends on NVMEM_MTK_EFUSE
|
||||
help
|
||||
The MediaTek SoC Information (mtk-socinfo) driver provides
|
||||
information about the SoC to the userspace including the
|
||||
manufacturer name, marketing name and soc name.
|
||||
|
||||
endmenu
|
||||
|
@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_REGULATOR_COUPLER) += mtk-regulator-coupler.o
|
||||
obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
|
||||
obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
|
||||
obj-$(CONFIG_MTK_SVS) += mtk-svs.o
|
||||
obj-$(CONFIG_MTK_SOCINFO) += mtk-socinfo.o
|
||||
|
191
drivers/soc/mediatek/mtk-socinfo.c
Normal file
191
drivers/soc/mediatek/mtk-socinfo.c
Normal file
@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 MediaTek Inc.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/device/bus.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define MTK_SOCINFO_ENTRY(_soc_name, _segment_name, _marketing_name, _cell_data1, _cell_data2) {\
|
||||
.soc_name = _soc_name, \
|
||||
.segment_name = _segment_name, \
|
||||
.marketing_name = _marketing_name, \
|
||||
.cell_data = {_cell_data1, _cell_data2} \
|
||||
}
|
||||
#define CELL_NOT_USED (0xFFFFFFFF)
|
||||
#define MAX_CELLS (2)
|
||||
|
||||
struct mtk_socinfo {
|
||||
struct device *dev;
|
||||
struct name_data *name_data;
|
||||
struct socinfo_data *socinfo_data;
|
||||
struct soc_device *soc_dev;
|
||||
};
|
||||
|
||||
struct socinfo_data {
|
||||
char *soc_name;
|
||||
char *segment_name;
|
||||
char *marketing_name;
|
||||
u32 cell_data[MAX_CELLS];
|
||||
};
|
||||
|
||||
static const char *cell_names[MAX_CELLS] = {"socinfo-data1", "socinfo-data2"};
|
||||
|
||||
static struct socinfo_data socinfo_data_table[] = {
|
||||
MTK_SOCINFO_ENTRY("MT8173", "MT8173V/AC", "MT8173", 0x6CA20004, 0x10000000),
|
||||
MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000840),
|
||||
MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000940),
|
||||
MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED),
|
||||
MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED),
|
||||
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 830", 0x81880000, 0x00000010),
|
||||
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 830", 0x81880000, 0x00000011),
|
||||
MTK_SOCINFO_ENTRY("MT8192", "MT8192V/AZA", "Kompanio 820", 0x00001100, 0x00040080),
|
||||
MTK_SOCINFO_ENTRY("MT8192T", "MT8192V/ATZA", "Kompanio 828", 0x00000100, 0x000400C0),
|
||||
MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EZA", "Kompanio 1200", 0x81950300, CELL_NOT_USED),
|
||||
MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EHZA", "Kompanio 1200", 0x81950304, CELL_NOT_USED),
|
||||
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
|
||||
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
|
||||
};
|
||||
|
||||
static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop)
|
||||
{
|
||||
struct soc_device_attribute *attrs;
|
||||
static char machine[30] = {0};
|
||||
static const char *soc_manufacturer = "MediaTek";
|
||||
|
||||
attrs = devm_kzalloc(mtk_socinfop->dev, sizeof(*attrs), GFP_KERNEL);
|
||||
if (!attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(machine, sizeof(machine), "%s (%s)", mtk_socinfop->socinfo_data->marketing_name,
|
||||
mtk_socinfop->socinfo_data->soc_name);
|
||||
attrs->family = soc_manufacturer;
|
||||
attrs->machine = machine;
|
||||
|
||||
mtk_socinfop->soc_dev = soc_device_register(attrs);
|
||||
if (IS_ERR(mtk_socinfop->soc_dev))
|
||||
return PTR_ERR(mtk_socinfop->soc_dev);
|
||||
|
||||
dev_info(mtk_socinfop->dev, "%s %s SoC detected.\n", soc_manufacturer, attrs->machine);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 mtk_socinfo_read_cell(struct device *dev, const char *name)
|
||||
{
|
||||
struct nvmem_device *nvmemp;
|
||||
struct device_node *np, *nvmem_node = dev->parent->of_node;
|
||||
u32 offset;
|
||||
u32 cell_val = CELL_NOT_USED;
|
||||
|
||||
/* should never fail since the nvmem driver registers this child */
|
||||
nvmemp = nvmem_device_find(nvmem_node, device_match_of_node);
|
||||
if (IS_ERR(nvmemp))
|
||||
goto out;
|
||||
|
||||
np = of_get_child_by_name(nvmem_node, name);
|
||||
if (!np)
|
||||
goto put_device;
|
||||
|
||||
if (of_property_read_u32_index(np, "reg", 0, &offset))
|
||||
goto put_node;
|
||||
|
||||
nvmem_device_read(nvmemp, offset, sizeof(cell_val), &cell_val);
|
||||
|
||||
put_node:
|
||||
of_node_put(np);
|
||||
put_device:
|
||||
nvmem_device_put(nvmemp);
|
||||
out:
|
||||
return cell_val;
|
||||
}
|
||||
|
||||
static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop)
|
||||
{
|
||||
unsigned int i, j;
|
||||
unsigned int num_cell_data = 0;
|
||||
u32 cell_data[MAX_CELLS] = {0};
|
||||
bool match_socinfo;
|
||||
int match_socinfo_index = -1;
|
||||
|
||||
for (i = 0; i < MAX_CELLS; i++) {
|
||||
cell_data[i] = mtk_socinfo_read_cell(mtk_socinfop->dev, cell_names[i]);
|
||||
if (cell_data[i] != CELL_NOT_USED)
|
||||
num_cell_data++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!num_cell_data)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(socinfo_data_table); i++) {
|
||||
match_socinfo = true;
|
||||
for (j = 0; j < num_cell_data; j++) {
|
||||
if (cell_data[j] != socinfo_data_table[i].cell_data[j]) {
|
||||
match_socinfo = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match_socinfo) {
|
||||
mtk_socinfop->socinfo_data = &(socinfo_data_table[i]);
|
||||
match_socinfo_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return match_socinfo_index >= 0 ? match_socinfo_index : -ENOENT;
|
||||
}
|
||||
|
||||
static int mtk_socinfo_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_socinfo *mtk_socinfop;
|
||||
int ret;
|
||||
|
||||
mtk_socinfop = devm_kzalloc(&pdev->dev, sizeof(*mtk_socinfop), GFP_KERNEL);
|
||||
if (!mtk_socinfop)
|
||||
return -ENOMEM;
|
||||
|
||||
mtk_socinfop->dev = &pdev->dev;
|
||||
|
||||
ret = mtk_socinfo_get_socinfo_data(mtk_socinfop);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(mtk_socinfop->dev, ret, "Failed to get socinfo data\n");
|
||||
|
||||
ret = mtk_socinfo_create_socinfo_node(mtk_socinfop);
|
||||
if (ret)
|
||||
return dev_err_probe(mtk_socinfop->dev, ret, "Cannot create node\n");
|
||||
|
||||
platform_set_drvdata(pdev, mtk_socinfop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_socinfo_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_socinfo *mtk_socinfop = platform_get_drvdata(pdev);
|
||||
|
||||
soc_device_unregister(mtk_socinfop->soc_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver mtk_socinfo = {
|
||||
.probe = mtk_socinfo_probe,
|
||||
.remove_new = mtk_socinfo_remove,
|
||||
.driver = {
|
||||
.name = "mtk-socinfo",
|
||||
},
|
||||
};
|
||||
module_platform_driver(mtk_socinfo);
|
||||
|
||||
MODULE_AUTHOR("William-TW LIN <william-tw.lin@mediatek.com>");
|
||||
MODULE_DESCRIPTION("MediaTek socinfo driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -268,4 +268,13 @@ config QCOM_INLINE_CRYPTO_ENGINE
|
||||
tristate
|
||||
select QCOM_SCM
|
||||
|
||||
config QCOM_PBS
|
||||
tristate "PBS trigger support for Qualcomm Technologies, Inc. PMICS"
|
||||
depends on SPMI
|
||||
help
|
||||
This driver supports configuring software programmable boot sequencer (PBS)
|
||||
trigger event through PBS RAM on Qualcomm Technologies, Inc. PMICs.
|
||||
This module provides the APIs to the client drivers that wants to send the
|
||||
PBS trigger event to the PBS RAM.
|
||||
|
||||
endmenu
|
||||
|
@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
CFLAGS_rpmh-rsc.o := -I$(src)
|
||||
CFLAGS_qcom_aoss.o := -I$(src)
|
||||
obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o
|
||||
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
|
||||
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
|
||||
@ -34,3 +35,4 @@ obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o
|
||||
obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o
|
||||
qcom_ice-objs += ice.o
|
||||
obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE) += qcom_ice.o
|
||||
obj-$(CONFIG_QCOM_PBS) += qcom-pbs.o
|
||||
|
@ -399,7 +399,7 @@ static int apr_uevent(const struct device *dev, struct kobj_uevent_env *env)
|
||||
return add_uevent_var(env, "MODALIAS=apr:%s", adev->name);
|
||||
}
|
||||
|
||||
struct bus_type aprbus = {
|
||||
const struct bus_type aprbus = {
|
||||
.name = "aprbus",
|
||||
.match = apr_device_match,
|
||||
.probe = apr_device_probe,
|
||||
|
@ -859,6 +859,8 @@ static int llcc_update_act_ctrl(u32 sid,
|
||||
ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
|
||||
slice_status, !(slice_status & status),
|
||||
0, LLCC_STATUS_READ_DELAY);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (drv_data->version >= LLCC_VERSION_4_1_0_0)
|
||||
ret = regmap_write(drv_data->bcast_regmap, act_clear_reg,
|
||||
|
@ -89,7 +89,6 @@
|
||||
* @base: Base address of this instance of QUP wrapper core
|
||||
* @clks: Handle to the primary & optional secondary AHB clocks
|
||||
* @num_clks: Count of clocks
|
||||
* @to_core: Core ICC path
|
||||
*/
|
||||
struct geni_wrapper {
|
||||
struct device *dev;
|
||||
|
236
drivers/soc/qcom/qcom-pbs.c
Normal file
236
drivers/soc/qcom/qcom-pbs.c
Normal file
@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spmi.h>
|
||||
#include <linux/soc/qcom/qcom-pbs.h>
|
||||
|
||||
#define PBS_CLIENT_TRIG_CTL 0x42
|
||||
#define PBS_CLIENT_SW_TRIG_BIT BIT(7)
|
||||
#define PBS_CLIENT_SCRATCH1 0x50
|
||||
#define PBS_CLIENT_SCRATCH2 0x51
|
||||
#define PBS_CLIENT_SCRATCH2_ERROR 0xFF
|
||||
|
||||
#define RETRIES 2000
|
||||
#define DELAY 1100
|
||||
|
||||
struct pbs_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mutex lock;
|
||||
struct device_link *link;
|
||||
|
||||
u32 base;
|
||||
};
|
||||
|
||||
static int qcom_pbs_wait_for_ack(struct pbs_dev *pbs, u8 bit_pos)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2,
|
||||
val, val & BIT(bit_pos), DELAY, DELAY * RETRIES);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(pbs->dev, "Timeout for PBS ACK/NACK for bit %u\n", bit_pos);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (val == PBS_CLIENT_SCRATCH2_ERROR) {
|
||||
ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0);
|
||||
dev_err(pbs->dev, "NACK from PBS for bit %u\n", bit_pos);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(pbs->dev, "PBS sequence for bit %u executed!\n", bit_pos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_pbs_trigger_event() - Trigger the PBS RAM sequence
|
||||
* @pbs: Pointer to PBS device
|
||||
* @bitmap: bitmap
|
||||
*
|
||||
* This function is used to trigger the PBS RAM sequence to be
|
||||
* executed by the client driver.
|
||||
*
|
||||
* The PBS trigger sequence involves
|
||||
* 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1
|
||||
* 2. Initiating the SW PBS trigger
|
||||
* 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the
|
||||
* completion of the sequence.
|
||||
* 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute
|
||||
*
|
||||
* Return: 0 on success, < 0 on failure
|
||||
*/
|
||||
int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap)
|
||||
{
|
||||
unsigned int val;
|
||||
u16 bit_pos;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!bitmap))
|
||||
return -EINVAL;
|
||||
|
||||
if (IS_ERR_OR_NULL(pbs))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pbs->lock);
|
||||
ret = regmap_read(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, &val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (val == PBS_CLIENT_SCRATCH2_ERROR) {
|
||||
/* PBS error - clear SCRATCH2 register */
|
||||
ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (bit_pos = 0; bit_pos < 8; bit_pos++) {
|
||||
if (!(bitmap & BIT(bit_pos)))
|
||||
continue;
|
||||
|
||||
/* Clear the PBS sequence bit position */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2,
|
||||
BIT(bit_pos), 0);
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
/* Set the PBS sequence bit position */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1,
|
||||
BIT(bit_pos), BIT(bit_pos));
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
/* Initiate the SW trigger */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_TRIG_CTL,
|
||||
PBS_CLIENT_SW_TRIG_BIT, PBS_CLIENT_SW_TRIG_BIT);
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
ret = qcom_pbs_wait_for_ack(pbs, bit_pos);
|
||||
if (ret < 0)
|
||||
goto out_clear_scratch1;
|
||||
|
||||
/* Clear the PBS sequence bit position */
|
||||
regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0);
|
||||
regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0);
|
||||
}
|
||||
|
||||
out_clear_scratch1:
|
||||
/* Clear all the requested bitmap */
|
||||
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, bitmap, 0);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pbs->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_pbs_trigger_event);
|
||||
|
||||
/**
|
||||
* get_pbs_client_device() - Get the PBS device used by client
|
||||
* @dev: Client device
|
||||
*
|
||||
* This function is used to get the PBS device that is being
|
||||
* used by the client.
|
||||
*
|
||||
* Return: pbs_dev on success, ERR_PTR on failure
|
||||
*/
|
||||
struct pbs_dev *get_pbs_client_device(struct device *dev)
|
||||
{
|
||||
struct device_node *pbs_dev_node;
|
||||
struct platform_device *pdev;
|
||||
struct pbs_dev *pbs;
|
||||
|
||||
pbs_dev_node = of_parse_phandle(dev->of_node, "qcom,pbs", 0);
|
||||
if (!pbs_dev_node) {
|
||||
dev_err(dev, "Missing qcom,pbs property\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(pbs_dev_node);
|
||||
if (!pdev) {
|
||||
dev_err(dev, "Unable to find PBS dev_node\n");
|
||||
pbs = ERR_PTR(-EPROBE_DEFER);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pbs = platform_get_drvdata(pdev);
|
||||
if (!pbs) {
|
||||
dev_err(dev, "Cannot get pbs instance from %s\n", dev_name(&pdev->dev));
|
||||
platform_device_put(pdev);
|
||||
pbs = ERR_PTR(-EPROBE_DEFER);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pbs->link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
|
||||
if (!pbs->link) {
|
||||
dev_err(&pdev->dev, "Failed to create device link to consumer %s\n", dev_name(dev));
|
||||
platform_device_put(pdev);
|
||||
pbs = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
of_node_put(pbs_dev_node);
|
||||
return pbs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_pbs_client_device);
|
||||
|
||||
static int qcom_pbs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pbs_dev *pbs;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL);
|
||||
if (!pbs)
|
||||
return -ENOMEM;
|
||||
|
||||
pbs->dev = &pdev->dev;
|
||||
pbs->regmap = dev_get_regmap(pbs->dev->parent, NULL);
|
||||
if (!pbs->regmap) {
|
||||
dev_err(pbs->dev, "Couldn't get parent's regmap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(pbs->dev, "reg", &val);
|
||||
if (ret < 0) {
|
||||
dev_err(pbs->dev, "Couldn't find reg, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
pbs->base = val;
|
||||
mutex_init(&pbs->lock);
|
||||
|
||||
platform_set_drvdata(pdev, pbs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_pbs_match_table[] = {
|
||||
{ .compatible = "qcom,pbs" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_pbs_match_table);
|
||||
|
||||
static struct platform_driver qcom_pbs_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-pbs",
|
||||
.of_match_table = qcom_pbs_match_table,
|
||||
},
|
||||
.probe = qcom_pbs_probe,
|
||||
};
|
||||
module_platform_driver(qcom_pbs_driver)
|
||||
|
||||
MODULE_DESCRIPTION("QCOM PBS DRIVER");
|
||||
MODULE_LICENSE("GPL");
|
@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2019, Linaro Ltd
|
||||
*/
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
@ -13,6 +14,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/qcom/qcom_aoss.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace-aoss.h"
|
||||
|
||||
#define QMP_DESC_MAGIC 0x0
|
||||
#define QMP_DESC_VERSION 0x4
|
||||
#define QMP_DESC_FEATURES 0x8
|
||||
@ -44,6 +48,8 @@
|
||||
|
||||
#define QMP_NUM_COOLING_RESOURCES 2
|
||||
|
||||
#define QMP_DEBUGFS_FILES 4
|
||||
|
||||
static bool qmp_cdev_max_state = 1;
|
||||
|
||||
struct qmp_cooling_device {
|
||||
@ -65,6 +71,8 @@ struct qmp_cooling_device {
|
||||
* @tx_lock: provides synchronization between multiple callers of qmp_send()
|
||||
* @qdss_clk: QDSS clock hw struct
|
||||
* @cooling_devs: thermal cooling devices
|
||||
* @debugfs_root: directory for the developer/tester interface
|
||||
* @debugfs_files: array of individual debugfs entries under debugfs_root
|
||||
*/
|
||||
struct qmp {
|
||||
void __iomem *msgram;
|
||||
@ -82,6 +90,8 @@ struct qmp {
|
||||
|
||||
struct clk_hw qdss_clk;
|
||||
struct qmp_cooling_device *cooling_devs;
|
||||
struct dentry *debugfs_root;
|
||||
struct dentry *debugfs_files[QMP_DEBUGFS_FILES];
|
||||
};
|
||||
|
||||
static void qmp_kick(struct qmp *qmp)
|
||||
@ -214,7 +224,7 @@ static bool qmp_message_empty(struct qmp *qmp)
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure
|
||||
*/
|
||||
int qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
int __printf(2, 3) qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
{
|
||||
char buf[QMP_MSG_LEN];
|
||||
long time_left;
|
||||
@ -235,6 +245,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
|
||||
mutex_lock(&qmp->tx_lock);
|
||||
|
||||
trace_aoss_send(buf);
|
||||
|
||||
/* The message RAM only implements 32-bit accesses */
|
||||
__iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32),
|
||||
buf, sizeof(buf) / sizeof(u32));
|
||||
@ -256,6 +268,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
trace_aoss_send_done(buf, ret);
|
||||
|
||||
mutex_unlock(&qmp->tx_lock);
|
||||
|
||||
return ret;
|
||||
@ -475,6 +489,91 @@ void qmp_put(struct qmp *qmp)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qmp_put);
|
||||
|
||||
struct qmp_debugfs_entry {
|
||||
const char *name;
|
||||
const char *fmt;
|
||||
bool is_bool;
|
||||
const char *true_val;
|
||||
const char *false_val;
|
||||
};
|
||||
|
||||
static const struct qmp_debugfs_entry qmp_debugfs_entries[QMP_DEBUGFS_FILES] = {
|
||||
{ "ddr_frequency_mhz", "{class: ddr, res: fixed, val: %u}", false },
|
||||
{ "prevent_aoss_sleep", "{class: aoss_slp, res: sleep: %s}", true, "enable", "disable" },
|
||||
{ "prevent_cx_collapse", "{class: cx_mol, res: cx, val: %s}", true, "mol", "off" },
|
||||
{ "prevent_ddr_collapse", "{class: ddr_mol, res: ddr, val: %s}", true, "mol", "off" },
|
||||
};
|
||||
|
||||
static ssize_t qmp_debugfs_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
const struct qmp_debugfs_entry *entry = NULL;
|
||||
struct qmp *qmp = file->private_data;
|
||||
char buf[QMP_MSG_LEN];
|
||||
unsigned int uint_val;
|
||||
const char *str_val;
|
||||
bool bool_val;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) {
|
||||
if (qmp->debugfs_files[i] == file->f_path.dentry) {
|
||||
entry = &qmp_debugfs_entries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (WARN_ON(!entry))
|
||||
return -EFAULT;
|
||||
|
||||
if (entry->is_bool) {
|
||||
ret = kstrtobool_from_user(user_buf, count, &bool_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
str_val = bool_val ? entry->true_val : entry->false_val;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf), entry->fmt, str_val);
|
||||
if (ret >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
ret = kstrtou32_from_user(user_buf, count, 0, &uint_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snprintf(buf, sizeof(buf), entry->fmt, uint_val);
|
||||
if (ret >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = qmp_send(qmp, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations qmp_debugfs_fops = {
|
||||
.open = simple_open,
|
||||
.write = qmp_debugfs_write,
|
||||
};
|
||||
|
||||
static void qmp_debugfs_create(struct qmp *qmp)
|
||||
{
|
||||
const struct qmp_debugfs_entry *entry;
|
||||
int i;
|
||||
|
||||
qmp->debugfs_root = debugfs_create_dir("qcom_aoss", NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) {
|
||||
entry = &qmp_debugfs_entries[i];
|
||||
|
||||
qmp->debugfs_files[i] = debugfs_create_file(entry->name, 0200,
|
||||
qmp->debugfs_root,
|
||||
qmp,
|
||||
&qmp_debugfs_fops);
|
||||
}
|
||||
}
|
||||
|
||||
static int qmp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qmp *qmp;
|
||||
@ -523,6 +622,8 @@ static int qmp_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, qmp);
|
||||
|
||||
qmp_debugfs_create(qmp);
|
||||
|
||||
return 0;
|
||||
|
||||
err_close_qmp:
|
||||
@ -537,6 +638,8 @@ static void qmp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qmp *qmp = platform_get_drvdata(pdev);
|
||||
|
||||
debugfs_remove_recursive(qmp->debugfs_root);
|
||||
|
||||
qmp_qdss_clk_remove(qmp);
|
||||
qmp_cooling_devices_remove(qmp);
|
||||
|
||||
|
@ -655,8 +655,6 @@ invalid_canary:
|
||||
void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
|
||||
{
|
||||
struct smem_partition *part;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
void *ptr = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
if (!__smem)
|
||||
@ -665,12 +663,6 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
|
||||
if (WARN_ON(item >= __smem->item_count))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
|
||||
HWSPINLOCK_TIMEOUT,
|
||||
&flags);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
|
||||
part = &__smem->partitions[host];
|
||||
ptr = qcom_smem_get_private(__smem, part, item, size);
|
||||
@ -681,10 +673,7 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
|
||||
ptr = qcom_smem_get_global(__smem, item, size);
|
||||
}
|
||||
|
||||
hwspin_unlock_irqrestore(__smem->hwlock, &flags);
|
||||
|
||||
return ptr;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_smem_get);
|
||||
|
||||
|
@ -58,8 +58,8 @@
|
||||
* @valid_entries: number of allocated entries
|
||||
* @flags:
|
||||
* @entries: individual communication entries
|
||||
* @name: name of the entry
|
||||
* @value: content of the entry
|
||||
* @entries.name: name of the entry
|
||||
* @entries.value: content of the entry
|
||||
*/
|
||||
struct smp2p_smem_item {
|
||||
u32 magic;
|
||||
@ -275,6 +275,8 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
|
||||
*
|
||||
* Handle notifications from the remote side to handle newly allocated entries
|
||||
* or any changes to the state bits of existing entries.
|
||||
*
|
||||
* Return: %IRQ_HANDLED
|
||||
*/
|
||||
static irqreturn_t qcom_smp2p_intr(int irq, void *data)
|
||||
{
|
||||
|
@ -124,7 +124,7 @@ static const char *const pmic_models[] = {
|
||||
[50] = "PM8350B",
|
||||
[51] = "PMR735A",
|
||||
[52] = "PMR735B",
|
||||
[55] = "PM2250",
|
||||
[55] = "PM4125",
|
||||
[58] = "PM8450",
|
||||
[65] = "PM8010",
|
||||
[69] = "PM8550VS",
|
||||
@ -424,8 +424,11 @@ static const struct soc_id soc_id[] = {
|
||||
{ qcom_board_id(IPQ9510) },
|
||||
{ qcom_board_id(QRB4210) },
|
||||
{ qcom_board_id(QRB2210) },
|
||||
{ qcom_board_id(SM8475) },
|
||||
{ qcom_board_id(SM8475P) },
|
||||
{ qcom_board_id(SA8775P) },
|
||||
{ qcom_board_id(QRU1000) },
|
||||
{ qcom_board_id(SM8475_2) },
|
||||
{ qcom_board_id(QDU1000) },
|
||||
{ qcom_board_id(SM8650) },
|
||||
{ qcom_board_id(SM4450) },
|
||||
@ -437,6 +440,8 @@ static const struct soc_id soc_id[] = {
|
||||
{ qcom_board_id(IPQ5322) },
|
||||
{ qcom_board_id(IPQ5312) },
|
||||
{ qcom_board_id(IPQ5302) },
|
||||
{ qcom_board_id(QCS8550) },
|
||||
{ qcom_board_id(QCM8550) },
|
||||
{ qcom_board_id(IPQ5300) },
|
||||
};
|
||||
|
||||
|
@ -6,20 +6,40 @@
|
||||
* SAW power controller driver
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/linear_range.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
#include <soc/qcom/spm.h>
|
||||
|
||||
#define FIELD_SET(current, mask, val) \
|
||||
(((current) & ~(mask)) | FIELD_PREP((mask), (val)))
|
||||
|
||||
#define SPM_CTL_INDEX 0x7f
|
||||
#define SPM_CTL_INDEX_SHIFT 4
|
||||
#define SPM_CTL_EN BIT(0)
|
||||
|
||||
/* These registers might be specific to SPM 1.1 */
|
||||
#define SPM_VCTL_VLVL GENMASK(7, 0)
|
||||
#define SPM_PMIC_DATA_0_VLVL GENMASK(7, 0)
|
||||
#define SPM_PMIC_DATA_1_MIN_VSEL GENMASK(5, 0)
|
||||
#define SPM_PMIC_DATA_1_MAX_VSEL GENMASK(21, 16)
|
||||
|
||||
#define SPM_1_1_AVS_CTL_AVS_ENABLED BIT(27)
|
||||
#define SPM_AVS_CTL_MAX_VLVL GENMASK(22, 17)
|
||||
#define SPM_AVS_CTL_MIN_VLVL GENMASK(15, 10)
|
||||
|
||||
enum spm_reg {
|
||||
SPM_REG_CFG,
|
||||
SPM_REG_SPM_CTL,
|
||||
@ -29,13 +49,44 @@ enum spm_reg {
|
||||
SPM_REG_PMIC_DATA_1,
|
||||
SPM_REG_VCTL,
|
||||
SPM_REG_SEQ_ENTRY,
|
||||
SPM_REG_SPM_STS,
|
||||
SPM_REG_STS0,
|
||||
SPM_REG_STS1,
|
||||
SPM_REG_PMIC_STS,
|
||||
SPM_REG_AVS_CTL,
|
||||
SPM_REG_AVS_LIMIT,
|
||||
SPM_REG_RST,
|
||||
SPM_REG_NR,
|
||||
};
|
||||
|
||||
#define MAX_PMIC_DATA 2
|
||||
#define MAX_SEQ_DATA 64
|
||||
|
||||
struct spm_reg_data {
|
||||
const u16 *reg_offset;
|
||||
u32 spm_cfg;
|
||||
u32 spm_dly;
|
||||
u32 pmic_dly;
|
||||
u32 pmic_data[MAX_PMIC_DATA];
|
||||
u32 avs_ctl;
|
||||
u32 avs_limit;
|
||||
u8 seq[MAX_SEQ_DATA];
|
||||
u8 start_index[PM_SLEEP_MODE_NR];
|
||||
|
||||
smp_call_func_t set_vdd;
|
||||
/* for now we support only a single range */
|
||||
struct linear_range *range;
|
||||
unsigned int ramp_delay;
|
||||
unsigned int init_uV;
|
||||
};
|
||||
|
||||
struct spm_driver_data {
|
||||
void __iomem *reg_base;
|
||||
const struct spm_reg_data *reg_data;
|
||||
struct device *dev;
|
||||
unsigned int volt_sel;
|
||||
int reg_cpu;
|
||||
};
|
||||
|
||||
static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = {
|
||||
[SPM_REG_AVS_CTL] = 0x904,
|
||||
[SPM_REG_AVS_LIMIT] = 0x908,
|
||||
@ -169,6 +220,10 @@ static const struct spm_reg_data spm_reg_8226_cpu = {
|
||||
|
||||
static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
|
||||
[SPM_REG_CFG] = 0x08,
|
||||
[SPM_REG_STS0] = 0x0c,
|
||||
[SPM_REG_STS1] = 0x10,
|
||||
[SPM_REG_VCTL] = 0x14,
|
||||
[SPM_REG_AVS_CTL] = 0x18,
|
||||
[SPM_REG_SPM_CTL] = 0x20,
|
||||
[SPM_REG_PMIC_DLY] = 0x24,
|
||||
[SPM_REG_PMIC_DATA_0] = 0x28,
|
||||
@ -176,7 +231,12 @@ static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
|
||||
[SPM_REG_SEQ_ENTRY] = 0x80,
|
||||
};
|
||||
|
||||
static void smp_set_vdd_v1_1(void *data);
|
||||
|
||||
/* SPM register data for 8064 */
|
||||
static struct linear_range spm_v1_1_regulator_range =
|
||||
REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500);
|
||||
|
||||
static const struct spm_reg_data spm_reg_8064_cpu = {
|
||||
.reg_offset = spm_reg_offset_v1_1,
|
||||
.spm_cfg = 0x1F,
|
||||
@ -187,6 +247,10 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
|
||||
0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
|
||||
.start_index[PM_SLEEP_MODE_STBY] = 0,
|
||||
.start_index[PM_SLEEP_MODE_SPC] = 2,
|
||||
.set_vdd = smp_set_vdd_v1_1,
|
||||
.range = &spm_v1_1_regulator_range,
|
||||
.init_uV = 1300000,
|
||||
.ramp_delay = 1250,
|
||||
};
|
||||
|
||||
static inline void spm_register_write(struct spm_driver_data *drv,
|
||||
@ -238,6 +302,178 @@ void spm_set_low_power_mode(struct spm_driver_data *drv,
|
||||
spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
|
||||
}
|
||||
|
||||
static int spm_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector)
|
||||
{
|
||||
struct spm_driver_data *drv = rdev_get_drvdata(rdev);
|
||||
|
||||
drv->volt_sel = selector;
|
||||
|
||||
/* Always do the SAW register writes on the corresponding CPU */
|
||||
return smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true);
|
||||
}
|
||||
|
||||
static int spm_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
struct spm_driver_data *drv = rdev_get_drvdata(rdev);
|
||||
|
||||
return drv->volt_sel;
|
||||
}
|
||||
|
||||
static const struct regulator_ops spm_reg_ops = {
|
||||
.set_voltage_sel = spm_set_voltage_sel,
|
||||
.get_voltage_sel = spm_get_voltage_sel,
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
||||
};
|
||||
|
||||
static void smp_set_vdd_v1_1(void *data)
|
||||
{
|
||||
struct spm_driver_data *drv = data;
|
||||
unsigned int vctl, data0, data1, avs_ctl, sts;
|
||||
unsigned int vlevel, volt_sel;
|
||||
bool avs_enabled;
|
||||
|
||||
volt_sel = drv->volt_sel;
|
||||
vlevel = volt_sel | 0x80; /* band */
|
||||
|
||||
avs_ctl = spm_register_read(drv, SPM_REG_AVS_CTL);
|
||||
vctl = spm_register_read(drv, SPM_REG_VCTL);
|
||||
data0 = spm_register_read(drv, SPM_REG_PMIC_DATA_0);
|
||||
data1 = spm_register_read(drv, SPM_REG_PMIC_DATA_1);
|
||||
|
||||
avs_enabled = avs_ctl & SPM_1_1_AVS_CTL_AVS_ENABLED;
|
||||
|
||||
/* If AVS is enabled, switch it off during the voltage change */
|
||||
if (avs_enabled) {
|
||||
avs_ctl &= ~SPM_1_1_AVS_CTL_AVS_ENABLED;
|
||||
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
|
||||
}
|
||||
|
||||
/* Kick the state machine back to idle */
|
||||
spm_register_write(drv, SPM_REG_RST, 1);
|
||||
|
||||
vctl = FIELD_SET(vctl, SPM_VCTL_VLVL, vlevel);
|
||||
data0 = FIELD_SET(data0, SPM_PMIC_DATA_0_VLVL, vlevel);
|
||||
data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MIN_VSEL, volt_sel);
|
||||
data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MAX_VSEL, volt_sel);
|
||||
|
||||
spm_register_write(drv, SPM_REG_VCTL, vctl);
|
||||
spm_register_write(drv, SPM_REG_PMIC_DATA_0, data0);
|
||||
spm_register_write(drv, SPM_REG_PMIC_DATA_1, data1);
|
||||
|
||||
if (read_poll_timeout_atomic(spm_register_read,
|
||||
sts, sts == vlevel,
|
||||
1, 200, false,
|
||||
drv, SPM_REG_STS1)) {
|
||||
dev_err_ratelimited(drv->dev, "timeout setting the voltage (%x %x)!\n", sts, vlevel);
|
||||
goto enable_avs;
|
||||
}
|
||||
|
||||
if (avs_enabled) {
|
||||
unsigned int max_avs = volt_sel;
|
||||
unsigned int min_avs = max(max_avs, 4U) - 4;
|
||||
|
||||
avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MIN_VLVL, min_avs);
|
||||
avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MAX_VLVL, max_avs);
|
||||
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
|
||||
}
|
||||
|
||||
enable_avs:
|
||||
if (avs_enabled) {
|
||||
avs_ctl |= SPM_1_1_AVS_CTL_AVS_ENABLED;
|
||||
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
|
||||
}
|
||||
}
|
||||
|
||||
static int spm_get_cpu(struct device *dev)
|
||||
{
|
||||
int cpu;
|
||||
bool found;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct device_node *cpu_node, *saw_node;
|
||||
|
||||
cpu_node = of_cpu_device_node_get(cpu);
|
||||
if (!cpu_node)
|
||||
continue;
|
||||
|
||||
saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
|
||||
found = (saw_node == dev->of_node);
|
||||
of_node_put(saw_node);
|
||||
of_node_put(cpu_node);
|
||||
|
||||
if (found)
|
||||
return cpu;
|
||||
}
|
||||
|
||||
/* L2 SPM is not bound to any CPU, voltage setting is not supported */
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv)
|
||||
{
|
||||
struct regulator_config config = {
|
||||
.dev = dev,
|
||||
.driver_data = drv,
|
||||
};
|
||||
struct regulator_desc *rdesc;
|
||||
struct regulator_dev *rdev;
|
||||
int ret;
|
||||
bool found;
|
||||
|
||||
if (!drv->reg_data->set_vdd)
|
||||
return 0;
|
||||
|
||||
rdesc = devm_kzalloc(dev, sizeof(*rdesc), GFP_KERNEL);
|
||||
if (!rdesc)
|
||||
return -ENOMEM;
|
||||
|
||||
rdesc->name = "spm";
|
||||
rdesc->of_match = of_match_ptr("regulator");
|
||||
rdesc->type = REGULATOR_VOLTAGE;
|
||||
rdesc->owner = THIS_MODULE;
|
||||
rdesc->ops = &spm_reg_ops;
|
||||
|
||||
rdesc->linear_ranges = drv->reg_data->range;
|
||||
rdesc->n_linear_ranges = 1;
|
||||
rdesc->n_voltages = rdesc->linear_ranges[rdesc->n_linear_ranges - 1].max_sel + 1;
|
||||
rdesc->ramp_delay = drv->reg_data->ramp_delay;
|
||||
|
||||
ret = spm_get_cpu(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drv->reg_cpu = ret;
|
||||
dev_dbg(dev, "SAW2 bound to CPU %d\n", drv->reg_cpu);
|
||||
|
||||
/*
|
||||
* Program initial voltage, otherwise registration will also try
|
||||
* setting the voltage, which might result in undervolting the CPU.
|
||||
*/
|
||||
drv->volt_sel = DIV_ROUND_UP(drv->reg_data->init_uV - rdesc->min_uV,
|
||||
rdesc->uV_step);
|
||||
ret = linear_range_get_selector_high(drv->reg_data->range,
|
||||
drv->reg_data->init_uV,
|
||||
&drv->volt_sel,
|
||||
&found);
|
||||
if (ret) {
|
||||
dev_err(dev, "Initial uV value out of bounds\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Always do the SAW register writes on the corresponding CPU */
|
||||
smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true);
|
||||
|
||||
rdev = devm_regulator_register(dev, rdesc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(dev, "failed to register regulator\n");
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id spm_match_table[] = {
|
||||
{ .compatible = "qcom,sdm660-gold-saw2-v4.1-l2",
|
||||
.data = &spm_reg_660_gold_l2 },
|
||||
@ -288,6 +524,7 @@ static int spm_dev_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
|
||||
drv->reg_data = match_id->data;
|
||||
drv->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, drv);
|
||||
|
||||
/* Write the SPM sequences first.. */
|
||||
@ -315,6 +552,9 @@ static int spm_dev_probe(struct platform_device *pdev)
|
||||
if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL])
|
||||
spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
|
||||
|
||||
if (IS_ENABLED(CONFIG_REGULATOR))
|
||||
return spm_register_regulator(&pdev->dev, drv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
48
drivers/soc/qcom/trace-aoss.h
Normal file
48
drivers/soc/qcom/trace-aoss.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM qcom_aoss
|
||||
|
||||
#if !defined(_TRACE_QCOM_AOSS_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_QCOM_AOSS_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(aoss_send,
|
||||
TP_PROTO(const char *msg),
|
||||
TP_ARGS(msg),
|
||||
TP_STRUCT__entry(
|
||||
__string(msg, msg)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(msg, msg);
|
||||
),
|
||||
TP_printk("%s", __get_str(msg))
|
||||
);
|
||||
|
||||
TRACE_EVENT(aoss_send_done,
|
||||
TP_PROTO(const char *msg, int ret),
|
||||
TP_ARGS(msg, ret),
|
||||
TP_STRUCT__entry(
|
||||
__string(msg, msg)
|
||||
__field(int, ret)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(msg, msg);
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk("%s: %d", __get_str(msg), __entry->ret)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_QCOM_AOSS_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace-aoss
|
||||
|
||||
#include <trace/define_trace.h>
|
@ -34,6 +34,10 @@ config ARCH_RCAR_GEN3
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select SYS_SUPPORTS_SH_TMU
|
||||
|
||||
config ARCH_RCAR_GEN4
|
||||
bool
|
||||
select ARCH_RCAR_GEN3
|
||||
|
||||
config ARCH_RMOBILE
|
||||
bool
|
||||
select PM
|
||||
@ -240,7 +244,7 @@ config ARCH_R8A77961
|
||||
|
||||
config ARCH_R8A779F0
|
||||
bool "ARM64 Platform support for R-Car S4-8"
|
||||
select ARCH_RCAR_GEN3
|
||||
select ARCH_RCAR_GEN4
|
||||
select SYSC_R8A779F0
|
||||
help
|
||||
This enables support for the Renesas R-Car S4-8 SoC.
|
||||
@ -261,18 +265,25 @@ config ARCH_R8A77970
|
||||
|
||||
config ARCH_R8A779A0
|
||||
bool "ARM64 Platform support for R-Car V3U"
|
||||
select ARCH_RCAR_GEN3
|
||||
select ARCH_RCAR_GEN4
|
||||
select SYSC_R8A779A0
|
||||
help
|
||||
This enables support for the Renesas R-Car V3U SoC.
|
||||
|
||||
config ARCH_R8A779G0
|
||||
bool "ARM64 Platform support for R-Car V4H"
|
||||
select ARCH_RCAR_GEN3
|
||||
select ARCH_RCAR_GEN4
|
||||
select SYSC_R8A779G0
|
||||
help
|
||||
This enables support for the Renesas R-Car V4H SoC.
|
||||
|
||||
config ARCH_R8A779H0
|
||||
bool "ARM64 Platform support for R-Car V4M"
|
||||
select ARCH_RCAR_GEN4
|
||||
select SYSC_R8A779H0
|
||||
help
|
||||
This enables support for the Renesas R-Car V4M SoC.
|
||||
|
||||
config ARCH_R8A774C0
|
||||
bool "ARM64 Platform support for RZ/G2E"
|
||||
select ARCH_RCAR_GEN3
|
||||
|
@ -117,6 +117,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
|
||||
{ .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u },
|
||||
{ .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 },
|
||||
{ .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 },
|
||||
{ .compatible = "renesas,r8a779h0-rst", .data = &rcar_rst_gen4 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
@ -270,6 +270,11 @@ static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = {
|
||||
.id = 0x5c,
|
||||
};
|
||||
|
||||
static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = {
|
||||
.family = &fam_rcar_gen4,
|
||||
.id = 0x5d,
|
||||
};
|
||||
|
||||
static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = {
|
||||
.family = &fam_shmobile,
|
||||
.id = 0x37,
|
||||
@ -380,6 +385,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = {
|
||||
#ifdef CONFIG_ARCH_R8A779G0
|
||||
{ .compatible = "renesas,r8a779g0", .data = &soc_rcar_v4h },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_R8A779H0
|
||||
{ .compatible = "renesas,r8a779h0", .data = &soc_rcar_v4m },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_R9A07G043
|
||||
#ifdef CONFIG_RISCV
|
||||
{ .compatible = "renesas,r9a07g043", .data = &soc_rz_five },
|
||||
|
@ -42,6 +42,7 @@ config EXYNOS_PMU
|
||||
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
|
||||
select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS
|
||||
select MFD_CORE
|
||||
select REGMAP_MMIO
|
||||
|
||||
# There is no need to enable these drivers for ARMv8
|
||||
config EXYNOS_PMU_ARM_DRIVERS
|
||||
|
@ -5,6 +5,7 @@
|
||||
//
|
||||
// Exynos - CPU PMU(Power Management Unit) support
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/mfd/core.h>
|
||||
@ -12,19 +13,134 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/soc/samsung/exynos-regs-pmu.h>
|
||||
#include <linux/soc/samsung/exynos-pmu.h>
|
||||
|
||||
#include "exynos-pmu.h"
|
||||
|
||||
#define PMUALIVE_MASK GENMASK(13, 0)
|
||||
#define TENSOR_SET_BITS (BIT(15) | BIT(14))
|
||||
#define TENSOR_CLR_BITS BIT(15)
|
||||
#define TENSOR_SMC_PMU_SEC_REG 0x82000504
|
||||
#define TENSOR_PMUREG_READ 0
|
||||
#define TENSOR_PMUREG_WRITE 1
|
||||
#define TENSOR_PMUREG_RMW 2
|
||||
|
||||
struct exynos_pmu_context {
|
||||
struct device *dev;
|
||||
const struct exynos_pmu_data *pmu_data;
|
||||
struct regmap *pmureg;
|
||||
};
|
||||
|
||||
void __iomem *pmu_base_addr;
|
||||
static struct exynos_pmu_context *pmu_context;
|
||||
/* forward declaration */
|
||||
static struct platform_driver exynos_pmu_driver;
|
||||
|
||||
/*
|
||||
* Tensor SoCs are configured so that PMU_ALIVE registers can only be written
|
||||
* from EL3, but are still read accessible. As Linux needs to write some of
|
||||
* these registers, the following functions are provided and exposed via
|
||||
* regmap.
|
||||
*
|
||||
* Note: This SMC interface is known to be implemented on gs101 and derivative
|
||||
* SoCs.
|
||||
*/
|
||||
|
||||
/* Write to a protected PMU register. */
|
||||
static int tensor_sec_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
unsigned long pmu_base = (unsigned long)context;
|
||||
|
||||
arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg,
|
||||
TENSOR_PMUREG_WRITE, val, 0, 0, 0, 0, &res);
|
||||
|
||||
/* returns -EINVAL if access isn't allowed or 0 */
|
||||
if (res.a0)
|
||||
pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0);
|
||||
|
||||
return (int)res.a0;
|
||||
}
|
||||
|
||||
/* Read/Modify/Write a protected PMU register. */
|
||||
static int tensor_sec_reg_rmw(void *context, unsigned int reg,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
unsigned long pmu_base = (unsigned long)context;
|
||||
|
||||
arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg,
|
||||
TENSOR_PMUREG_RMW, mask, val, 0, 0, 0, &res);
|
||||
|
||||
/* returns -EINVAL if access isn't allowed or 0 */
|
||||
if (res.a0)
|
||||
pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0);
|
||||
|
||||
return (int)res.a0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a protected PMU register. All PMU registers can be read by Linux.
|
||||
* Note: The SMC read register is not used, as only registers that can be
|
||||
* written are readable via SMC.
|
||||
*/
|
||||
static int tensor_sec_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
*val = pmu_raw_readl(reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For SoCs that have set/clear bit hardware this function can be used when
|
||||
* the PMU register will be accessed by multiple masters.
|
||||
*
|
||||
* For example, to set bits 13:8 in PMU reg offset 0x3e80
|
||||
* tensor_set_bits_atomic(ctx, 0x3e80, 0x3f00, 0x3f00);
|
||||
*
|
||||
* Set bit 8, and clear bits 13:9 PMU reg offset 0x3e80
|
||||
* tensor_set_bits_atomic(0x3e80, 0x100, 0x3f00);
|
||||
*/
|
||||
static int tensor_set_bits_atomic(void *ctx, unsigned int offset, u32 val,
|
||||
u32 mask)
|
||||
{
|
||||
int ret;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!(mask & BIT(i)))
|
||||
continue;
|
||||
|
||||
offset &= ~TENSOR_SET_BITS;
|
||||
|
||||
if (val & BIT(i))
|
||||
offset |= TENSOR_SET_BITS;
|
||||
else
|
||||
offset |= TENSOR_CLR_BITS;
|
||||
|
||||
ret = tensor_sec_reg_write(ctx, offset, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tensor_sec_update_bits(void *ctx, unsigned int reg,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
/*
|
||||
* Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF)
|
||||
* as the target registers can be accessed by multiple masters.
|
||||
*/
|
||||
if (reg > PMUALIVE_MASK)
|
||||
return tensor_sec_reg_rmw(ctx, reg, mask, val);
|
||||
|
||||
return tensor_set_bits_atomic(ctx, reg, val, mask);
|
||||
}
|
||||
|
||||
void pmu_raw_writel(u32 val, u32 offset)
|
||||
{
|
||||
@ -75,11 +191,41 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
|
||||
#define exynos_pmu_data_arm_ptr(data) NULL
|
||||
#endif
|
||||
|
||||
static const struct regmap_config regmap_smccfg = {
|
||||
.name = "pmu_regs",
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
.reg_read = tensor_sec_reg_read,
|
||||
.reg_write = tensor_sec_reg_write,
|
||||
.reg_update_bits = tensor_sec_update_bits,
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_mmiocfg = {
|
||||
.name = "pmu_regs",
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
static const struct exynos_pmu_data gs101_pmu_data = {
|
||||
.pmu_secure = true
|
||||
};
|
||||
|
||||
/*
|
||||
* PMU platform driver and devicetree bindings.
|
||||
*/
|
||||
static const struct of_device_id exynos_pmu_of_device_ids[] = {
|
||||
{
|
||||
.compatible = "google,gs101-pmu",
|
||||
.data = &gs101_pmu_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos3250-pmu",
|
||||
.data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data),
|
||||
}, {
|
||||
@ -113,19 +259,75 @@ static const struct mfd_cell exynos_pmu_devs[] = {
|
||||
{ .name = "exynos-clkout", },
|
||||
};
|
||||
|
||||
/**
|
||||
* exynos_get_pmu_regmap() - Obtain pmureg regmap
|
||||
*
|
||||
* Find the pmureg regmap previously configured in probe() and return regmap
|
||||
* pointer.
|
||||
*
|
||||
* Return: A pointer to regmap if found or ERR_PTR error value.
|
||||
*/
|
||||
struct regmap *exynos_get_pmu_regmap(void)
|
||||
{
|
||||
struct device_node *np = of_find_matching_node(NULL,
|
||||
exynos_pmu_of_device_ids);
|
||||
if (np)
|
||||
return syscon_node_to_regmap(np);
|
||||
return exynos_get_pmu_regmap_by_phandle(np, NULL);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap);
|
||||
|
||||
/**
|
||||
* exynos_get_pmu_regmap_by_phandle() - Obtain pmureg regmap via phandle
|
||||
* @np: Device node holding PMU phandle property
|
||||
* @propname: Name of property holding phandle value
|
||||
*
|
||||
* Find the pmureg regmap previously configured in probe() and return regmap
|
||||
* pointer.
|
||||
*
|
||||
* Return: A pointer to regmap if found or ERR_PTR error value.
|
||||
*/
|
||||
struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
|
||||
const char *propname)
|
||||
{
|
||||
struct exynos_pmu_context *ctx;
|
||||
struct device_node *pmu_np;
|
||||
struct device *dev;
|
||||
|
||||
if (propname)
|
||||
pmu_np = of_parse_phandle(np, propname, 0);
|
||||
else
|
||||
pmu_np = np;
|
||||
|
||||
if (!pmu_np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
/*
|
||||
* Determine if exynos-pmu device has probed and therefore regmap
|
||||
* has been created and can be returned to the caller. Otherwise we
|
||||
* return -EPROBE_DEFER.
|
||||
*/
|
||||
dev = driver_find_device_by_of_node(&exynos_pmu_driver.driver,
|
||||
(void *)pmu_np);
|
||||
|
||||
if (propname)
|
||||
of_node_put(pmu_np);
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
ctx = dev_get_drvdata(dev);
|
||||
|
||||
return ctx->pmureg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle);
|
||||
|
||||
static int exynos_pmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regmap_config pmu_regmcfg;
|
||||
struct regmap *regmap;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
pmu_base_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
@ -137,9 +339,38 @@ static int exynos_pmu_probe(struct platform_device *pdev)
|
||||
GFP_KERNEL);
|
||||
if (!pmu_context)
|
||||
return -ENOMEM;
|
||||
pmu_context->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
pmu_context->pmu_data = of_device_get_match_data(dev);
|
||||
|
||||
/* For SoCs that secure PMU register writes use custom regmap */
|
||||
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_secure) {
|
||||
pmu_regmcfg = regmap_smccfg;
|
||||
pmu_regmcfg.max_register = resource_size(res) -
|
||||
pmu_regmcfg.reg_stride;
|
||||
/* Need physical address for SMC call */
|
||||
regmap = devm_regmap_init(dev, NULL,
|
||||
(void *)(uintptr_t)res->start,
|
||||
&pmu_regmcfg);
|
||||
} else {
|
||||
/* All other SoCs use a MMIO regmap */
|
||||
pmu_regmcfg = regmap_mmiocfg;
|
||||
pmu_regmcfg.max_register = resource_size(res) -
|
||||
pmu_regmcfg.reg_stride;
|
||||
regmap = devm_regmap_init_mmio(dev, pmu_base_addr,
|
||||
&pmu_regmcfg);
|
||||
}
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
|
||||
"regmap init failed\n");
|
||||
|
||||
pmu_context->pmureg = regmap;
|
||||
pmu_context->dev = dev;
|
||||
|
||||
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init)
|
||||
pmu_context->pmu_data->pmu_init();
|
||||
|
||||
|
@ -21,6 +21,7 @@ struct exynos_pmu_conf {
|
||||
struct exynos_pmu_data {
|
||||
const struct exynos_pmu_conf *pmu_config;
|
||||
const struct exynos_pmu_conf *pmu_config_extra;
|
||||
bool pmu_secure;
|
||||
|
||||
void (*pmu_init)(void);
|
||||
void (*powerdown_conf)(enum sys_powerdown);
|
||||
|
@ -133,6 +133,11 @@ config ARCH_TEGRA_234_SOC
|
||||
help
|
||||
Enable support for the NVIDIA Tegra234 SoC.
|
||||
|
||||
config ARCH_TEGRA_241_SOC
|
||||
bool "NVIDIA Tegra241 SoC"
|
||||
help
|
||||
Enable support for the NVIDIA Tegra241 SoC.
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -3,11 +3,13 @@
|
||||
* Copyright (c) 2013-2023, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/of.h>
|
||||
@ -113,6 +115,28 @@ static void tegra_fuse_restore(void *base)
|
||||
fuse->clk = NULL;
|
||||
}
|
||||
|
||||
static void tegra_fuse_print_sku_info(struct tegra_sku_info *tegra_sku_info)
|
||||
{
|
||||
pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
|
||||
tegra_revision_name[tegra_sku_info->revision],
|
||||
tegra_sku_info->sku_id, tegra_sku_info->cpu_process_id,
|
||||
tegra_sku_info->soc_process_id);
|
||||
pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
|
||||
tegra_sku_info->cpu_speedo_id, tegra_sku_info->soc_speedo_id);
|
||||
}
|
||||
|
||||
static int tegra_fuse_add_lookups(struct tegra_fuse *fuse)
|
||||
{
|
||||
fuse->lookups = kmemdup_array(fuse->soc->lookups, sizeof(*fuse->lookups),
|
||||
fuse->soc->num_lookups, GFP_KERNEL);
|
||||
if (!fuse->lookups)
|
||||
return -ENOMEM;
|
||||
|
||||
nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_fuse_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base = fuse->base;
|
||||
@ -130,15 +154,46 @@ static int tegra_fuse_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(fuse->base);
|
||||
fuse->phys = res->start;
|
||||
|
||||
fuse->clk = devm_clk_get(&pdev->dev, "fuse");
|
||||
if (IS_ERR(fuse->clk)) {
|
||||
if (PTR_ERR(fuse->clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
|
||||
PTR_ERR(fuse->clk));
|
||||
/* Initialize the soc data and lookups if using ACPI boot. */
|
||||
if (is_acpi_node(dev_fwnode(&pdev->dev)) && !fuse->soc) {
|
||||
u8 chip;
|
||||
|
||||
return PTR_ERR(fuse->clk);
|
||||
tegra_acpi_init_apbmisc();
|
||||
|
||||
chip = tegra_get_chip_id();
|
||||
switch (chip) {
|
||||
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
case TEGRA194:
|
||||
fuse->soc = &tegra194_fuse_soc;
|
||||
break;
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
case TEGRA234:
|
||||
fuse->soc = &tegra234_fuse_soc;
|
||||
break;
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_241_SOC)
|
||||
case TEGRA241:
|
||||
fuse->soc = &tegra241_fuse_soc;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return dev_err_probe(&pdev->dev, -EINVAL, "Unsupported SoC: %02x\n", chip);
|
||||
}
|
||||
|
||||
fuse->soc->init(fuse);
|
||||
tegra_fuse_print_sku_info(&tegra_sku_info);
|
||||
tegra_soc_device_register();
|
||||
|
||||
err = tegra_fuse_add_lookups(fuse);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err, "failed to add FUSE lookups\n");
|
||||
}
|
||||
|
||||
fuse->clk = devm_clk_get_optional(&pdev->dev, "fuse");
|
||||
if (IS_ERR(fuse->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(fuse->clk), "failed to get FUSE clock\n");
|
||||
|
||||
platform_set_drvdata(pdev, fuse);
|
||||
fuse->dev = &pdev->dev;
|
||||
|
||||
@ -179,12 +234,8 @@ static int tegra_fuse_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse");
|
||||
if (IS_ERR(fuse->rst)) {
|
||||
err = PTR_ERR(fuse->rst);
|
||||
dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n",
|
||||
fuse->rst);
|
||||
return err;
|
||||
}
|
||||
if (IS_ERR(fuse->rst))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(fuse->rst), "failed to get FUSE reset\n");
|
||||
|
||||
/*
|
||||
* FUSE clock is enabled at a boot time, hence this resume/suspend
|
||||
@ -262,10 +313,17 @@ static const struct dev_pm_ops tegra_fuse_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_fuse_suspend, tegra_fuse_resume)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id tegra_fuse_acpi_match[] = {
|
||||
{ "NVDA200F" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, tegra_fuse_acpi_match);
|
||||
|
||||
static struct platform_driver tegra_fuse_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-fuse",
|
||||
.of_match_table = tegra_fuse_match,
|
||||
.acpi_match_table = tegra_fuse_acpi_match,
|
||||
.pm = &tegra_fuse_pm,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
@ -287,7 +345,16 @@ u32 __init tegra_fuse_read_early(unsigned int offset)
|
||||
|
||||
int tegra_fuse_readl(unsigned long offset, u32 *value)
|
||||
{
|
||||
if (!fuse->read || !fuse->clk)
|
||||
if (!fuse->dev)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/*
|
||||
* Wait for fuse->clk to be initialized if device-tree boot is used.
|
||||
*/
|
||||
if (is_of_node(dev_fwnode(fuse->dev)) && !fuse->clk)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!fuse->read)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (IS_ERR(fuse->clk))
|
||||
@ -343,7 +410,8 @@ const struct attribute_group tegra_soc_attr_group = {
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
|
||||
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \
|
||||
IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC)
|
||||
static ssize_t platform_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -370,7 +438,7 @@ const struct attribute_group tegra194_soc_attr_group = {
|
||||
};
|
||||
#endif
|
||||
|
||||
struct device * __init tegra_soc_device_register(void)
|
||||
struct device *tegra_soc_device_register(void)
|
||||
{
|
||||
struct soc_device_attribute *attr;
|
||||
struct soc_device *dev;
|
||||
@ -407,6 +475,7 @@ static int __init tegra_init_fuse(void)
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np;
|
||||
struct resource regs;
|
||||
int err;
|
||||
|
||||
tegra_init_apbmisc();
|
||||
|
||||
@ -497,22 +566,13 @@ static int __init tegra_init_fuse(void)
|
||||
|
||||
fuse->soc->init(fuse);
|
||||
|
||||
pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
|
||||
tegra_revision_name[tegra_sku_info.revision],
|
||||
tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
|
||||
tegra_sku_info.soc_process_id);
|
||||
pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
|
||||
tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
|
||||
tegra_fuse_print_sku_info(&tegra_sku_info);
|
||||
|
||||
if (fuse->soc->lookups) {
|
||||
size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups;
|
||||
err = tegra_fuse_add_lookups(fuse);
|
||||
if (err)
|
||||
pr_err("failed to add FUSE lookups\n");
|
||||
|
||||
fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL);
|
||||
if (fuse->lookups)
|
||||
nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
early_initcall(tegra_init_fuse);
|
||||
|
||||
|
@ -38,7 +38,8 @@
|
||||
defined(CONFIG_ARCH_TEGRA_210_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_186_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_194_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
defined(CONFIG_ARCH_TEGRA_234_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_241_SOC)
|
||||
static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset)
|
||||
{
|
||||
if (WARN_ON(!fuse->base))
|
||||
@ -678,3 +679,23 @@ const struct tegra_fuse_soc tegra234_fuse_soc = {
|
||||
.clk_suspend_on = false,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_TEGRA_241_SOC)
|
||||
static const struct tegra_fuse_info tegra241_fuse_info = {
|
||||
.read = tegra30_fuse_read,
|
||||
.size = 0x16008,
|
||||
.spare = 0xcf0,
|
||||
};
|
||||
|
||||
static const struct nvmem_keepout tegra241_fuse_keepouts[] = {
|
||||
{ .start = 0xc, .end = 0x1600c }
|
||||
};
|
||||
|
||||
const struct tegra_fuse_soc tegra241_fuse_soc = {
|
||||
.init = tegra30_fuse_init,
|
||||
.info = &tegra241_fuse_info,
|
||||
.keepouts = tegra241_fuse_keepouts,
|
||||
.num_keepouts = ARRAY_SIZE(tegra241_fuse_keepouts),
|
||||
.soc_attr_group = &tegra194_soc_attr_group,
|
||||
};
|
||||
#endif
|
||||
|
@ -69,6 +69,7 @@ struct tegra_fuse {
|
||||
|
||||
void tegra_init_revision(void);
|
||||
void tegra_init_apbmisc(void);
|
||||
void tegra_acpi_init_apbmisc(void);
|
||||
|
||||
u32 __init tegra_fuse_read_spare(unsigned int spare);
|
||||
u32 __init tegra_fuse_read_early(unsigned int offset);
|
||||
@ -123,7 +124,8 @@ extern const struct tegra_fuse_soc tegra186_fuse_soc;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
|
||||
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \
|
||||
IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC)
|
||||
extern const struct attribute_group tegra194_soc_attr_group;
|
||||
#endif
|
||||
|
||||
@ -135,4 +137,8 @@ extern const struct tegra_fuse_soc tegra194_fuse_soc;
|
||||
extern const struct tegra_fuse_soc tegra234_fuse_soc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_241_SOC
|
||||
extern const struct tegra_fuse_soc tegra241_fuse_soc;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -3,9 +3,11 @@
|
||||
* Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
@ -62,6 +64,7 @@ bool tegra_is_silicon(void)
|
||||
switch (tegra_get_chip_id()) {
|
||||
case TEGRA194:
|
||||
case TEGRA234:
|
||||
case TEGRA241:
|
||||
case TEGRA264:
|
||||
if (tegra_get_platform() == 0)
|
||||
return true;
|
||||
@ -160,9 +163,34 @@ void __init tegra_init_revision(void)
|
||||
tegra_sku_info.platform = tegra_get_platform();
|
||||
}
|
||||
|
||||
void __init tegra_init_apbmisc(void)
|
||||
static void tegra_init_apbmisc_resources(struct resource *apbmisc,
|
||||
struct resource *straps)
|
||||
{
|
||||
void __iomem *strapping_base;
|
||||
|
||||
apbmisc_base = ioremap(apbmisc->start, resource_size(apbmisc));
|
||||
if (apbmisc_base)
|
||||
chipid = readl_relaxed(apbmisc_base + 4);
|
||||
else
|
||||
pr_err("failed to map APBMISC registers\n");
|
||||
|
||||
strapping_base = ioremap(straps->start, resource_size(straps));
|
||||
if (strapping_base) {
|
||||
strapping = readl_relaxed(strapping_base);
|
||||
iounmap(strapping_base);
|
||||
} else {
|
||||
pr_err("failed to map strapping options registers\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_init_apbmisc - Initializes Tegra APBMISC and Strapping registers.
|
||||
*
|
||||
* This is called during early init as some of the old 32-bit ARM code needs
|
||||
* information from the APBMISC registers very early during boot.
|
||||
*/
|
||||
void __init tegra_init_apbmisc(void)
|
||||
{
|
||||
struct resource apbmisc, straps;
|
||||
struct device_node *np;
|
||||
|
||||
@ -219,23 +247,73 @@ void __init tegra_init_apbmisc(void)
|
||||
}
|
||||
}
|
||||
|
||||
apbmisc_base = ioremap(apbmisc.start, resource_size(&apbmisc));
|
||||
if (!apbmisc_base) {
|
||||
pr_err("failed to map APBMISC registers\n");
|
||||
} else {
|
||||
chipid = readl_relaxed(apbmisc_base + 4);
|
||||
}
|
||||
|
||||
strapping_base = ioremap(straps.start, resource_size(&straps));
|
||||
if (!strapping_base) {
|
||||
pr_err("failed to map strapping options registers\n");
|
||||
} else {
|
||||
strapping = readl_relaxed(strapping_base);
|
||||
iounmap(strapping_base);
|
||||
}
|
||||
|
||||
tegra_init_apbmisc_resources(&apbmisc, &straps);
|
||||
long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code");
|
||||
|
||||
put:
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id apbmisc_acpi_match[] = {
|
||||
{ "NVDA2010" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
void tegra_acpi_init_apbmisc(void)
|
||||
{
|
||||
struct resource *resources[2] = { NULL };
|
||||
struct resource_entry *rentry;
|
||||
struct acpi_device *adev = NULL;
|
||||
struct list_head resource_list;
|
||||
int rcount = 0;
|
||||
int ret;
|
||||
|
||||
adev = acpi_dev_get_first_match_dev(apbmisc_acpi_match[0].id, NULL, -1);
|
||||
if (!adev)
|
||||
return;
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
|
||||
ret = acpi_dev_get_memory_resources(adev, &resource_list);
|
||||
if (ret < 0) {
|
||||
pr_err("failed to get APBMISC memory resources");
|
||||
goto out_put_acpi_dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get required memory resources.
|
||||
*
|
||||
* resources[0]: apbmisc.
|
||||
* resources[1]: straps.
|
||||
*/
|
||||
resource_list_for_each_entry(rentry, &resource_list) {
|
||||
if (rcount >= ARRAY_SIZE(resources))
|
||||
break;
|
||||
|
||||
resources[rcount++] = rentry->res;
|
||||
}
|
||||
|
||||
if (!resources[0]) {
|
||||
pr_err("failed to get APBMISC registers\n");
|
||||
goto out_free_resource_list;
|
||||
}
|
||||
|
||||
if (!resources[1]) {
|
||||
pr_err("failed to get strapping options registers\n");
|
||||
goto out_free_resource_list;
|
||||
}
|
||||
|
||||
tegra_init_apbmisc_resources(resources[0], resources[1]);
|
||||
|
||||
out_free_resource_list:
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
out_put_acpi_dev:
|
||||
acpi_dev_put(adev);
|
||||
}
|
||||
#else
|
||||
void tegra_acpi_init_apbmisc(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -3,7 +3,7 @@
|
||||
* drivers/soc/tegra/pmc.c
|
||||
*
|
||||
* Copyright (c) 2010 Google, Inc
|
||||
* Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
@ -384,6 +384,7 @@ struct tegra_pmc_soc {
|
||||
bool has_blink_output;
|
||||
bool has_usb_sleepwalk;
|
||||
bool supports_core_domain;
|
||||
bool has_single_mmio_aperture;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1777,30 +1778,6 @@ static int tegra_io_pad_get_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id)
|
||||
return TEGRA_IO_PAD_VOLTAGE_3V3;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_io_rail_power_on() - enable power to I/O rail
|
||||
* @id: Tegra I/O pad ID for which to enable power
|
||||
*
|
||||
* See also: tegra_io_pad_power_enable()
|
||||
*/
|
||||
int tegra_io_rail_power_on(unsigned int id)
|
||||
{
|
||||
return tegra_io_pad_power_enable(id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_io_rail_power_on);
|
||||
|
||||
/**
|
||||
* tegra_io_rail_power_off() - disable power to I/O rail
|
||||
* @id: Tegra I/O pad ID for which to disable power
|
||||
*
|
||||
* See also: tegra_io_pad_power_disable()
|
||||
*/
|
||||
int tegra_io_rail_power_off(unsigned int id)
|
||||
{
|
||||
return tegra_io_pad_power_disable(id);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_io_rail_power_off);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
|
||||
{
|
||||
@ -2909,31 +2886,33 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake");
|
||||
if (res) {
|
||||
if (pmc->soc->has_single_mmio_aperture) {
|
||||
pmc->wake = base;
|
||||
pmc->aotag = base;
|
||||
pmc->scratch = base;
|
||||
} else {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"wake");
|
||||
pmc->wake = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pmc->wake))
|
||||
return PTR_ERR(pmc->wake);
|
||||
} else {
|
||||
pmc->wake = base;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag");
|
||||
if (res) {
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"aotag");
|
||||
pmc->aotag = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pmc->aotag))
|
||||
return PTR_ERR(pmc->aotag);
|
||||
} else {
|
||||
pmc->aotag = base;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch");
|
||||
if (res) {
|
||||
pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pmc->scratch))
|
||||
return PTR_ERR(pmc->scratch);
|
||||
} else {
|
||||
pmc->scratch = base;
|
||||
/* "scratch" is an optional aperture */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"scratch");
|
||||
if (res) {
|
||||
pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pmc->scratch))
|
||||
return PTR_ERR(pmc->scratch);
|
||||
} else {
|
||||
pmc->scratch = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pmc->clk = devm_clk_get_optional(&pdev->dev, "pclk");
|
||||
@ -2945,12 +2924,15 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
||||
* PMC should be last resort for restarting since it soft-resets
|
||||
* CPU without resetting everything else.
|
||||
*/
|
||||
err = devm_register_reboot_notifier(&pdev->dev,
|
||||
&tegra_pmc_reboot_notifier);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
|
||||
err);
|
||||
return err;
|
||||
if (pmc->scratch) {
|
||||
err = devm_register_reboot_notifier(&pdev->dev,
|
||||
&tegra_pmc_reboot_notifier);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to register reboot notifier, %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = devm_register_sys_off_handler(&pdev->dev,
|
||||
@ -3324,6 +3306,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
|
||||
.num_pmc_clks = 0,
|
||||
.has_blink_output = true,
|
||||
.has_usb_sleepwalk = true,
|
||||
.has_single_mmio_aperture = true,
|
||||
};
|
||||
|
||||
static const char * const tegra30_powergates[] = {
|
||||
@ -3385,6 +3368,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
|
||||
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
|
||||
.has_blink_output = true,
|
||||
.has_usb_sleepwalk = true,
|
||||
.has_single_mmio_aperture = true,
|
||||
};
|
||||
|
||||
static const char * const tegra114_powergates[] = {
|
||||
@ -3442,6 +3426,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
|
||||
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
|
||||
.has_blink_output = true,
|
||||
.has_usb_sleepwalk = true,
|
||||
.has_single_mmio_aperture = true,
|
||||
};
|
||||
|
||||
static const char * const tegra124_powergates[] = {
|
||||
@ -3586,6 +3571,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
|
||||
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
|
||||
.has_blink_output = true,
|
||||
.has_usb_sleepwalk = true,
|
||||
.has_single_mmio_aperture = true,
|
||||
};
|
||||
|
||||
static const char * const tegra210_powergates[] = {
|
||||
@ -3749,6 +3735,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
|
||||
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
|
||||
.has_blink_output = true,
|
||||
.has_usb_sleepwalk = true,
|
||||
.has_single_mmio_aperture = true,
|
||||
};
|
||||
|
||||
static const struct tegra_io_pad_soc tegra186_io_pads[] = {
|
||||
@ -3946,6 +3933,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
|
||||
.num_pmc_clks = 0,
|
||||
.has_blink_output = false,
|
||||
.has_usb_sleepwalk = false,
|
||||
.has_single_mmio_aperture = false,
|
||||
};
|
||||
|
||||
static const struct tegra_io_pad_soc tegra194_io_pads[] = {
|
||||
@ -4131,6 +4119,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
|
||||
.num_pmc_clks = 0,
|
||||
.has_blink_output = false,
|
||||
.has_usb_sleepwalk = false,
|
||||
.has_single_mmio_aperture = false,
|
||||
};
|
||||
|
||||
static const struct tegra_io_pad_soc tegra234_io_pads[] = {
|
||||
@ -4220,6 +4209,7 @@ static const char * const tegra234_reset_sources[] = {
|
||||
};
|
||||
|
||||
static const struct tegra_wake_event tegra234_wake_events[] = {
|
||||
TEGRA_WAKE_GPIO("sd-wake", 8, 0, TEGRA234_MAIN_GPIO(G, 7)),
|
||||
TEGRA_WAKE_IRQ("pmu", 24, 209),
|
||||
TEGRA_WAKE_GPIO("power", 29, 1, TEGRA234_AON_GPIO(EE, 4)),
|
||||
TEGRA_WAKE_GPIO("mgbe", 56, 0, TEGRA234_MAIN_GPIO(Y, 3)),
|
||||
@ -4259,6 +4249,7 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = {
|
||||
.pmc_clks_data = NULL,
|
||||
.num_pmc_clks = 0,
|
||||
.has_blink_output = false,
|
||||
.has_single_mmio_aperture = false,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_pmc_match[] = {
|
||||
|
@ -48,7 +48,7 @@ Example of usage:
|
||||
-----------------
|
||||
|
||||
This example places the bridge on top of the i.MX WEIM parallel bus, see:
|
||||
Documentation/devicetree/bindings/bus/imx-weim.txt
|
||||
Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml
|
||||
|
||||
&weim {
|
||||
controller@0,0 {
|
||||
|
@ -1226,7 +1226,7 @@ static int tee_client_device_uevent(const struct device *dev,
|
||||
return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id);
|
||||
}
|
||||
|
||||
struct bus_type tee_bus_type = {
|
||||
const struct bus_type tee_bus_type = {
|
||||
.name = "tee",
|
||||
.match = tee_client_device_match,
|
||||
.uevent = tee_client_device_uevent,
|
||||
|
@ -512,7 +512,6 @@ config S3C2410_WATCHDOG
|
||||
tristate "S3C6410/S5Pv210/Exynos Watchdog"
|
||||
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
select MFD_SYSCON if ARCH_EXYNOS
|
||||
help
|
||||
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
|
||||
SoCs. This will reboot the system when the timer expires with
|
||||
|
@ -24,9 +24,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/soc/samsung/exynos-pmu.h>
|
||||
|
||||
#define S3C2410_WTCON 0x00
|
||||
#define S3C2410_WTDAT 0x04
|
||||
@ -699,11 +699,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
|
||||
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"samsung,syscon-phandle");
|
||||
wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
|
||||
"samsung,syscon-phandle");
|
||||
if (IS_ERR(wdt->pmureg))
|
||||
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
|
||||
"syscon regmap lookup failed.\n");
|
||||
"PMU regmap lookup failed.\n");
|
||||
}
|
||||
|
||||
wdt_irq = platform_get_irq(pdev, 0);
|
||||
|
@ -252,8 +252,11 @@
|
||||
#define QCOM_ID_IPQ9510 521
|
||||
#define QCOM_ID_QRB4210 523
|
||||
#define QCOM_ID_QRB2210 524
|
||||
#define QCOM_ID_SM8475 530
|
||||
#define QCOM_ID_SM8475P 531
|
||||
#define QCOM_ID_SA8775P 534
|
||||
#define QCOM_ID_QRU1000 539
|
||||
#define QCOM_ID_SM8475_2 540
|
||||
#define QCOM_ID_QDU1000 545
|
||||
#define QCOM_ID_SM8650 557
|
||||
#define QCOM_ID_SM4450 568
|
||||
@ -265,6 +268,8 @@
|
||||
#define QCOM_ID_IPQ5322 593
|
||||
#define QCOM_ID_IPQ5312 594
|
||||
#define QCOM_ID_IPQ5302 595
|
||||
#define QCOM_ID_QCS8550 603
|
||||
#define QCOM_ID_QCM8550 604
|
||||
#define QCOM_ID_IPQ5300 624
|
||||
|
||||
/*
|
||||
|
@ -209,7 +209,7 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev) { return false; }
|
||||
#define module_ffa_driver(__ffa_driver) \
|
||||
module_driver(__ffa_driver, ffa_register, ffa_unregister)
|
||||
|
||||
extern struct bus_type ffa_bus_type;
|
||||
extern const struct bus_type ffa_bus_type;
|
||||
|
||||
/* FFA transport related */
|
||||
struct ffa_partition_info {
|
||||
|
@ -47,6 +47,10 @@ struct scmi_clock_info {
|
||||
bool rate_discrete;
|
||||
bool rate_changed_notifications;
|
||||
bool rate_change_requested_notifications;
|
||||
bool state_ctrl_forbidden;
|
||||
bool rate_ctrl_forbidden;
|
||||
bool parent_ctrl_forbidden;
|
||||
bool extended_config;
|
||||
union {
|
||||
struct {
|
||||
int num_rates;
|
||||
@ -72,6 +76,13 @@ struct scmi_handle;
|
||||
struct scmi_device;
|
||||
struct scmi_protocol_handle;
|
||||
|
||||
enum scmi_clock_oem_config {
|
||||
SCMI_CLOCK_CFG_DUTY_CYCLE = 0x1,
|
||||
SCMI_CLOCK_CFG_PHASE,
|
||||
SCMI_CLOCK_CFG_OEM_START = 0x80,
|
||||
SCMI_CLOCK_CFG_OEM_END = 0xFF,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_clk_proto_ops - represents the various operations provided
|
||||
* by SCMI Clock Protocol
|
||||
@ -104,10 +115,11 @@ struct scmi_clk_proto_ops {
|
||||
int (*state_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
bool *enabled, bool atomic);
|
||||
int (*config_oem_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u8 oem_type, u32 *oem_val, u32 *attributes,
|
||||
bool atomic);
|
||||
enum scmi_clock_oem_config oem_type,
|
||||
u32 *oem_val, u32 *attributes, bool atomic);
|
||||
int (*config_oem_set)(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u8 oem_type, u32 oem_val, bool atomic);
|
||||
enum scmi_clock_oem_config oem_type,
|
||||
u32 oem_val, bool atomic);
|
||||
int (*parent_get)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 *parent_id);
|
||||
int (*parent_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 parent_id);
|
||||
};
|
||||
@ -953,6 +965,8 @@ struct scmi_perf_limits_report {
|
||||
unsigned int domain_id;
|
||||
unsigned int range_max;
|
||||
unsigned int range_min;
|
||||
unsigned long range_max_freq;
|
||||
unsigned long range_min_freq;
|
||||
};
|
||||
|
||||
struct scmi_perf_level_report {
|
||||
@ -960,6 +974,7 @@ struct scmi_perf_level_report {
|
||||
unsigned int agent_id;
|
||||
unsigned int domain_id;
|
||||
unsigned int performance_level;
|
||||
unsigned long performance_level_freq;
|
||||
};
|
||||
|
||||
struct scmi_sensor_trip_point_report {
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <dt-bindings/soc/qcom,apr.h>
|
||||
#include <dt-bindings/soc/qcom,gpr.h>
|
||||
|
||||
extern struct bus_type aprbus;
|
||||
extern const struct bus_type aprbus;
|
||||
|
||||
#define APR_HDR_LEN(hdr_len) ((hdr_len)/4)
|
||||
|
||||
|
30
include/linux/soc/qcom/qcom-pbs.h
Normal file
30
include/linux/soc/qcom/qcom-pbs.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _QCOM_PBS_H
|
||||
#define _QCOM_PBS_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device_node;
|
||||
struct pbs_dev;
|
||||
|
||||
#if IS_ENABLED(CONFIG_QCOM_PBS)
|
||||
int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap);
|
||||
struct pbs_dev *get_pbs_client_device(struct device *client_dev);
|
||||
#else
|
||||
static inline int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline struct pbs_dev *get_pbs_client_device(struct device *client_dev)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -10,6 +10,7 @@
|
||||
#define __LINUX_SOC_EXYNOS_PMU_H
|
||||
|
||||
struct regmap;
|
||||
struct device_node;
|
||||
|
||||
enum sys_powerdown {
|
||||
SYS_AFTR,
|
||||
@ -20,12 +21,20 @@ enum sys_powerdown {
|
||||
|
||||
extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
|
||||
#ifdef CONFIG_EXYNOS_PMU
|
||||
extern struct regmap *exynos_get_pmu_regmap(void);
|
||||
struct regmap *exynos_get_pmu_regmap(void);
|
||||
struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
|
||||
const char *propname);
|
||||
#else
|
||||
static inline struct regmap *exynos_get_pmu_regmap(void)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
|
||||
const char *propname)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_SOC_EXYNOS_PMU_H */
|
||||
|
@ -217,6 +217,7 @@ extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
|
||||
extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
|
||||
extern void *kvmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
|
||||
extern char *kmemdup_nul(const char *s, size_t len, gfp_t gfp);
|
||||
extern void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp);
|
||||
|
||||
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
|
||||
extern void argv_free(char **argv);
|
||||
|
@ -482,7 +482,7 @@ static inline bool tee_param_is_memref(struct tee_param *param)
|
||||
}
|
||||
}
|
||||
|
||||
extern struct bus_type tee_bus_type;
|
||||
extern const struct bus_type tee_bus_type;
|
||||
|
||||
/**
|
||||
* struct tee_client_device - tee based device
|
||||
|
@ -49,7 +49,7 @@
|
||||
#define PMK8350_SUBTYPE 0x2f
|
||||
#define PMR735B_SUBTYPE 0x34
|
||||
#define PM6350_SUBTYPE 0x36
|
||||
#define PM2250_SUBTYPE 0x37
|
||||
#define PM4125_SUBTYPE 0x37
|
||||
|
||||
#define PMI8998_FAB_ID_SMIC 0x11
|
||||
#define PMI8998_FAB_ID_GF 0x30
|
||||
|
@ -7,11 +7,6 @@
|
||||
#ifndef __SPM_H__
|
||||
#define __SPM_H__
|
||||
|
||||
#include <linux/cpuidle.h>
|
||||
|
||||
#define MAX_PMIC_DATA 2
|
||||
#define MAX_SEQ_DATA 64
|
||||
|
||||
enum pm_sleep_mode {
|
||||
PM_SLEEP_MODE_STBY,
|
||||
PM_SLEEP_MODE_RET,
|
||||
@ -20,23 +15,7 @@ enum pm_sleep_mode {
|
||||
PM_SLEEP_MODE_NR,
|
||||
};
|
||||
|
||||
struct spm_reg_data {
|
||||
const u16 *reg_offset;
|
||||
u32 spm_cfg;
|
||||
u32 spm_dly;
|
||||
u32 pmic_dly;
|
||||
u32 pmic_data[MAX_PMIC_DATA];
|
||||
u32 avs_ctl;
|
||||
u32 avs_limit;
|
||||
u8 seq[MAX_SEQ_DATA];
|
||||
u8 start_index[PM_SLEEP_MODE_NR];
|
||||
};
|
||||
|
||||
struct spm_driver_data {
|
||||
void __iomem *reg_base;
|
||||
const struct spm_reg_data *reg_data;
|
||||
};
|
||||
|
||||
struct spm_driver_data;
|
||||
void spm_set_low_power_mode(struct spm_driver_data *drv,
|
||||
enum pm_sleep_mode mode);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#define TEGRA186 0x18
|
||||
#define TEGRA194 0x19
|
||||
#define TEGRA234 0x23
|
||||
#define TEGRA241 0x24
|
||||
#define TEGRA264 0x26
|
||||
|
||||
#define TEGRA_FUSE_SKU_CALIB_0 0xf0
|
||||
|
@ -148,10 +148,6 @@ enum tegra_io_pad {
|
||||
TEGRA_IO_PAD_AO_HV,
|
||||
};
|
||||
|
||||
/* deprecated, use TEGRA_IO_PAD_{HDMI,LVDS} instead */
|
||||
#define TEGRA_IO_RAIL_HDMI TEGRA_IO_PAD_HDMI
|
||||
#define TEGRA_IO_RAIL_LVDS TEGRA_IO_PAD_LVDS
|
||||
|
||||
#ifdef CONFIG_SOC_TEGRA_PMC
|
||||
int tegra_powergate_power_on(unsigned int id);
|
||||
int tegra_powergate_power_off(unsigned int id);
|
||||
@ -164,10 +160,6 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
|
||||
int tegra_io_pad_power_enable(enum tegra_io_pad id);
|
||||
int tegra_io_pad_power_disable(enum tegra_io_pad id);
|
||||
|
||||
/* deprecated, use tegra_io_pad_power_{enable,disable}() instead */
|
||||
int tegra_io_rail_power_on(unsigned int id);
|
||||
int tegra_io_rail_power_off(unsigned int id);
|
||||
|
||||
void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode);
|
||||
void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode);
|
||||
|
||||
@ -211,16 +203,6 @@ static inline int tegra_io_pad_get_voltage(enum tegra_io_pad id)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int tegra_io_rail_power_on(unsigned int id)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int tegra_io_rail_power_off(unsigned int id)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
|
||||
{
|
||||
}
|
||||
|
17
mm/util.c
17
mm/util.c
@ -135,6 +135,23 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp)
|
||||
}
|
||||
EXPORT_SYMBOL(kmemdup);
|
||||
|
||||
/**
|
||||
* kmemdup_array - duplicate a given array.
|
||||
*
|
||||
* @src: array to duplicate.
|
||||
* @element_size: size of each element of array.
|
||||
* @count: number of elements to duplicate from array.
|
||||
* @gfp: GFP mask to use.
|
||||
*
|
||||
* Return: duplicated array of @src or %NULL in case of error,
|
||||
* result is physically contiguous. Use kfree() to free.
|
||||
*/
|
||||
void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp)
|
||||
{
|
||||
return kmemdup(src, size_mul(element_size, count), gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(kmemdup_array);
|
||||
|
||||
/**
|
||||
* kvmemdup - duplicate region of memory
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user