forked from Minki/linux
ARM: SoC driver updates for v5.9
A couple of subsystems have their own subsystem maintainers but choose to have the code merged through the soc tree as upstream, as the code tends to be used across multiple SoCs or has SoC specific drivers itself: - memory controllers: Krzysztof Kozlowski takes ownership of the drivers/memory subsystem and its drivers, starting out with a set of cleanup patches. A larger driver for the Tegra memory controller that was accidentally missed for v5.8 is now added. - reset controllers: Only minor updates to drivers/reset this time - firmware: The "turris mox" firmware driver gains support for signed firmware blobs The tegra firmware driver gets extended to export some debug information Various updates to i.MX firmware drivers, mostly cosmetic - ARM SCMI/SCPI: A new mechanism for platform notifications is added, among a number of minor changes. - optee: Probing of the TEE bus is rewritten to better support detection of devices that depend on the tee-supplicant user space. A new firmware based trusted platform module (fTPM) driver is added based on OP-TEE - SoC attributes: A new driver is added to provide a generic soc_device for identifying a machine through the SMCCC ARCH_SOC_ID firmware interface rather than by probing SoC family specific registers. The series also contains some cleanups to the common soc_device code. There are also a number of updates to SoC specific drivers, the main ones are: - Mediatek cmdq driver gains a few in-kernel interfaces - Minor updates to Qualcomm RPMh, socinfo, rpm drivers, mostly adding support for additional SoC variants - The Qualcomm GENI core code gains interconnect path voting and performance level support, and integrating this into a number of device drivers. - A new driver for Samsung Exynos5800 voltage coupler for - Renesas RZ/G2H (R8A774E1) SoC support gets added to a couple of SoC specific device drivers - Updates to the TI K3 Ring Accelerator driver -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAl8j3y4ACgkQmmx57+YA GNm8Iw//euEC37KaiBDhlK3mcAL7NOdITqZpq4m+ZJZBsF02NDMWktJR8bYuOgmp kjR4LjCxa2i+UOq1Ln+zYSlS27AngZLHFM+YSG3jqDho12GYIe4OBZB/q/hkDu71 L5jCPNrZV9+GIcean2u8LOWDNlQ4SZQyZ1/gcCK7y7I8W1pVulmJRhtJ0MNkezni gDQ+OH+6+6XY8AethWK9ubsYH7SeJX/U6I8t5KJGhPr6FlaJFZOO5RTdUkBFMHpS i4UaT4meuqZUjwz4BhjvoYul5AT6Zc8OOTQwk1FM7dIe47aI8VkWrWci/IekxoLh UXtKbAJxerCIdehfiygX4pKtOmRKSisS2ocWsKg46Htu11ltv0XMRgyLyGv4Vm84 g+fKfKUL0SUueDqr+jKEq2aZdyLxwV5ZUoFt3IVsXdHRkZtxpN8jmOHOjV6erLVY m7S85U5eclNdK5Ap7RSVvQa4NP3NTUvJd1IDNIneUVyACRkxzWEKmE3ZuEO4qttS WSDW74m5ja80pltv1umFbGAsOUTZWA+WGULeXPv4CIooaD8RL6Jzs+7tkZEEhleU WlGBFE4eJi/ChMeyTKXPvEqsQncLSf0mGzM4/DVY6XRSTIrW+cuj1/Gsso1BJdod aZZ76uMNHJdAt0PcxL47lDUDxhJDkTwBsfGNJseZ3sYlAQ7Wmqo= =nezz -----END PGP SIGNATURE----- Merge tag 'arm-drivers-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull ARM SoC driver updates from Arnd Bergmann: "A couple of subsystems have their own subsystem maintainers but choose to have the code merged through the soc tree as upstream, as the code tends to be used across multiple SoCs or has SoC specific drivers itself: - memory controllers: Krzysztof Kozlowski takes ownership of the drivers/memory subsystem and its drivers, starting out with a set of cleanup patches. A larger driver for the Tegra memory controller that was accidentally missed for v5.8 is now added. - reset controllers: Only minor updates to drivers/reset this time - firmware: The "turris mox" firmware driver gains support for signed firmware blobs The tegra firmware driver gets extended to export some debug information Various updates to i.MX firmware drivers, mostly cosmetic - ARM SCMI/SCPI: A new mechanism for platform notifications is added, among a number of minor changes. - optee: Probing of the TEE bus is rewritten to better support detection of devices that depend on the tee-supplicant user space. A new firmware based trusted platform module (fTPM) driver is added based on OP-TEE - SoC attributes: A new driver is added to provide a generic soc_device for identifying a machine through the SMCCC ARCH_SOC_ID firmware interface rather than by probing SoC family specific registers. The series also contains some cleanups to the common soc_device code. There are also a number of updates to SoC specific drivers, the main ones are: - Mediatek cmdq driver gains a few in-kernel interfaces - Minor updates to Qualcomm RPMh, socinfo, rpm drivers, mostly adding support for additional SoC variants - The Qualcomm GENI core code gains interconnect path voting and performance level support, and integrating this into a number of device drivers. - A new driver for Samsung Exynos5800 voltage coupler for - Renesas RZ/G2H (R8A774E1) SoC support gets added to a couple of SoC specific device drivers - Updates to the TI K3 Ring Accelerator driver" * tag 'arm-drivers-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (164 commits) soc: qcom: geni: Fix unused label warning soc: qcom: smd-rpm: Fix kerneldoc memory: jz4780_nemc: Only request IO memory the driver will use soc: qcom: pdr: Reorder the PD state indication ack MAINTAINERS: Add Git repository for memory controller drivers memory: brcmstb_dpfe: Fix language typo memory: samsung: exynos5422-dmc: Correct white space issues memory: samsung: exynos-srom: Correct alignment memory: pl172: Enclose macro argument usage in parenthesis memory: of: Correct kerneldoc memory: omap-gpmc: Fix language typo memory: omap-gpmc: Correct white space issues memory: omap-gpmc: Use 'unsigned int' for consistency memory: omap-gpmc: Enclose macro argument usage in parenthesis memory: omap-gpmc: Correct kerneldoc memory: mvebu-devbus: Align with open parenthesis memory: mvebu-devbus: Add missing braces to all arms of if statement memory: bt1-l2-ctl: Add blank lines after declarations soc: TI knav_qmss: make symbol 'knav_acc_range_ops' static firmware: ti_sci: Replace HTTP links with HTTPS ones ...
This commit is contained in:
commit
822ef14e9d
9
Documentation/ABI/testing/debugfs-turris-mox-rwtm
Normal file
9
Documentation/ABI/testing/debugfs-turris-mox-rwtm
Normal file
@ -0,0 +1,9 @@
|
||||
What: /sys/kernel/debug/turris-mox-rwtm/do_sign
|
||||
Date: Jun 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (W) Message to sign with the ECDSA private key stored in
|
||||
device's OTP. The message must be exactly 64 bytes (since
|
||||
this is intended for SHA-512 hashes).
|
||||
(R) The resulting signature, 136 bytes. This contains the R and
|
||||
S values of the ECDSA signature, both in big-endian format.
|
8
Documentation/ABI/testing/sysfs-bus-optee-devices
Normal file
8
Documentation/ABI/testing/sysfs-bus-optee-devices
Normal file
@ -0,0 +1,8 @@
|
||||
What: /sys/bus/tee/devices/optee-ta-<uuid>/
|
||||
Date: May 2020
|
||||
KernelVersion 5.8
|
||||
Contact: op-tee@lists.trustedfirmware.org
|
||||
Description:
|
||||
OP-TEE bus provides reference to registered drivers under this directory. The <uuid>
|
||||
matches Trusted Application (TA) driver and corresponding TA in secure OS. Drivers
|
||||
are free to create needed API under optee-ta-<uuid> directory.
|
@ -26,6 +26,30 @@ Description:
|
||||
Read-only attribute common to all SoCs. Contains SoC family name
|
||||
(e.g. DB8500).
|
||||
|
||||
On many of ARM based silicon with SMCCC v1.2+ compliant firmware
|
||||
this will contain the JEDEC JEP106 manufacturer’s identification
|
||||
code. The format is "jep106:XXYY" where XX is identity code and
|
||||
YY is continuation code.
|
||||
|
||||
This manufacturer’s identification code is defined by one
|
||||
or more eight (8) bit fields, each consisting of seven (7)
|
||||
data bits plus one (1) odd parity bit. It is a single field,
|
||||
limiting the possible number of vendors to 126. To expand
|
||||
the maximum number of identification codes, a continuation
|
||||
scheme has been defined.
|
||||
|
||||
The specified mechanism is that an identity code of 0x7F
|
||||
represents the "continuation code" and implies the presence
|
||||
of an additional identity code field, and this mechanism
|
||||
may be extended to multiple continuation codes followed
|
||||
by the manufacturer's identity code.
|
||||
|
||||
For example, ARM has identity code 0x7F 0x7F 0x7F 0x7F 0x3B,
|
||||
which is code 0x3B on the fifth 'page'. This is shortened
|
||||
as JEP106 identity code of 0x3B and a continuation code of
|
||||
0x4 to represent the four continuation codes preceding the
|
||||
identity code.
|
||||
|
||||
What: /sys/devices/socX/serial_number
|
||||
Date: January 2019
|
||||
contact: Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
@ -40,6 +64,12 @@ Description:
|
||||
Read-only attribute supported by most SoCs. In the case of
|
||||
ST-Ericsson's chips this contains the SoC serial number.
|
||||
|
||||
On many of ARM based silicon with SMCCC v1.2+ compliant firmware
|
||||
this will contain the SOC ID appended to the family attribute
|
||||
to ensure there is no conflict in this namespace across various
|
||||
vendors. The format is "jep106:XXYY:ZZZZ" where XX is identity
|
||||
code, YY is continuation code and ZZZZ is the SOC ID.
|
||||
|
||||
What: /sys/devices/socX/revision
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
|
@ -11,10 +11,12 @@ Required properties:
|
||||
* "qcom,scm-apq8084"
|
||||
* "qcom,scm-ipq4019"
|
||||
* "qcom,scm-ipq806x"
|
||||
* "qcom,scm-ipq8074"
|
||||
* "qcom,scm-msm8660"
|
||||
* "qcom,scm-msm8916"
|
||||
* "qcom,scm-msm8960"
|
||||
* "qcom,scm-msm8974"
|
||||
* "qcom,scm-msm8994"
|
||||
* "qcom,scm-msm8996"
|
||||
* "qcom,scm-msm8998"
|
||||
* "qcom,scm-sc7180"
|
||||
|
@ -55,7 +55,7 @@ Required Properties:
|
||||
corresponds to a range of host irqs.
|
||||
|
||||
For more details on TISCI IRQ resource management refer:
|
||||
http://downloads.ti.com/tisci/esd/latest/2_tisci_msgs/rm/rm_irq.html
|
||||
https://downloads.ti.com/tisci/esd/latest/2_tisci_msgs/rm/rm_irq.html
|
||||
|
||||
Example:
|
||||
--------
|
||||
|
@ -1,49 +0,0 @@
|
||||
Freescale i.MX System Reset Controller
|
||||
======================================
|
||||
|
||||
Please also refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,<chip>-src"
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- interrupts: Should contain SRC interrupt and CPU WDOG interrupt,
|
||||
in this order.
|
||||
- #reset-cells: 1, see below
|
||||
|
||||
example:
|
||||
|
||||
src: src@20d8000 {
|
||||
compatible = "fsl,imx6q-src";
|
||||
reg = <0x020d8000 0x4000>;
|
||||
interrupts = <0 91 0x04 0 96 0x04>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
Specifying reset lines connected to IP modules
|
||||
==============================================
|
||||
|
||||
The system reset controller can be used to reset the GPU, VPU,
|
||||
IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
|
||||
nodes should specify the reset line on the SRC in their resets
|
||||
property, containing a phandle to the SRC device node and a
|
||||
RESET_INDEX specifying which module to reset, as described in
|
||||
reset.txt
|
||||
|
||||
example:
|
||||
|
||||
ipu1: ipu@2400000 {
|
||||
resets = <&src 2>;
|
||||
};
|
||||
ipu2: ipu@2800000 {
|
||||
resets = <&src 4>;
|
||||
};
|
||||
|
||||
The following RESET_INDEX values are valid for i.MX5:
|
||||
GPU_RESET 0
|
||||
VPU_RESET 1
|
||||
IPU1_RESET 2
|
||||
OPEN_VG_RESET 3
|
||||
The following additional RESET_INDEX value is valid for i.MX6:
|
||||
IPU2_RESET 4
|
82
Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
Normal file
82
Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
Normal file
@ -0,0 +1,82 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/fsl,imx-src.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale i.MX System Reset Controller
|
||||
|
||||
maintainers:
|
||||
- Philipp Zabel <p.zabel@pengutronix.de>
|
||||
|
||||
description: |
|
||||
The system reset controller can be used to reset the GPU, VPU,
|
||||
IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
|
||||
nodes should specify the reset line on the SRC in their resets
|
||||
property, containing a phandle to the SRC device node and a
|
||||
RESET_INDEX specifying which module to reset, as described in
|
||||
reset.txt
|
||||
|
||||
The following RESET_INDEX values are valid for i.MX5:
|
||||
GPU_RESET 0
|
||||
VPU_RESET 1
|
||||
IPU1_RESET 2
|
||||
OPEN_VG_RESET 3
|
||||
The following additional RESET_INDEX value is valid for i.MX6:
|
||||
IPU2_RESET 4
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: "fsl,imx51-src"
|
||||
- items:
|
||||
- const: "fsl,imx50-src"
|
||||
- const: "fsl,imx51-src"
|
||||
- items:
|
||||
- const: "fsl,imx53-src"
|
||||
- const: "fsl,imx51-src"
|
||||
- items:
|
||||
- const: "fsl,imx6q-src"
|
||||
- const: "fsl,imx51-src"
|
||||
- items:
|
||||
- const: "fsl,imx6sx-src"
|
||||
- const: "fsl,imx51-src"
|
||||
- items:
|
||||
- const: "fsl,imx6sl-src"
|
||||
- const: "fsl,imx51-src"
|
||||
- items:
|
||||
- const: "fsl,imx6ul-src"
|
||||
- const: "fsl,imx51-src"
|
||||
- items:
|
||||
- const: "fsl,imx6sll-src"
|
||||
- const: "fsl,imx51-src"
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: SRC interrupt
|
||||
- description: CPU WDOG interrupts out of SRC
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
reset-controller@73fd0000 {
|
||||
compatible = "fsl,imx51-src";
|
||||
reg = <0x73fd0000 0x4000>;
|
||||
interrupts = <75>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -1,56 +0,0 @@
|
||||
Freescale i.MX7 System Reset Controller
|
||||
======================================
|
||||
|
||||
Please also refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
- For i.MX7 SoCs should be "fsl,imx7d-src", "syscon"
|
||||
- For i.MX8MQ SoCs should be "fsl,imx8mq-src", "syscon"
|
||||
- For i.MX8MM SoCs should be "fsl,imx8mm-src", "fsl,imx8mq-src", "syscon"
|
||||
- For i.MX8MN SoCs should be "fsl,imx8mn-src", "fsl,imx8mq-src", "syscon"
|
||||
- For i.MX8MP SoCs should be "fsl,imx8mp-src", "syscon"
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- interrupts: Should contain SRC interrupt
|
||||
- #reset-cells: 1, see below
|
||||
|
||||
example:
|
||||
|
||||
src: reset-controller@30390000 {
|
||||
compatible = "fsl,imx7d-src", "syscon";
|
||||
reg = <0x30390000 0x2000>;
|
||||
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
|
||||
Specifying reset lines connected to IP modules
|
||||
==============================================
|
||||
|
||||
The system reset controller can be used to reset various set of
|
||||
peripherals. Device nodes that need access to reset lines should
|
||||
specify them as a reset phandle in their corresponding node as
|
||||
specified in reset.txt.
|
||||
|
||||
Example:
|
||||
|
||||
pcie: pcie@33800000 {
|
||||
|
||||
...
|
||||
|
||||
resets = <&src IMX7_RESET_PCIEPHY>,
|
||||
<&src IMX7_RESET_PCIE_CTRL_APPS_EN>;
|
||||
reset-names = "pciephy", "apps";
|
||||
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
For list of all valid reset indices see
|
||||
<dt-bindings/reset/imx7-reset.h> for i.MX7,
|
||||
<dt-bindings/reset/imx8mq-reset.h> for i.MX8MQ and
|
||||
<dt-bindings/reset/imx8mq-reset.h> for i.MX8MM and
|
||||
<dt-bindings/reset/imx8mq-reset.h> for i.MX8MN and
|
||||
<dt-bindings/reset/imx8mp-reset.h> for i.MX8MP
|
58
Documentation/devicetree/bindings/reset/fsl,imx7-src.yaml
Normal file
58
Documentation/devicetree/bindings/reset/fsl,imx7-src.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/fsl,imx7-src.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale i.MX7 System Reset Controller
|
||||
|
||||
maintainers:
|
||||
- Andrey Smirnov <andrew.smirnov@gmail.com>
|
||||
|
||||
description: |
|
||||
The system reset controller can be used to reset various set of
|
||||
peripherals. Device nodes that need access to reset lines should
|
||||
specify them as a reset phandle in their corresponding node as
|
||||
specified in reset.txt.
|
||||
|
||||
For list of all valid reset indices see
|
||||
<dt-bindings/reset/imx7-reset.h> for i.MX7,
|
||||
<dt-bindings/reset/imx8mq-reset.h> for i.MX8MQ, i.MX8MM and i.MX8MN,
|
||||
<dt-bindings/reset/imx8mp-reset.h> for i.MX8MP.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- fsl,imx7d-src
|
||||
- fsl,imx8mq-src
|
||||
- fsl,imx8mp-src
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
reset-controller@30390000 {
|
||||
compatible = "fsl,imx7d-src", "syscon";
|
||||
reg = <0x30390000 0x2000>;
|
||||
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -1,62 +0,0 @@
|
||||
Qualcomm Resource Power Manager (RPM) over SMD
|
||||
|
||||
This driver is used to interface with the Resource Power Manager (RPM) found in
|
||||
various Qualcomm platforms. The RPM allows each component in the system to vote
|
||||
for state of the system resources, such as clocks, regulators and bus
|
||||
frequencies.
|
||||
|
||||
The SMD information for the RPM edge should be filled out. See qcom,smd.txt for
|
||||
the required edge properties. All SMD related properties will reside within the
|
||||
RPM node itself.
|
||||
|
||||
= SUBDEVICES
|
||||
|
||||
The RPM exposes resources to its subnodes. The rpm_requests node must be
|
||||
present and this subnode may contain children that designate regulator
|
||||
resources.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be one of:
|
||||
"qcom,rpm-apq8084"
|
||||
"qcom,rpm-msm8916"
|
||||
"qcom,rpm-msm8974"
|
||||
"qcom,rpm-msm8976"
|
||||
"qcom,rpm-msm8998"
|
||||
"qcom,rpm-sdm660"
|
||||
"qcom,rpm-qcs404"
|
||||
|
||||
- qcom,smd-channels:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be "rpm_requests"
|
||||
|
||||
Refer to Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
|
||||
for information on the regulator subnodes that can exist under the rpm_requests.
|
||||
|
||||
Example:
|
||||
|
||||
soc {
|
||||
apcs: syscon@f9011000 {
|
||||
compatible = "syscon";
|
||||
reg = <0xf9011000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
smd {
|
||||
compatible = "qcom,smd";
|
||||
|
||||
rpm {
|
||||
interrupts = <0 168 1>;
|
||||
qcom,ipc = <&apcs 8 0>;
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm_requests {
|
||||
compatible = "qcom,rpm-msm8974";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
87
Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml
Normal file
87
Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml
Normal file
@ -0,0 +1,87 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/soc/qcom/qcom,smd-rpm.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Qualcomm Resource Power Manager (RPM) over SMD
|
||||
|
||||
description: |
|
||||
This driver is used to interface with the Resource Power Manager (RPM) found
|
||||
in various Qualcomm platforms. The RPM allows each component in the system
|
||||
to vote for state of the system resources, such as clocks, regulators and bus
|
||||
frequencies.
|
||||
|
||||
The SMD information for the RPM edge should be filled out. See qcom,smd.txt
|
||||
for the required edge properties. All SMD related properties will reside
|
||||
within the RPM node itself.
|
||||
|
||||
The RPM exposes resources to its subnodes. The rpm_requests node must be
|
||||
present and this subnode may contain children that designate regulator
|
||||
resources.
|
||||
|
||||
Refer to Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
|
||||
for information on the regulator subnodes that can exist under the
|
||||
rpm_requests.
|
||||
|
||||
maintainers:
|
||||
- Kathiravan T <kathirav@codeaurora.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,rpm-apq8084
|
||||
- qcom,rpm-ipq6018
|
||||
- qcom,rpm-msm8916
|
||||
- qcom,rpm-msm8974
|
||||
- qcom,rpm-msm8976
|
||||
- qcom,rpm-msm8996
|
||||
- qcom,rpm-msm8998
|
||||
- qcom,rpm-sdm660
|
||||
- qcom,rpm-qcs404
|
||||
|
||||
qcom,smd-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
description: Channel name used for the RPM communication
|
||||
items:
|
||||
- const: rpm_requests
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,rpm-apq8084
|
||||
- qcom,rpm-msm8916
|
||||
- qcom,rpm-msm8974
|
||||
then:
|
||||
required:
|
||||
- qcom,smd-channels
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
smd {
|
||||
compatible = "qcom,smd";
|
||||
|
||||
rpm {
|
||||
interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
|
||||
qcom,ipc = <&apcs 8 0>;
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm_requests {
|
||||
compatible = "qcom,rpm-msm8974";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
/* Regulator nodes to follow */
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -1,59 +0,0 @@
|
||||
* Texas Instruments K3 NavigatorSS Ring Accelerator
|
||||
|
||||
The Ring Accelerator (RA) is a machine which converts read/write accesses
|
||||
from/to a constant address into corresponding read/write accesses from/to a
|
||||
circular data structure in memory. The RA eliminates the need for each DMA
|
||||
controller which needs to access ring elements from having to know the current
|
||||
state of the ring (base address, current offset). The DMA controller
|
||||
performs a read or write access to a specific address range (which maps to the
|
||||
source interface on the RA) and the RA replaces the address for the transaction
|
||||
with a new address which corresponds to the head or tail element of the ring
|
||||
(head for reads, tail for writes).
|
||||
|
||||
The Ring Accelerator is a hardware module that is responsible for accelerating
|
||||
management of the packet queues. The K3 SoCs can have more than one RA instances
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "ti,am654-navss-ringacc";
|
||||
- reg : Should contain register location and length of the following
|
||||
named register regions.
|
||||
- reg-names : should be
|
||||
"rt" - The RA Ring Real-time Control/Status Registers
|
||||
"fifos" - The RA Queues Registers
|
||||
"proxy_gcfg" - The RA Proxy Global Config Registers
|
||||
"proxy_target" - The RA Proxy Datapath Registers
|
||||
- ti,num-rings : Number of rings supported by RA
|
||||
- ti,sci-rm-range-gp-rings : TI-SCI RM subtype for GP ring range
|
||||
- ti,sci : phandle on TI-SCI compatible System controller node
|
||||
- ti,sci-dev-id : TI-SCI device id of the ring accelerator
|
||||
- msi-parent : phandle for "ti,sci-inta" interrupt controller
|
||||
|
||||
Optional properties:
|
||||
-- ti,dma-ring-reset-quirk : enable ringacc / udma ring state interoperability
|
||||
issue software w/a
|
||||
|
||||
Example:
|
||||
|
||||
ringacc: ringacc@3c000000 {
|
||||
compatible = "ti,am654-navss-ringacc";
|
||||
reg = <0x0 0x3c000000 0x0 0x400000>,
|
||||
<0x0 0x38000000 0x0 0x400000>,
|
||||
<0x0 0x31120000 0x0 0x100>,
|
||||
<0x0 0x33000000 0x0 0x40000>;
|
||||
reg-names = "rt", "fifos",
|
||||
"proxy_gcfg", "proxy_target";
|
||||
ti,num-rings = <818>;
|
||||
ti,sci-rm-range-gp-rings = <0x2>; /* GP ring range */
|
||||
ti,dma-ring-reset-quirk;
|
||||
ti,sci = <&dmsc>;
|
||||
ti,sci-dev-id = <187>;
|
||||
msi-parent = <&inta_main_udmass>;
|
||||
};
|
||||
|
||||
client:
|
||||
|
||||
dma_ipx: dma_ipx@<addr> {
|
||||
...
|
||||
ti,ringacc = <&ringacc>;
|
||||
...
|
||||
}
|
102
Documentation/devicetree/bindings/soc/ti/k3-ringacc.yaml
Normal file
102
Documentation/devicetree/bindings/soc/ti/k3-ringacc.yaml
Normal file
@ -0,0 +1,102 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/soc/ti/k3-ringacc.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Texas Instruments K3 NavigatorSS Ring Accelerator
|
||||
|
||||
maintainers:
|
||||
- Santosh Shilimkar <ssantosh@kernel.org>
|
||||
- Grygorii Strashko <grygorii.strashko@ti.com>
|
||||
|
||||
description: |
|
||||
The Ring Accelerator (RA) is a machine which converts read/write accesses
|
||||
from/to a constant address into corresponding read/write accesses from/to a
|
||||
circular data structure in memory. The RA eliminates the need for each DMA
|
||||
controller which needs to access ring elements from having to know the current
|
||||
state of the ring (base address, current offset). The DMA controller
|
||||
performs a read or write access to a specific address range (which maps to the
|
||||
source interface on the RA) and the RA replaces the address for the transaction
|
||||
with a new address which corresponds to the head or tail element of the ring
|
||||
(head for reads, tail for writes).
|
||||
|
||||
The Ring Accelerator is a hardware module that is responsible for accelerating
|
||||
management of the packet queues. The K3 SoCs can have more than one RA instances
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: ti,am654-navss-ringacc
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: real time registers regions
|
||||
- description: fifos registers regions
|
||||
- description: proxy gcfg registers regions
|
||||
- description: proxy target registers regions
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: rt
|
||||
- const: fifos
|
||||
- const: proxy_gcfg
|
||||
- const: proxy_target
|
||||
|
||||
msi-parent: true
|
||||
|
||||
ti,num-rings:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Number of rings supported by RA
|
||||
|
||||
ti,sci-rm-range-gp-rings:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: TI-SCI RM subtype for GP ring range
|
||||
|
||||
ti,sci:
|
||||
$ref: /schemas/types.yaml#definitions/phandle-array
|
||||
description: phandle on TI-SCI compatible System controller node
|
||||
|
||||
ti,sci-dev-id:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: TI-SCI device id of the ring accelerator
|
||||
|
||||
ti,dma-ring-reset-quirk:
|
||||
$ref: /schemas/types.yaml#definitions/flag
|
||||
description: |
|
||||
enable ringacc/udma ring state interoperability issue software w/a
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- msi-parent
|
||||
- ti,num-rings
|
||||
- ti,sci-rm-range-gp-rings
|
||||
- ti,sci
|
||||
- ti,sci-dev-id
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ringacc: ringacc@3c000000 {
|
||||
compatible = "ti,am654-navss-ringacc";
|
||||
reg = <0x0 0x3c000000 0x0 0x400000>,
|
||||
<0x0 0x38000000 0x0 0x400000>,
|
||||
<0x0 0x31120000 0x0 0x100>,
|
||||
<0x0 0x33000000 0x0 0x40000>;
|
||||
reg-names = "rt", "fifos", "proxy_gcfg", "proxy_target";
|
||||
ti,num-rings = <818>;
|
||||
ti,sci-rm-range-gp-rings = <0x2>; /* GP ring range */
|
||||
ti,dma-ring-reset-quirk;
|
||||
ti,sci = <&dmsc>;
|
||||
ti,sci-dev-id = <187>;
|
||||
msi-parent = <&inta_main_udmass>;
|
||||
};
|
||||
};
|
@ -11117,6 +11117,14 @@ F: Documentation/core-api/boot-time-mm.rst
|
||||
F: include/linux/memblock.h
|
||||
F: mm/memblock.c
|
||||
|
||||
MEMORY CONTROLLER DRIVERS
|
||||
M: Krzysztof Kozlowski <krzk@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl.git
|
||||
F: Documentation/devicetree/bindings/memory-controllers/
|
||||
F: drivers/memory/
|
||||
|
||||
MEMORY MANAGEMENT
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
L: linux-mm@kvack.org
|
||||
@ -12729,6 +12737,7 @@ OP-TEE DRIVER
|
||||
M: Jens Wiklander <jens.wiklander@linaro.org>
|
||||
L: op-tee@lists.trustedfirmware.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-optee-devices
|
||||
F: drivers/tee/optee/
|
||||
|
||||
OP-TEE RANDOM NUMBER GENERATOR (RNG) DRIVER
|
||||
|
@ -118,6 +118,7 @@ config SOC_EXYNOS5800
|
||||
bool "Samsung EXYNOS5800"
|
||||
default y
|
||||
depends on SOC_EXYNOS5420
|
||||
select EXYNOS_REGULATOR_COUPLER
|
||||
|
||||
config EXYNOS_MCPM
|
||||
bool
|
||||
|
@ -775,19 +775,23 @@ static const char * __init omap_get_family(void)
|
||||
return kasprintf(GFP_KERNEL, "Unknown");
|
||||
}
|
||||
|
||||
static ssize_t omap_get_type(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static ssize_t
|
||||
type_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", omap_types[omap_type()]);
|
||||
}
|
||||
|
||||
static struct device_attribute omap_soc_attr =
|
||||
__ATTR(type, S_IRUGO, omap_get_type, NULL);
|
||||
static DEVICE_ATTR_RO(type);
|
||||
|
||||
static struct attribute *omap_soc_attrs[] = {
|
||||
&dev_attr_type.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(omap_soc);
|
||||
|
||||
void __init omap_soc_device_init(void)
|
||||
{
|
||||
struct device *parent;
|
||||
struct soc_device *soc_dev;
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
|
||||
@ -798,14 +802,12 @@ void __init omap_soc_device_init(void)
|
||||
soc_dev_attr->machine = soc_name;
|
||||
soc_dev_attr->family = omap_get_family();
|
||||
soc_dev_attr->revision = soc_rev;
|
||||
soc_dev_attr->custom_attr_group = omap_soc_groups[0];
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
kfree(soc_dev_attr);
|
||||
return;
|
||||
}
|
||||
|
||||
parent = soc_device_to_device(soc_dev);
|
||||
device_create_file(parent, &omap_soc_attr);
|
||||
}
|
||||
#endif /* CONFIG_SOC_BUS */
|
||||
|
@ -881,7 +881,6 @@ CONFIG_OWL_PM_DOMAINS=y
|
||||
CONFIG_RASPBERRYPI_POWER=y
|
||||
CONFIG_FSL_DPAA=y
|
||||
CONFIG_FSL_MC_DPIO=y
|
||||
CONFIG_IMX_SCU_SOC=y
|
||||
CONFIG_QCOM_AOSS_QMP=y
|
||||
CONFIG_QCOM_GENI_SE=y
|
||||
CONFIG_QCOM_RMTFS_MEM=m
|
||||
|
@ -214,11 +214,10 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data)
|
||||
* Return:
|
||||
* On success, 0. On failure, -errno.
|
||||
*/
|
||||
static int ftpm_tee_probe(struct platform_device *pdev)
|
||||
static int ftpm_tee_probe(struct device *dev)
|
||||
{
|
||||
int rc;
|
||||
struct tpm_chip *chip;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ftpm_tee_private *pvt_data = NULL;
|
||||
struct tee_ioctl_open_session_arg sess_arg;
|
||||
|
||||
@ -297,6 +296,13 @@ out_tee_session:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ftpm_plat_tee_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
return ftpm_tee_probe(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ftpm_tee_remove() - remove the TPM device
|
||||
* @pdev: the platform_device description.
|
||||
@ -304,9 +310,9 @@ out_tee_session:
|
||||
* Return:
|
||||
* 0 always.
|
||||
*/
|
||||
static int ftpm_tee_remove(struct platform_device *pdev)
|
||||
static int ftpm_tee_remove(struct device *dev)
|
||||
{
|
||||
struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev);
|
||||
struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev);
|
||||
|
||||
/* Release the chip */
|
||||
tpm_chip_unregister(pvt_data->chip);
|
||||
@ -328,11 +334,18 @@ static int ftpm_tee_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftpm_plat_tee_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
return ftpm_tee_remove(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ftpm_tee_shutdown() - shutdown the TPM device
|
||||
* @pdev: the platform_device description.
|
||||
*/
|
||||
static void ftpm_tee_shutdown(struct platform_device *pdev)
|
||||
static void ftpm_plat_tee_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
@ -347,17 +360,54 @@ static const struct of_device_id of_ftpm_tee_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids);
|
||||
|
||||
static struct platform_driver ftpm_tee_driver = {
|
||||
static struct platform_driver ftpm_tee_plat_driver = {
|
||||
.driver = {
|
||||
.name = "ftpm-tee",
|
||||
.of_match_table = of_match_ptr(of_ftpm_tee_ids),
|
||||
},
|
||||
.probe = ftpm_tee_probe,
|
||||
.remove = ftpm_tee_remove,
|
||||
.shutdown = ftpm_tee_shutdown,
|
||||
.shutdown = ftpm_plat_tee_shutdown,
|
||||
.probe = ftpm_plat_tee_probe,
|
||||
.remove = ftpm_plat_tee_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ftpm_tee_driver);
|
||||
/* UUID of the fTPM TA */
|
||||
static const struct tee_client_device_id optee_ftpm_id_table[] = {
|
||||
{UUID_INIT(0xbc50d971, 0xd4c9, 0x42c4,
|
||||
0x82, 0xcb, 0x34, 0x3f, 0xb7, 0xf3, 0x78, 0x96)},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table);
|
||||
|
||||
static struct tee_client_driver ftpm_tee_driver = {
|
||||
.id_table = optee_ftpm_id_table,
|
||||
.driver = {
|
||||
.name = "optee-ftpm",
|
||||
.bus = &tee_bus_type,
|
||||
.probe = ftpm_tee_probe,
|
||||
.remove = ftpm_tee_remove,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ftpm_mod_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = platform_driver_register(&ftpm_tee_plat_driver);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return driver_register(&ftpm_tee_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit ftpm_mod_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ftpm_tee_plat_driver);
|
||||
driver_unregister(&ftpm_tee_driver.driver);
|
||||
}
|
||||
|
||||
module_init(ftpm_mod_init);
|
||||
module_exit(ftpm_mod_exit);
|
||||
|
||||
MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>");
|
||||
MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE");
|
||||
|
@ -103,6 +103,8 @@ static const struct clk_ops scmi_clk_ops = {
|
||||
static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
|
||||
{
|
||||
int ret;
|
||||
unsigned long min_rate, max_rate;
|
||||
|
||||
struct clk_init_data init = {
|
||||
.flags = CLK_GET_RATE_NOCACHE,
|
||||
.num_parents = 0,
|
||||
@ -112,9 +114,23 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
|
||||
|
||||
sclk->hw.init = &init;
|
||||
ret = devm_clk_hw_register(dev, &sclk->hw);
|
||||
if (!ret)
|
||||
clk_hw_set_rate_range(&sclk->hw, sclk->info->range.min_rate,
|
||||
sclk->info->range.max_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (sclk->info->rate_discrete) {
|
||||
int num_rates = sclk->info->list.num_rates;
|
||||
|
||||
if (num_rates <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
min_rate = sclk->info->list.rates[0];
|
||||
max_rate = sclk->info->list.rates[num_rates - 1];
|
||||
} else {
|
||||
min_rate = sclk->info->range.min_rate;
|
||||
max_rate = sclk->info->range.max_rate;
|
||||
}
|
||||
|
||||
clk_hw_set_rate_range(&sclk->hw, min_rate, max_rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,8 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
|
||||
policy->cpuinfo.transition_latency = latency;
|
||||
|
||||
policy->fast_switch_possible = true;
|
||||
policy->fast_switch_possible =
|
||||
handle->perf_ops->fast_switch_possible(handle, cpu_dev);
|
||||
|
||||
em_register_perf_domain(policy->cpus, nr_opp, &em_cb);
|
||||
|
||||
|
@ -271,20 +271,12 @@ struct k3_udma_glue_tx_channel *k3_udma_glue_request_tx_chn(struct device *dev,
|
||||
atomic_set(&tx_chn->free_pkts, cfg->txcq_cfg.size);
|
||||
|
||||
/* request and cfg rings */
|
||||
tx_chn->ringtx = k3_ringacc_request_ring(tx_chn->common.ringacc,
|
||||
tx_chn->udma_tchan_id, 0);
|
||||
if (!tx_chn->ringtx) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "Failed to get TX ring %u\n",
|
||||
tx_chn->udma_tchan_id);
|
||||
goto err;
|
||||
}
|
||||
|
||||
tx_chn->ringtxcq = k3_ringacc_request_ring(tx_chn->common.ringacc,
|
||||
-1, 0);
|
||||
if (!tx_chn->ringtxcq) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "Failed to get TXCQ ring\n");
|
||||
ret = k3_ringacc_request_rings_pair(tx_chn->common.ringacc,
|
||||
tx_chn->udma_tchan_id, -1,
|
||||
&tx_chn->ringtx,
|
||||
&tx_chn->ringtxcq);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get TX/TXCQ rings %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -587,22 +579,16 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn,
|
||||
}
|
||||
|
||||
/* request and cfg rings */
|
||||
flow->ringrx = k3_ringacc_request_ring(rx_chn->common.ringacc,
|
||||
flow_cfg->ring_rxq_id, 0);
|
||||
if (!flow->ringrx) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "Failed to get RX ring\n");
|
||||
ret = k3_ringacc_request_rings_pair(rx_chn->common.ringacc,
|
||||
flow_cfg->ring_rxq_id,
|
||||
flow_cfg->ring_rxfdq0_id,
|
||||
&flow->ringrxfdq,
|
||||
&flow->ringrx);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get RX/RXFDQ rings %d\n", ret);
|
||||
goto err_rflow_put;
|
||||
}
|
||||
|
||||
flow->ringrxfdq = k3_ringacc_request_ring(rx_chn->common.ringacc,
|
||||
flow_cfg->ring_rxfdq0_id, 0);
|
||||
if (!flow->ringrxfdq) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "Failed to get RXFDQ ring\n");
|
||||
goto err_ringrx_free;
|
||||
}
|
||||
|
||||
ret = k3_ringacc_ring_cfg(flow->ringrx, &flow_cfg->rx_cfg);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to cfg ringrx %d\n", ret);
|
||||
@ -673,8 +659,6 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn,
|
||||
|
||||
err_ringrxfdq_free:
|
||||
k3_ringacc_ring_free(flow->ringrxfdq);
|
||||
|
||||
err_ringrx_free:
|
||||
k3_ringacc_ring_free(flow->ringrx);
|
||||
|
||||
err_rflow_put:
|
||||
|
@ -1418,17 +1418,12 @@ static int udma_alloc_tx_resources(struct udma_chan *uc)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uc->tchan->t_ring = k3_ringacc_request_ring(ud->ringacc,
|
||||
uc->tchan->id, 0);
|
||||
if (!uc->tchan->t_ring) {
|
||||
ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->tchan->id, -1,
|
||||
&uc->tchan->t_ring,
|
||||
&uc->tchan->tc_ring);
|
||||
if (ret) {
|
||||
ret = -EBUSY;
|
||||
goto err_tx_ring;
|
||||
}
|
||||
|
||||
uc->tchan->tc_ring = k3_ringacc_request_ring(ud->ringacc, -1, 0);
|
||||
if (!uc->tchan->tc_ring) {
|
||||
ret = -EBUSY;
|
||||
goto err_txc_ring;
|
||||
goto err_ring;
|
||||
}
|
||||
|
||||
memset(&ring_cfg, 0, sizeof(ring_cfg));
|
||||
@ -1447,10 +1442,9 @@ static int udma_alloc_tx_resources(struct udma_chan *uc)
|
||||
err_ringcfg:
|
||||
k3_ringacc_ring_free(uc->tchan->tc_ring);
|
||||
uc->tchan->tc_ring = NULL;
|
||||
err_txc_ring:
|
||||
k3_ringacc_ring_free(uc->tchan->t_ring);
|
||||
uc->tchan->t_ring = NULL;
|
||||
err_tx_ring:
|
||||
err_ring:
|
||||
udma_put_tchan(uc);
|
||||
|
||||
return ret;
|
||||
@ -1499,16 +1493,11 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
|
||||
|
||||
rflow = uc->rflow;
|
||||
fd_ring_id = ud->tchan_cnt + ud->echan_cnt + uc->rchan->id;
|
||||
rflow->fd_ring = k3_ringacc_request_ring(ud->ringacc, fd_ring_id, 0);
|
||||
if (!rflow->fd_ring) {
|
||||
ret = k3_ringacc_request_rings_pair(ud->ringacc, fd_ring_id, -1,
|
||||
&rflow->fd_ring, &rflow->r_ring);
|
||||
if (ret) {
|
||||
ret = -EBUSY;
|
||||
goto err_rx_ring;
|
||||
}
|
||||
|
||||
rflow->r_ring = k3_ringacc_request_ring(ud->ringacc, -1, 0);
|
||||
if (!rflow->r_ring) {
|
||||
ret = -EBUSY;
|
||||
goto err_rxc_ring;
|
||||
goto err_ring;
|
||||
}
|
||||
|
||||
memset(&ring_cfg, 0, sizeof(ring_cfg));
|
||||
@ -1533,10 +1522,9 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
|
||||
err_ringcfg:
|
||||
k3_ringacc_ring_free(rflow->r_ring);
|
||||
rflow->r_ring = NULL;
|
||||
err_rxc_ring:
|
||||
k3_ringacc_ring_free(rflow->fd_ring);
|
||||
rflow->fd_ring = NULL;
|
||||
err_rx_ring:
|
||||
err_ring:
|
||||
udma_put_rflow(uc);
|
||||
err_rflow:
|
||||
udma_put_rchan(uc);
|
||||
|
@ -1,9 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o
|
||||
scmi-bus-y = bus.o
|
||||
scmi-driver-y = driver.o
|
||||
scmi-driver-y = driver.o notify.o
|
||||
scmi-transport-y = shmem.o
|
||||
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
|
||||
scmi-transport-$(CONFIG_ARM_PSCI_FW) += smc.o
|
||||
scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
|
||||
|
@ -5,7 +5,15 @@
|
||||
* Copyright (C) 2018 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications BASE - " fmt
|
||||
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define SCMI_BASE_NUM_SOURCES 1
|
||||
#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
|
||||
|
||||
enum scmi_base_protocol_cmd {
|
||||
BASE_DISCOVER_VENDOR = 0x3,
|
||||
@ -19,16 +27,25 @@ enum scmi_base_protocol_cmd {
|
||||
BASE_RESET_AGENT_CONFIGURATION = 0xb,
|
||||
};
|
||||
|
||||
enum scmi_base_protocol_notify {
|
||||
BASE_ERROR_EVENT = 0x0,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_base_attributes {
|
||||
u8 num_protocols;
|
||||
u8 num_agents;
|
||||
__le16 reserved;
|
||||
};
|
||||
|
||||
struct scmi_msg_base_error_notify {
|
||||
__le32 event_control;
|
||||
#define BASE_TP_NOTIFY_ALL BIT(0)
|
||||
};
|
||||
|
||||
struct scmi_base_error_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 error_status;
|
||||
#define IS_FATAL_ERROR(x) ((x) & BIT(31))
|
||||
#define ERROR_CMD_COUNT(x) FIELD_GET(GENMASK(9, 0), (x))
|
||||
__le64 msg_reports[SCMI_BASE_MAX_CMD_ERR_COUNT];
|
||||
};
|
||||
|
||||
/**
|
||||
* scmi_base_attributes_get() - gets the implementation details
|
||||
* that are associated with the base protocol.
|
||||
@ -222,6 +239,83 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_base_error_notify(const struct scmi_handle *handle, bool enable)
|
||||
{
|
||||
int ret;
|
||||
u32 evt_cntl = enable ? BASE_TP_NOTIFY_ALL : 0;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_base_error_notify *cfg;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, BASE_NOTIFY_ERRORS,
|
||||
SCMI_PROTOCOL_BASE, sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->event_control = cpu_to_le32(evt_cntl);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_base_set_notify_enabled(const struct scmi_handle *handle,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = scmi_base_error_notify(handle, enable);
|
||||
if (ret)
|
||||
pr_debug("FAIL_ENABLED - evt[%X] ret:%d\n", evt_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *scmi_base_fill_custom_report(const struct scmi_handle *handle,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
int i;
|
||||
const struct scmi_base_error_notify_payld *p = payld;
|
||||
struct scmi_base_error_report *r = report;
|
||||
|
||||
/*
|
||||
* BaseError notification payload is variable in size but
|
||||
* up to a maximum length determined by the struct ponted by p.
|
||||
* Instead payld_sz is the effective length of this notification
|
||||
* payload so cannot be greater of the maximum allowed size as
|
||||
* pointed by p.
|
||||
*/
|
||||
if (evt_id != SCMI_EVENT_BASE_ERROR_EVENT || sizeof(*p) < payld_sz)
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->fatal = IS_FATAL_ERROR(le32_to_cpu(p->error_status));
|
||||
r->cmd_count = ERROR_CMD_COUNT(le32_to_cpu(p->error_status));
|
||||
for (i = 0; i < r->cmd_count; i++)
|
||||
r->reports[i] = le64_to_cpu(p->msg_reports[i]);
|
||||
*src_id = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct scmi_event base_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_BASE_ERROR_EVENT,
|
||||
.max_payld_sz = sizeof(struct scmi_base_error_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_base_error_report) +
|
||||
SCMI_BASE_MAX_CMD_ERR_COUNT * sizeof(u64),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops base_event_ops = {
|
||||
.set_notify_enabled = scmi_base_set_notify_enabled,
|
||||
.fill_custom_report = scmi_base_fill_custom_report,
|
||||
};
|
||||
|
||||
int scmi_base_protocol_init(struct scmi_handle *h)
|
||||
{
|
||||
int id, ret;
|
||||
@ -256,6 +350,12 @@ int scmi_base_protocol_init(struct scmi_handle *h)
|
||||
dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
|
||||
rev->num_agents);
|
||||
|
||||
scmi_register_protocol_events(handle, SCMI_PROTOCOL_BASE,
|
||||
(4 * SCMI_PROTO_QUEUE_SZ),
|
||||
&base_event_ops, base_events,
|
||||
ARRAY_SIZE(base_events),
|
||||
SCMI_BASE_NUM_SOURCES);
|
||||
|
||||
for (id = 0; id < rev->num_agents; id++) {
|
||||
scmi_base_discover_agent_get(handle, id, name);
|
||||
dev_dbg(dev, "Agent %d: %s\n", id, name);
|
||||
|
@ -5,6 +5,8 @@
|
||||
* Copyright (C) 2018 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
enum scmi_clock_protocol_cmd {
|
||||
@ -121,11 +123,23 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rate_cmp_func(const void *_r1, const void *_r2)
|
||||
{
|
||||
const u64 *r1 = _r1, *r2 = _r2;
|
||||
|
||||
if (*r1 < *r2)
|
||||
return -1;
|
||||
else if (*r1 == *r2)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
|
||||
struct scmi_clock_info *clk)
|
||||
{
|
||||
u64 *rate;
|
||||
u64 *rate = NULL;
|
||||
int ret, cnt;
|
||||
bool rate_discrete = false;
|
||||
u32 tot_rate_cnt = 0, rates_flag;
|
||||
@ -184,8 +198,10 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
|
||||
*/
|
||||
} while (num_returned && num_remaining);
|
||||
|
||||
if (rate_discrete)
|
||||
if (rate_discrete && rate) {
|
||||
clk->list.num_rates = tot_rate_cnt;
|
||||
sort(rate, tot_rate_cnt, sizeof(*rate), rate_cmp_func, NULL);
|
||||
}
|
||||
|
||||
clk->rate_discrete = rate_discrete;
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
*
|
||||
* Copyright (C) 2018 ARM Ltd.
|
||||
*/
|
||||
#ifndef _SCMI_COMMON_H
|
||||
#define _SCMI_COMMON_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/completion.h>
|
||||
@ -235,3 +237,5 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
|
||||
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
|
||||
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer);
|
||||
|
||||
#endif /* _SCMI_COMMON_H */
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/scmi.h>
|
||||
@ -208,7 +209,9 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
|
||||
struct device *dev = cinfo->dev;
|
||||
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
|
||||
struct scmi_xfers_info *minfo = &info->rx_minfo;
|
||||
ktime_t ts;
|
||||
|
||||
ts = ktime_get_boottime();
|
||||
xfer = scmi_xfer_get(cinfo->handle, minfo);
|
||||
if (IS_ERR(xfer)) {
|
||||
dev_err(dev, "failed to get free message slot (%ld)\n",
|
||||
@ -221,6 +224,8 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
|
||||
scmi_dump_header_dbg(dev, &xfer->hdr);
|
||||
info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size,
|
||||
xfer);
|
||||
scmi_notify(cinfo->handle, xfer->hdr.protocol_id,
|
||||
xfer->hdr.id, xfer->rx.buf, xfer->rx.len, ts);
|
||||
|
||||
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
|
||||
xfer->hdr.protocol_id, xfer->hdr.seq,
|
||||
@ -392,8 +397,7 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
|
||||
info->desc->ops->mark_txdone(cinfo, ret);
|
||||
|
||||
trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
|
||||
xfer->hdr.protocol_id, xfer->hdr.seq,
|
||||
xfer->hdr.status);
|
||||
xfer->hdr.protocol_id, xfer->hdr.seq, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -789,6 +793,9 @@ static int scmi_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (scmi_notification_init(handle))
|
||||
dev_err(dev, "SCMI Notifications NOT available.\n");
|
||||
|
||||
ret = scmi_base_protocol_init(handle);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
|
||||
@ -831,6 +838,8 @@ static int scmi_remove(struct platform_device *pdev)
|
||||
struct scmi_info *info = platform_get_drvdata(pdev);
|
||||
struct idr *idr = &info->tx_idr;
|
||||
|
||||
scmi_notification_exit(&info->handle);
|
||||
|
||||
mutex_lock(&scmi_list_mutex);
|
||||
if (info->users)
|
||||
ret = -EBUSY;
|
||||
@ -901,7 +910,7 @@ ATTRIBUTE_GROUPS(versions);
|
||||
/* Each compatible listed below must have descriptor associated with it */
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
|
||||
#ifdef CONFIG_ARM_PSCI_FW
|
||||
#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY
|
||||
{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
|
||||
#endif
|
||||
{ /* Sentinel */ },
|
||||
|
1526
drivers/firmware/arm_scmi/notify.c
Normal file
1526
drivers/firmware/arm_scmi/notify.c
Normal file
File diff suppressed because it is too large
Load Diff
68
drivers/firmware/arm_scmi/notify.h
Normal file
68
drivers/firmware/arm_scmi/notify.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Message Protocol
|
||||
* notification header file containing some definitions, structures
|
||||
* and function prototypes related to SCMI Notification handling.
|
||||
*
|
||||
* Copyright (C) 2020 ARM Ltd.
|
||||
*/
|
||||
#ifndef _SCMI_NOTIFY_H
|
||||
#define _SCMI_NOTIFY_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define SCMI_PROTO_QUEUE_SZ 4096
|
||||
|
||||
/**
|
||||
* struct scmi_event - Describes an event to be supported
|
||||
* @id: Event ID
|
||||
* @max_payld_sz: Max possible size for the payload of a notification message
|
||||
* @max_report_sz: Max possible size for the report of a notification message
|
||||
*
|
||||
* Each SCMI protocol, during its initialization phase, can describe the events
|
||||
* it wishes to support in a few struct scmi_event and pass them to the core
|
||||
* using scmi_register_protocol_events().
|
||||
*/
|
||||
struct scmi_event {
|
||||
u8 id;
|
||||
size_t max_payld_sz;
|
||||
size_t max_report_sz;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_event_ops - Protocol helpers called by the notification core.
|
||||
* @set_notify_enabled: Enable/disable the required evt_id/src_id notifications
|
||||
* using the proper custom protocol commands.
|
||||
* Return 0 on Success
|
||||
* @fill_custom_report: fills a custom event report from the provided
|
||||
* event message payld identifying the event
|
||||
* specific src_id.
|
||||
* Return NULL on failure otherwise @report now fully
|
||||
* populated
|
||||
*
|
||||
* Context: Helpers described in &struct scmi_event_ops are called only in
|
||||
* process context.
|
||||
*/
|
||||
struct scmi_event_ops {
|
||||
int (*set_notify_enabled)(const struct scmi_handle *handle,
|
||||
u8 evt_id, u32 src_id, bool enabled);
|
||||
void *(*fill_custom_report)(const struct scmi_handle *handle,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id);
|
||||
};
|
||||
|
||||
int scmi_notification_init(struct scmi_handle *handle);
|
||||
void scmi_notification_exit(struct scmi_handle *handle);
|
||||
|
||||
int scmi_register_protocol_events(const struct scmi_handle *handle,
|
||||
u8 proto_id, size_t queue_sz,
|
||||
const struct scmi_event_ops *ops,
|
||||
const struct scmi_event *evt, int num_events,
|
||||
int num_sources);
|
||||
int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
|
||||
const void *buf, size_t len, ktime_t ts);
|
||||
|
||||
#endif /* _SCMI_NOTIFY_H */
|
@ -5,15 +5,19 @@
|
||||
* Copyright (C) 2018 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications PERF - " fmt
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_performance_protocol_cmd {
|
||||
PERF_DOMAIN_ATTRIBUTES = 0x3,
|
||||
@ -27,11 +31,6 @@ enum scmi_performance_protocol_cmd {
|
||||
PERF_DESCRIBE_FASTCHANNEL = 0xb,
|
||||
};
|
||||
|
||||
enum scmi_performance_protocol_notify {
|
||||
PERFORMANCE_LIMITS_CHANGED = 0x0,
|
||||
PERFORMANCE_LEVEL_CHANGED = 0x1,
|
||||
};
|
||||
|
||||
struct scmi_opp {
|
||||
u32 perf;
|
||||
u32 power;
|
||||
@ -86,6 +85,19 @@ struct scmi_perf_notify_level_or_limits {
|
||||
__le32 notify_enable;
|
||||
};
|
||||
|
||||
struct scmi_perf_limits_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 domain_id;
|
||||
__le32 range_max;
|
||||
__le32 range_min;
|
||||
};
|
||||
|
||||
struct scmi_perf_level_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 domain_id;
|
||||
__le32 performance_level;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_perf_describe_levels {
|
||||
__le16 num_returned;
|
||||
__le16 num_remaining;
|
||||
@ -158,6 +170,11 @@ struct scmi_perf_info {
|
||||
struct perf_dom_info *dom_info;
|
||||
};
|
||||
|
||||
static enum scmi_performance_protocol_cmd evt_2_cmd[] = {
|
||||
PERF_NOTIFY_LIMITS,
|
||||
PERF_NOTIFY_LEVEL,
|
||||
};
|
||||
|
||||
static int scmi_perf_attributes_get(const struct scmi_handle *handle,
|
||||
struct scmi_perf_info *pi)
|
||||
{
|
||||
@ -488,6 +505,29 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
|
||||
return scmi_perf_mb_level_get(handle, domain, level, poll);
|
||||
}
|
||||
|
||||
static int scmi_perf_level_limits_notify(const struct scmi_handle *handle,
|
||||
u32 domain, int message_id,
|
||||
bool enable)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_perf_notify_level_or_limits *notify;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, message_id, SCMI_PROTOCOL_PERF,
|
||||
sizeof(*notify), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
notify = t->tx.buf;
|
||||
notify->domain = cpu_to_le32(domain);
|
||||
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size)
|
||||
{
|
||||
if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4)
|
||||
@ -697,6 +737,17 @@ static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool scmi_fast_switch_possible(const struct scmi_handle *handle,
|
||||
struct device *dev)
|
||||
{
|
||||
struct perf_dom_info *dom;
|
||||
struct scmi_perf_info *pi = handle->perf_priv;
|
||||
|
||||
dom = pi->dom_info + scmi_dev_domain_id(dev);
|
||||
|
||||
return dom->fc_info && dom->fc_info->level_set_addr;
|
||||
}
|
||||
|
||||
static struct scmi_perf_ops perf_ops = {
|
||||
.limits_set = scmi_perf_limits_set,
|
||||
.limits_get = scmi_perf_limits_get,
|
||||
@ -708,6 +759,90 @@ static struct scmi_perf_ops perf_ops = {
|
||||
.freq_set = scmi_dvfs_freq_set,
|
||||
.freq_get = scmi_dvfs_freq_get,
|
||||
.est_power_get = scmi_dvfs_est_power_get,
|
||||
.fast_switch_possible = scmi_fast_switch_possible,
|
||||
};
|
||||
|
||||
static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret, cmd_id;
|
||||
|
||||
if (evt_id >= ARRAY_SIZE(evt_2_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
cmd_id = evt_2_cmd[evt_id];
|
||||
ret = scmi_perf_level_limits_notify(handle, src_id, cmd_id, enable);
|
||||
if (ret)
|
||||
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
|
||||
evt_id, src_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *scmi_perf_fill_custom_report(const struct scmi_handle *handle,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
void *rep = NULL;
|
||||
|
||||
switch (evt_id) {
|
||||
case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED:
|
||||
{
|
||||
const struct scmi_perf_limits_notify_payld *p = payld;
|
||||
struct scmi_perf_limits_report *r = report;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
break;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
r->range_max = le32_to_cpu(p->range_max);
|
||||
r->range_min = le32_to_cpu(p->range_min);
|
||||
*src_id = r->domain_id;
|
||||
rep = r;
|
||||
break;
|
||||
}
|
||||
case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED:
|
||||
{
|
||||
const struct scmi_perf_level_notify_payld *p = payld;
|
||||
struct scmi_perf_level_report *r = report;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
break;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
r->performance_level = le32_to_cpu(p->performance_level);
|
||||
*src_id = r->domain_id;
|
||||
rep = r;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
static const struct scmi_event perf_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED,
|
||||
.max_payld_sz = sizeof(struct scmi_perf_limits_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_perf_limits_report),
|
||||
},
|
||||
{
|
||||
.id = SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED,
|
||||
.max_payld_sz = sizeof(struct scmi_perf_level_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_perf_level_report),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops perf_event_ops = {
|
||||
.set_notify_enabled = scmi_perf_set_notify_enabled,
|
||||
.fill_custom_report = scmi_perf_fill_custom_report,
|
||||
};
|
||||
|
||||
static int scmi_perf_protocol_init(struct scmi_handle *handle)
|
||||
@ -742,6 +877,12 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
|
||||
scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
|
||||
}
|
||||
|
||||
scmi_register_protocol_events(handle,
|
||||
SCMI_PROTOCOL_PERF, SCMI_PROTO_QUEUE_SZ,
|
||||
&perf_event_ops, perf_events,
|
||||
ARRAY_SIZE(perf_events),
|
||||
pinfo->num_domains);
|
||||
|
||||
pinfo->version = version;
|
||||
handle->perf_ops = &perf_ops;
|
||||
handle->perf_priv = pinfo;
|
||||
|
@ -5,19 +5,18 @@
|
||||
* Copyright (C) 2018 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
|
||||
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_power_protocol_cmd {
|
||||
POWER_DOMAIN_ATTRIBUTES = 0x3,
|
||||
POWER_STATE_SET = 0x4,
|
||||
POWER_STATE_GET = 0x5,
|
||||
POWER_STATE_NOTIFY = 0x6,
|
||||
POWER_STATE_CHANGE_REQUESTED_NOTIFY = 0x7,
|
||||
};
|
||||
|
||||
enum scmi_power_protocol_notify {
|
||||
POWER_STATE_CHANGED = 0x0,
|
||||
POWER_STATE_CHANGE_REQUESTED = 0x1,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_power_attributes {
|
||||
@ -48,6 +47,12 @@ struct scmi_power_state_notify {
|
||||
__le32 notify_enable;
|
||||
};
|
||||
|
||||
struct scmi_power_state_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 domain_id;
|
||||
__le32 power_state;
|
||||
};
|
||||
|
||||
struct power_dom_info {
|
||||
bool state_set_sync;
|
||||
bool state_set_async;
|
||||
@ -186,6 +191,75 @@ static struct scmi_power_ops power_ops = {
|
||||
.state_get = scmi_power_state_get,
|
||||
};
|
||||
|
||||
static int scmi_power_request_notify(const struct scmi_handle *handle,
|
||||
u32 domain, bool enable)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_power_state_notify *notify;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, POWER_STATE_NOTIFY,
|
||||
SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
notify = t->tx.buf;
|
||||
notify->domain = cpu_to_le32(domain);
|
||||
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_power_set_notify_enabled(const struct scmi_handle *handle,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = scmi_power_request_notify(handle, src_id, enable);
|
||||
if (ret)
|
||||
pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",
|
||||
evt_id, src_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *scmi_power_fill_custom_report(const struct scmi_handle *handle,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
const struct scmi_power_state_notify_payld *p = payld;
|
||||
struct scmi_power_state_changed_report *r = report;
|
||||
|
||||
if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz)
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
r->power_state = le32_to_cpu(p->power_state);
|
||||
*src_id = r->domain_id;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct scmi_event power_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_POWER_STATE_CHANGED,
|
||||
.max_payld_sz = sizeof(struct scmi_power_state_notify_payld),
|
||||
.max_report_sz =
|
||||
sizeof(struct scmi_power_state_changed_report),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops power_event_ops = {
|
||||
.set_notify_enabled = scmi_power_set_notify_enabled,
|
||||
.fill_custom_report = scmi_power_fill_custom_report,
|
||||
};
|
||||
|
||||
static int scmi_power_protocol_init(struct scmi_handle *handle)
|
||||
{
|
||||
int domain;
|
||||
@ -214,6 +288,12 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
|
||||
scmi_power_domain_attributes_get(handle, domain, dom);
|
||||
}
|
||||
|
||||
scmi_register_protocol_events(handle,
|
||||
SCMI_PROTOCOL_POWER, SCMI_PROTO_QUEUE_SZ,
|
||||
&power_event_ops, power_events,
|
||||
ARRAY_SIZE(power_events),
|
||||
pinfo->num_domains);
|
||||
|
||||
pinfo->version = version;
|
||||
handle->power_ops = &power_ops;
|
||||
handle->power_priv = pinfo;
|
||||
|
@ -5,7 +5,12 @@
|
||||
* Copyright (C) 2019 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
|
||||
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_reset_protocol_cmd {
|
||||
RESET_DOMAIN_ATTRIBUTES = 0x3,
|
||||
@ -13,10 +18,6 @@ enum scmi_reset_protocol_cmd {
|
||||
RESET_NOTIFY = 0x5,
|
||||
};
|
||||
|
||||
enum scmi_reset_protocol_notify {
|
||||
RESET_ISSUED = 0x0,
|
||||
};
|
||||
|
||||
#define NUM_RESET_DOMAIN_MASK 0xffff
|
||||
#define RESET_NOTIFY_ENABLE BIT(0)
|
||||
|
||||
@ -40,6 +41,18 @@ struct scmi_msg_reset_domain_reset {
|
||||
#define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE)
|
||||
};
|
||||
|
||||
struct scmi_msg_reset_notify {
|
||||
__le32 id;
|
||||
__le32 event_control;
|
||||
#define RESET_TP_NOTIFY_ALL BIT(0)
|
||||
};
|
||||
|
||||
struct scmi_reset_issued_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 domain_id;
|
||||
__le32 reset_state;
|
||||
};
|
||||
|
||||
struct reset_dom_info {
|
||||
bool async_reset;
|
||||
bool reset_notify;
|
||||
@ -190,6 +203,75 @@ static struct scmi_reset_ops reset_ops = {
|
||||
.deassert = scmi_reset_domain_deassert,
|
||||
};
|
||||
|
||||
static int scmi_reset_notify(const struct scmi_handle *handle, u32 domain_id,
|
||||
bool enable)
|
||||
{
|
||||
int ret;
|
||||
u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_reset_notify *cfg;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, RESET_NOTIFY,
|
||||
SCMI_PROTOCOL_RESET, sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(domain_id);
|
||||
cfg->event_control = cpu_to_le32(evt_cntl);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_reset_set_notify_enabled(const struct scmi_handle *handle,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = scmi_reset_notify(handle, src_id, enable);
|
||||
if (ret)
|
||||
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
|
||||
evt_id, src_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *scmi_reset_fill_custom_report(const struct scmi_handle *handle,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
const struct scmi_reset_issued_notify_payld *p = payld;
|
||||
struct scmi_reset_issued_report *r = report;
|
||||
|
||||
if (evt_id != SCMI_EVENT_RESET_ISSUED || sizeof(*p) != payld_sz)
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->domain_id = le32_to_cpu(p->domain_id);
|
||||
r->reset_state = le32_to_cpu(p->reset_state);
|
||||
*src_id = r->domain_id;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct scmi_event reset_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_RESET_ISSUED,
|
||||
.max_payld_sz = sizeof(struct scmi_reset_issued_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_reset_issued_report),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops reset_event_ops = {
|
||||
.set_notify_enabled = scmi_reset_set_notify_enabled,
|
||||
.fill_custom_report = scmi_reset_fill_custom_report,
|
||||
};
|
||||
|
||||
static int scmi_reset_protocol_init(struct scmi_handle *handle)
|
||||
{
|
||||
int domain;
|
||||
@ -218,6 +300,12 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
|
||||
scmi_reset_domain_attributes_get(handle, domain, dom);
|
||||
}
|
||||
|
||||
scmi_register_protocol_events(handle,
|
||||
SCMI_PROTOCOL_RESET, SCMI_PROTO_QUEUE_SZ,
|
||||
&reset_event_ops, reset_events,
|
||||
ARRAY_SIZE(reset_events),
|
||||
pinfo->num_domains);
|
||||
|
||||
pinfo->version = version;
|
||||
handle->reset_ops = &reset_ops;
|
||||
handle->reset_priv = pinfo;
|
||||
|
@ -85,7 +85,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
|
||||
for (i = 0; i < num_domains; i++, scmi_pd++) {
|
||||
u32 state;
|
||||
|
||||
domains[i] = &scmi_pd->genpd;
|
||||
if (handle->power_ops->state_get(handle, i, &state)) {
|
||||
dev_warn(dev, "failed to get state for domain %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
scmi_pd->domain = i;
|
||||
scmi_pd->handle = handle;
|
||||
@ -94,13 +97,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
|
||||
scmi_pd->genpd.power_off = scmi_pd_power_off;
|
||||
scmi_pd->genpd.power_on = scmi_pd_power_on;
|
||||
|
||||
if (handle->power_ops->state_get(handle, i, &state)) {
|
||||
dev_warn(dev, "failed to get state for domain %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
pm_genpd_init(&scmi_pd->genpd, NULL,
|
||||
state == SCMI_POWER_STATE_GENERIC_OFF);
|
||||
|
||||
domains[i] = &scmi_pd->genpd;
|
||||
}
|
||||
|
||||
scmi_pd_data->domains = domains;
|
||||
|
@ -5,7 +5,12 @@
|
||||
* Copyright (C) 2018 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt
|
||||
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "notify.h"
|
||||
|
||||
enum scmi_sensor_protocol_cmd {
|
||||
SENSOR_DESCRIPTION_GET = 0x3,
|
||||
@ -14,10 +19,6 @@ enum scmi_sensor_protocol_cmd {
|
||||
SENSOR_READING_GET = 0x6,
|
||||
};
|
||||
|
||||
enum scmi_sensor_protocol_notify {
|
||||
SENSOR_TRIP_POINT_EVENT = 0x0,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_sensor_attributes {
|
||||
__le16 num_sensors;
|
||||
u8 max_requests;
|
||||
@ -71,6 +72,12 @@ struct scmi_msg_sensor_reading_get {
|
||||
#define SENSOR_READ_ASYNC BIT(0)
|
||||
};
|
||||
|
||||
struct scmi_sensor_trip_notify_payld {
|
||||
__le32 agent_id;
|
||||
__le32 sensor_id;
|
||||
__le32 trip_point_desc;
|
||||
};
|
||||
|
||||
struct sensors_info {
|
||||
u32 version;
|
||||
int num_sensors;
|
||||
@ -271,11 +278,57 @@ static int scmi_sensor_count_get(const struct scmi_handle *handle)
|
||||
static struct scmi_sensor_ops sensor_ops = {
|
||||
.count_get = scmi_sensor_count_get,
|
||||
.info_get = scmi_sensor_info_get,
|
||||
.trip_point_notify = scmi_sensor_trip_point_notify,
|
||||
.trip_point_config = scmi_sensor_trip_point_config,
|
||||
.reading_get = scmi_sensor_reading_get,
|
||||
};
|
||||
|
||||
static int scmi_sensor_set_notify_enabled(const struct scmi_handle *handle,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = scmi_sensor_trip_point_notify(handle, src_id, enable);
|
||||
if (ret)
|
||||
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
|
||||
evt_id, src_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *scmi_sensor_fill_custom_report(const struct scmi_handle *handle,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
const struct scmi_sensor_trip_notify_payld *p = payld;
|
||||
struct scmi_sensor_trip_point_report *r = report;
|
||||
|
||||
if (evt_id != SCMI_EVENT_SENSOR_TRIP_POINT_EVENT ||
|
||||
sizeof(*p) != payld_sz)
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->sensor_id = le32_to_cpu(p->sensor_id);
|
||||
r->trip_point_desc = le32_to_cpu(p->trip_point_desc);
|
||||
*src_id = r->sensor_id;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct scmi_event sensor_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_SENSOR_TRIP_POINT_EVENT,
|
||||
.max_payld_sz = sizeof(struct scmi_sensor_trip_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_sensor_trip_point_report),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops sensor_event_ops = {
|
||||
.set_notify_enabled = scmi_sensor_set_notify_enabled,
|
||||
.fill_custom_report = scmi_sensor_fill_custom_report,
|
||||
};
|
||||
|
||||
static int scmi_sensors_protocol_init(struct scmi_handle *handle)
|
||||
{
|
||||
u32 version;
|
||||
@ -299,6 +352,12 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
|
||||
|
||||
scmi_sensor_description_get(handle, sinfo);
|
||||
|
||||
scmi_register_protocol_events(handle,
|
||||
SCMI_PROTOCOL_SENSOR, SCMI_PROTO_QUEUE_SZ,
|
||||
&sensor_event_ops, sensor_events,
|
||||
ARRAY_SIZE(sensor_events),
|
||||
sinfo->num_sensors);
|
||||
|
||||
sinfo->version = version;
|
||||
handle->sensor_ops = &sensor_ops;
|
||||
handle->sensor_priv = sinfo;
|
||||
|
@ -21,6 +21,7 @@
|
||||
*
|
||||
* @cinfo: SCMI channel info
|
||||
* @shmem: Transmit/Receive shared memory area
|
||||
* @shmem_lock: Lock to protect access to Tx/Rx shared memory area
|
||||
* @func_id: smc/hvc call function id
|
||||
*/
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_IMX_DSP) += imx-dsp.o
|
||||
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o
|
||||
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o
|
||||
obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/firmware/imx/ipc.h>
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#define IMX_SC_IRQ_FUNC_ENABLE 1
|
||||
#define IMX_SC_IRQ_FUNC_STATUS 2
|
||||
@ -91,6 +92,7 @@ static void imx_scu_irq_work_handler(struct work_struct *work)
|
||||
if (!irq_status)
|
||||
continue;
|
||||
|
||||
pm_system_wakeup();
|
||||
imx_scu_irq_notifier_call_chain(irq_status, &i);
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define IMX_SCU_SOC_DRIVER_NAME "imx-scu-soc"
|
||||
|
||||
static struct imx_sc_ipc *soc_ipc_handle;
|
||||
static struct imx_sc_ipc *imx_sc_soc_ipc_handle;
|
||||
|
||||
struct imx_sc_msg_misc_get_soc_id {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
@ -44,7 +42,7 @@ static int imx_scu_soc_uid(u64 *soc_uid)
|
||||
hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID;
|
||||
hdr->size = 1;
|
||||
|
||||
ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true);
|
||||
ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true);
|
||||
if (ret) {
|
||||
pr_err("%s: get soc uid failed, ret %d\n", __func__, ret);
|
||||
return ret;
|
||||
@ -71,7 +69,7 @@ static int imx_scu_soc_id(void)
|
||||
msg.data.req.control = IMX_SC_C_ID;
|
||||
msg.data.req.resource = IMX_SC_R_SYSTEM;
|
||||
|
||||
ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true);
|
||||
ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true);
|
||||
if (ret) {
|
||||
pr_err("%s: get soc info failed, ret %d\n", __func__, ret);
|
||||
return ret;
|
||||
@ -80,7 +78,7 @@ static int imx_scu_soc_id(void)
|
||||
return msg.data.resp.id;
|
||||
}
|
||||
|
||||
static int imx_scu_soc_probe(struct platform_device *pdev)
|
||||
int imx_scu_soc_init(struct device *dev)
|
||||
{
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
struct soc_device *soc_dev;
|
||||
@ -88,11 +86,11 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
|
||||
u64 uid = 0;
|
||||
u32 val;
|
||||
|
||||
ret = imx_scu_get_handle(&soc_ipc_handle);
|
||||
ret = imx_scu_get_handle(&imx_sc_soc_ipc_handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr),
|
||||
soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr),
|
||||
GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return -ENOMEM;
|
||||
@ -115,73 +113,26 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
|
||||
|
||||
/* format soc_id value passed from SCU firmware */
|
||||
val = id & 0x1f;
|
||||
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val);
|
||||
soc_dev_attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "0x%x", val);
|
||||
if (!soc_dev_attr->soc_id)
|
||||
return -ENOMEM;
|
||||
|
||||
/* format revision value passed from SCU firmware */
|
||||
val = (id >> 5) & 0xf;
|
||||
val = (((val >> 2) + 1) << 4) | (val & 0x3);
|
||||
soc_dev_attr->revision = kasprintf(GFP_KERNEL,
|
||||
"%d.%d",
|
||||
(val >> 4) & 0xf,
|
||||
val & 0xf);
|
||||
if (!soc_dev_attr->revision) {
|
||||
ret = -ENOMEM;
|
||||
goto free_soc_id;
|
||||
}
|
||||
soc_dev_attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d",
|
||||
(val >> 4) & 0xf, val & 0xf);
|
||||
if (!soc_dev_attr->revision)
|
||||
return -ENOMEM;
|
||||
|
||||
soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", uid);
|
||||
if (!soc_dev_attr->serial_number) {
|
||||
ret = -ENOMEM;
|
||||
goto free_revision;
|
||||
}
|
||||
soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"%016llX", uid);
|
||||
if (!soc_dev_attr->serial_number)
|
||||
return -ENOMEM;
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
ret = PTR_ERR(soc_dev);
|
||||
goto free_serial_number;
|
||||
}
|
||||
if (IS_ERR(soc_dev))
|
||||
return PTR_ERR(soc_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
free_serial_number:
|
||||
kfree(soc_dev_attr->serial_number);
|
||||
free_revision:
|
||||
kfree(soc_dev_attr->revision);
|
||||
free_soc_id:
|
||||
kfree(soc_dev_attr->soc_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver imx_scu_soc_driver = {
|
||||
.driver = {
|
||||
.name = IMX_SCU_SOC_DRIVER_NAME,
|
||||
},
|
||||
.probe = imx_scu_soc_probe,
|
||||
};
|
||||
|
||||
static int __init imx_scu_soc_init(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx-scu");
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
ret = platform_driver_register(&imx_scu_soc_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdev = platform_device_register_simple(IMX_SCU_SOC_DRIVER_NAME,
|
||||
-1, NULL, 0);
|
||||
if (IS_ERR(pdev))
|
||||
platform_driver_unregister(&imx_scu_soc_driver);
|
||||
|
||||
return PTR_ERR_OR_ZERO(pdev);
|
||||
}
|
||||
device_initcall(imx_scu_soc_init);
|
@ -328,6 +328,10 @@ static int imx_scu_probe(struct platform_device *pdev)
|
||||
|
||||
imx_sc_ipc_handle = sc_ipc;
|
||||
|
||||
ret = imx_scu_soc_init(dev);
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to initialize SoC info: %d\n", ret);
|
||||
|
||||
ret = imx_scu_enable_general_irq_channel(dev);
|
||||
if (ret)
|
||||
dev_warn(dev,
|
||||
|
45
drivers/firmware/imx/rm.c
Normal file
45
drivers/firmware/imx/rm.c
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2020 NXP
|
||||
*
|
||||
* File containing client-side RPC functions for the RM service. These
|
||||
* function are ported to clients that communicate to the SC.
|
||||
*/
|
||||
|
||||
#include <linux/firmware/imx/svc/rm.h>
|
||||
|
||||
struct imx_sc_msg_rm_rsrc_owned {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
u16 resource;
|
||||
} __packed __aligned(4);
|
||||
|
||||
/*
|
||||
* This function check @resource is owned by current partition or not
|
||||
*
|
||||
* @param[in] ipc IPC handle
|
||||
* @param[in] resource resource the control is associated with
|
||||
*
|
||||
* @return Returns 0 for not owned and 1 for owned.
|
||||
*/
|
||||
bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource)
|
||||
{
|
||||
struct imx_sc_msg_rm_rsrc_owned msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_RM;
|
||||
hdr->func = IMX_SC_RM_FUNC_IS_RESOURCE_OWNED;
|
||||
hdr->size = 2;
|
||||
|
||||
msg.resource = resource;
|
||||
|
||||
/*
|
||||
* SCU firmware only returns value 0 or 1
|
||||
* for resource owned check which means not owned or owned.
|
||||
* So it is always successful.
|
||||
*/
|
||||
imx_scu_call_rpc(ipc, &msg, true);
|
||||
|
||||
return hdr->func;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_sc_rm_is_resource_owned);
|
@ -167,8 +167,18 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
||||
{ "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 },
|
||||
|
||||
/* CM40 SS */
|
||||
{ "cm40_i2c", IMX_SC_R_M4_0_I2C, 1, 0 },
|
||||
{ "cm40_intmux", IMX_SC_R_M4_0_INTMUX, 1, 0 },
|
||||
{ "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 },
|
||||
{ "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 },
|
||||
{ "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0},
|
||||
{ "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0},
|
||||
{ "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0},
|
||||
|
||||
/* CM41 SS */
|
||||
{ "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 },
|
||||
{ "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 },
|
||||
{ "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0},
|
||||
{ "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0},
|
||||
{ "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0},
|
||||
};
|
||||
|
||||
static const struct imx_sc_pd_soc imx8qxp_scu_pd = {
|
||||
|
@ -391,7 +391,7 @@ static int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
|
||||
|
||||
desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0;
|
||||
|
||||
return qcom_scm_call(__scm->dev, &desc, NULL);
|
||||
return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
|
||||
}
|
||||
|
||||
static void qcom_scm_set_download_mode(bool enable)
|
||||
@ -650,7 +650,7 @@ int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val)
|
||||
int ret;
|
||||
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
ret = qcom_scm_call_atomic(__scm->dev, &desc, &res);
|
||||
if (ret >= 0)
|
||||
*val = res.result[0];
|
||||
|
||||
@ -669,8 +669,7 @@ int qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
|
||||
|
||||
return qcom_scm_call(__scm->dev, &desc, NULL);
|
||||
return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_io_writel);
|
||||
|
||||
@ -1151,6 +1150,7 @@ static const struct of_device_id qcom_scm_dt_match[] = {
|
||||
SCM_HAS_IFACE_CLK |
|
||||
SCM_HAS_BUS_CLK)
|
||||
},
|
||||
{ .compatible = "qcom,scm-msm8994" },
|
||||
{ .compatible = "qcom,scm-msm8996" },
|
||||
{ .compatible = "qcom,scm" },
|
||||
{}
|
||||
|
@ -14,3 +14,12 @@ config HAVE_ARM_SMCCC_DISCOVERY
|
||||
to add SMCCC discovery mechanism though the PSCI firmware
|
||||
implementation of PSCI_FEATURES(SMCCC_VERSION) which returns
|
||||
success on firmware compliant to SMCCC v1.1 and above.
|
||||
|
||||
config ARM_SMCCC_SOC_ID
|
||||
bool "SoC bus device for the ARM SMCCC SOC_ID"
|
||||
depends on HAVE_ARM_SMCCC_DISCOVERY
|
||||
default y
|
||||
select SOC_BUS
|
||||
help
|
||||
Include support for the SoC bus on the ARM SMCCC firmware based
|
||||
platforms providing some sysfs information about the SoC variant.
|
||||
|
@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o
|
||||
obj-$(CONFIG_ARM_SMCCC_SOC_ID) += soc_id.o
|
||||
|
114
drivers/firmware/smccc/soc_id.c
Normal file
114
drivers/firmware/smccc/soc_id.c
Normal file
@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2020 Arm Limited
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SMCCC: SOC_ID: " fmt
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
#define SMCCC_SOC_ID_JEP106_BANK_IDX_MASK GENMASK(30, 24)
|
||||
/*
|
||||
* As per the SMC Calling Convention specification v1.2 (ARM DEN 0028C)
|
||||
* Section 7.4 SMCCC_ARCH_SOC_ID bits[23:16] are JEP-106 identification
|
||||
* code with parity bit for the SiP. We can drop the parity bit.
|
||||
*/
|
||||
#define SMCCC_SOC_ID_JEP106_ID_CODE_MASK GENMASK(22, 16)
|
||||
#define SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK GENMASK(15, 0)
|
||||
|
||||
#define JEP106_BANK_CONT_CODE(x) \
|
||||
(u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_BANK_IDX_MASK, (x)))
|
||||
#define JEP106_ID_CODE(x) \
|
||||
(u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_ID_CODE_MASK, (x)))
|
||||
#define IMP_DEF_SOC_ID(x) \
|
||||
(u16)(FIELD_GET(SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK, (x)))
|
||||
|
||||
static struct soc_device *soc_dev;
|
||||
static struct soc_device_attribute *soc_dev_attr;
|
||||
|
||||
static int __init smccc_soc_init(void)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
int soc_id_rev, soc_id_version;
|
||||
static char soc_id_str[20], soc_id_rev_str[12];
|
||||
static char soc_id_jep106_id_str[12];
|
||||
|
||||
if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
|
||||
return 0;
|
||||
|
||||
if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) {
|
||||
pr_err("%s: invalid SMCCC conduit\n", __func__);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
|
||||
ARM_SMCCC_ARCH_SOC_ID, &res);
|
||||
|
||||
if (res.a0 == SMCCC_RET_NOT_SUPPORTED) {
|
||||
pr_info("ARCH_SOC_ID not implemented, skipping ....\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((int)res.a0 < 0) {
|
||||
pr_info("ARCH_FEATURES(ARCH_SOC_ID) returned error: %lx\n",
|
||||
res.a0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res);
|
||||
if ((int)res.a0 < 0) {
|
||||
pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
soc_id_version = res.a0;
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
|
||||
if ((int)res.a0 < 0) {
|
||||
pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
soc_id_rev = res.a0;
|
||||
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
sprintf(soc_id_rev_str, "0x%08x", soc_id_rev);
|
||||
sprintf(soc_id_jep106_id_str, "jep106:%02x%02x",
|
||||
JEP106_BANK_CONT_CODE(soc_id_version),
|
||||
JEP106_ID_CODE(soc_id_version));
|
||||
sprintf(soc_id_str, "%s:%04x", soc_id_jep106_id_str,
|
||||
IMP_DEF_SOC_ID(soc_id_version));
|
||||
|
||||
soc_dev_attr->soc_id = soc_id_str;
|
||||
soc_dev_attr->revision = soc_id_rev_str;
|
||||
soc_dev_attr->family = soc_id_jep106_id_str;
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
kfree(soc_dev_attr);
|
||||
return PTR_ERR(soc_dev);
|
||||
}
|
||||
|
||||
pr_info("ID = %s Revision = %s\n", soc_dev_attr->soc_id,
|
||||
soc_dev_attr->revision);
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(smccc_soc_init);
|
||||
|
||||
static void __exit smccc_soc_exit(void)
|
||||
{
|
||||
if (soc_dev)
|
||||
soc_device_unregister(soc_dev);
|
||||
kfree(soc_dev_attr);
|
||||
}
|
||||
module_exit(smccc_soc_exit);
|
@ -4,11 +4,14 @@
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include <soc/tegra/bpmp-abi.h>
|
||||
|
||||
static DEFINE_MUTEX(bpmp_debug_lock);
|
||||
|
||||
struct seqbuf {
|
||||
char *buf;
|
||||
size_t pos;
|
||||
@ -96,6 +99,354 @@ static const char *get_filename(struct tegra_bpmp *bpmp,
|
||||
return filename;
|
||||
}
|
||||
|
||||
static int mrq_debug_open(struct tegra_bpmp *bpmp, const char *name,
|
||||
uint32_t *fd, uint32_t *len, bool write)
|
||||
{
|
||||
struct mrq_debug_request req = {
|
||||
.cmd = cpu_to_le32(write ? CMD_DEBUG_OPEN_WO : CMD_DEBUG_OPEN_RO),
|
||||
};
|
||||
struct mrq_debug_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUG,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
ssize_t sz_name;
|
||||
int err = 0;
|
||||
|
||||
sz_name = strscpy(req.fop.name, name, sizeof(req.fop.name));
|
||||
if (sz_name < 0) {
|
||||
pr_err("File name too large: %s\n", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (msg.rx.ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*len = resp.fop.datalen;
|
||||
*fd = resp.fop.fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrq_debug_close(struct tegra_bpmp *bpmp, uint32_t fd)
|
||||
{
|
||||
struct mrq_debug_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUG_CLOSE),
|
||||
.frd = {
|
||||
.fd = fd,
|
||||
},
|
||||
};
|
||||
struct mrq_debug_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUG,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
int err = 0;
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (msg.rx.ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrq_debug_read(struct tegra_bpmp *bpmp, const char *name,
|
||||
char *data, size_t sz_data, uint32_t *nbytes)
|
||||
{
|
||||
struct mrq_debug_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUG_READ),
|
||||
};
|
||||
struct mrq_debug_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUG,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
uint32_t fd = 0, len = 0;
|
||||
int remaining, err;
|
||||
|
||||
mutex_lock(&bpmp_debug_lock);
|
||||
err = mrq_debug_open(bpmp, name, &fd, &len, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (len > sz_data) {
|
||||
err = -EFBIG;
|
||||
goto close;
|
||||
}
|
||||
|
||||
req.frd.fd = fd;
|
||||
remaining = len;
|
||||
|
||||
while (remaining > 0) {
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0) {
|
||||
goto close;
|
||||
} else if (msg.rx.ret < 0) {
|
||||
err = -EINVAL;
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (resp.frd.readlen > remaining) {
|
||||
pr_err("%s: read data length invalid\n", __func__);
|
||||
err = -EINVAL;
|
||||
goto close;
|
||||
}
|
||||
|
||||
memcpy(data, resp.frd.data, resp.frd.readlen);
|
||||
data += resp.frd.readlen;
|
||||
remaining -= resp.frd.readlen;
|
||||
}
|
||||
|
||||
*nbytes = len;
|
||||
|
||||
close:
|
||||
err = mrq_debug_close(bpmp, fd);
|
||||
out:
|
||||
mutex_unlock(&bpmp_debug_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrq_debug_write(struct tegra_bpmp *bpmp, const char *name,
|
||||
uint8_t *data, size_t sz_data)
|
||||
{
|
||||
struct mrq_debug_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUG_WRITE)
|
||||
};
|
||||
struct mrq_debug_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUG,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
uint32_t fd = 0, len = 0;
|
||||
size_t remaining;
|
||||
int err;
|
||||
|
||||
mutex_lock(&bpmp_debug_lock);
|
||||
err = mrq_debug_open(bpmp, name, &fd, &len, 1);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (sz_data > len) {
|
||||
err = -EINVAL;
|
||||
goto close;
|
||||
}
|
||||
|
||||
req.fwr.fd = fd;
|
||||
remaining = sz_data;
|
||||
|
||||
while (remaining > 0) {
|
||||
len = min(remaining, sizeof(req.fwr.data));
|
||||
memcpy(req.fwr.data, data, len);
|
||||
req.fwr.datalen = len;
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0) {
|
||||
goto close;
|
||||
} else if (msg.rx.ret < 0) {
|
||||
err = -EINVAL;
|
||||
goto close;
|
||||
}
|
||||
|
||||
data += req.fwr.datalen;
|
||||
remaining -= req.fwr.datalen;
|
||||
}
|
||||
|
||||
close:
|
||||
err = mrq_debug_close(bpmp, fd);
|
||||
out:
|
||||
mutex_unlock(&bpmp_debug_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bpmp_debug_show(struct seq_file *m, void *p)
|
||||
{
|
||||
struct file *file = m->private;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct tegra_bpmp *bpmp = inode->i_private;
|
||||
char *databuf = NULL;
|
||||
char fnamebuf[256];
|
||||
const char *filename;
|
||||
uint32_t nbytes = 0;
|
||||
size_t len;
|
||||
int err;
|
||||
|
||||
len = seq_get_buf(m, &databuf);
|
||||
if (!databuf)
|
||||
return -ENOMEM;
|
||||
|
||||
filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
|
||||
if (!filename)
|
||||
return -ENOENT;
|
||||
|
||||
err = mrq_debug_read(bpmp, filename, databuf, len, &nbytes);
|
||||
if (!err)
|
||||
seq_commit(m, nbytes);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t bpmp_debug_store(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct tegra_bpmp *bpmp = inode->i_private;
|
||||
char *databuf = NULL;
|
||||
char fnamebuf[256];
|
||||
const char *filename;
|
||||
ssize_t err;
|
||||
|
||||
filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
|
||||
if (!filename)
|
||||
return -ENOENT;
|
||||
|
||||
databuf = kmalloc(count, GFP_KERNEL);
|
||||
if (!databuf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(databuf, buf, count)) {
|
||||
err = -EFAULT;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
err = mrq_debug_write(bpmp, filename, databuf, count);
|
||||
|
||||
free_ret:
|
||||
kfree(databuf);
|
||||
|
||||
return err ?: count;
|
||||
}
|
||||
|
||||
static int bpmp_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open_size(file, bpmp_debug_show, file, SZ_256K);
|
||||
}
|
||||
|
||||
static const struct file_operations bpmp_debug_fops = {
|
||||
.open = bpmp_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = bpmp_debug_store,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int bpmp_populate_debugfs_inband(struct tegra_bpmp *bpmp,
|
||||
struct dentry *parent,
|
||||
char *ppath)
|
||||
{
|
||||
const size_t pathlen = SZ_256;
|
||||
const size_t bufsize = SZ_16K;
|
||||
uint32_t dsize, attrs = 0;
|
||||
struct dentry *dentry;
|
||||
struct seqbuf seqbuf;
|
||||
char *buf, *pathbuf;
|
||||
const char *name;
|
||||
int err = 0;
|
||||
|
||||
if (!bpmp || !parent || !ppath)
|
||||
return -EINVAL;
|
||||
|
||||
buf = kmalloc(bufsize, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pathbuf = kzalloc(pathlen, GFP_KERNEL);
|
||||
if (!pathbuf) {
|
||||
kfree(buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = mrq_debug_read(bpmp, ppath, buf, bufsize, &dsize);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
seqbuf_init(&seqbuf, buf, dsize);
|
||||
|
||||
while (!seqbuf_eof(&seqbuf)) {
|
||||
err = seqbuf_read_u32(&seqbuf, &attrs);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = seqbuf_read_str(&seqbuf, &name);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (attrs & DEBUGFS_S_ISDIR) {
|
||||
size_t len;
|
||||
|
||||
dentry = debugfs_create_dir(name, parent);
|
||||
if (IS_ERR(dentry)) {
|
||||
err = PTR_ERR(dentry);
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = strlen(ppath) + strlen(name) + 1;
|
||||
if (len >= pathlen) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
strncpy(pathbuf, ppath, pathlen);
|
||||
strncat(pathbuf, name, strlen(name));
|
||||
strcat(pathbuf, "/");
|
||||
|
||||
err = bpmp_populate_debugfs_inband(bpmp, dentry,
|
||||
pathbuf);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
} else {
|
||||
umode_t mode;
|
||||
|
||||
mode = attrs & DEBUGFS_S_IRUSR ? 0400 : 0;
|
||||
mode |= attrs & DEBUGFS_S_IWUSR ? 0200 : 0;
|
||||
dentry = debugfs_create_file(name, mode, parent, bpmp,
|
||||
&bpmp_debug_fops);
|
||||
if (!dentry) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(pathbuf);
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrq_debugfs_read(struct tegra_bpmp *bpmp,
|
||||
dma_addr_t name, size_t sz_name,
|
||||
dma_addr_t data, size_t sz_data,
|
||||
@ -127,6 +478,8 @@ static int mrq_debugfs_read(struct tegra_bpmp *bpmp,
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (msg.rx.ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*nbytes = (size_t)resp.fop.nbytes;
|
||||
|
||||
@ -184,6 +537,8 @@ static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr,
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (msg.rx.ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*nbytes = (size_t)resp.dumpdir.nbytes;
|
||||
|
||||
@ -202,7 +557,7 @@ static int debugfs_show(struct seq_file *m, void *p)
|
||||
char buf[256];
|
||||
const char *filename;
|
||||
size_t len, nbytes;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
filename = get_filename(bpmp, file, buf, sizeof(buf));
|
||||
if (!filename)
|
||||
@ -216,24 +571,24 @@ static int debugfs_show(struct seq_file *m, void *p)
|
||||
datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!datavirt) {
|
||||
ret = -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto free_namebuf;
|
||||
}
|
||||
|
||||
len = strlen(filename);
|
||||
strncpy(namevirt, filename, namesize);
|
||||
|
||||
ret = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize,
|
||||
err = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize,
|
||||
&nbytes);
|
||||
|
||||
if (!ret)
|
||||
if (!err)
|
||||
seq_write(m, datavirt, nbytes);
|
||||
|
||||
dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
|
||||
free_namebuf:
|
||||
dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int debugfs_open(struct inode *inode, struct file *file)
|
||||
@ -253,7 +608,7 @@ static ssize_t debugfs_store(struct file *file, const char __user *buf,
|
||||
char fnamebuf[256];
|
||||
const char *filename;
|
||||
size_t len;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
|
||||
if (!filename)
|
||||
@ -267,7 +622,7 @@ static ssize_t debugfs_store(struct file *file, const char __user *buf,
|
||||
datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!datavirt) {
|
||||
ret = -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto free_namebuf;
|
||||
}
|
||||
|
||||
@ -275,11 +630,11 @@ static ssize_t debugfs_store(struct file *file, const char __user *buf,
|
||||
strncpy(namevirt, filename, namesize);
|
||||
|
||||
if (copy_from_user(datavirt, buf, count)) {
|
||||
ret = -EFAULT;
|
||||
err = -EFAULT;
|
||||
goto free_databuf;
|
||||
}
|
||||
|
||||
ret = mrq_debugfs_write(bpmp, namephys, len, dataphys,
|
||||
err = mrq_debugfs_write(bpmp, namephys, len, dataphys,
|
||||
count);
|
||||
|
||||
free_databuf:
|
||||
@ -287,7 +642,7 @@ free_databuf:
|
||||
free_namebuf:
|
||||
dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
|
||||
|
||||
return ret ?: count;
|
||||
return err ?: count;
|
||||
}
|
||||
|
||||
static const struct file_operations debugfs_fops = {
|
||||
@ -350,59 +705,66 @@ static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf,
|
||||
size_t bufsize, struct dentry *root)
|
||||
static int bpmp_populate_debugfs_shmem(struct tegra_bpmp *bpmp)
|
||||
{
|
||||
struct seqbuf seqbuf;
|
||||
const size_t sz = SZ_512K;
|
||||
dma_addr_t phys;
|
||||
size_t nbytes;
|
||||
void *virt;
|
||||
int err;
|
||||
|
||||
bpmp->debugfs_mirror = debugfs_create_dir("debug", root);
|
||||
if (!bpmp->debugfs_mirror)
|
||||
virt = dma_alloc_coherent(bpmp->dev, sz, &phys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!virt)
|
||||
return -ENOMEM;
|
||||
|
||||
seqbuf_init(&seqbuf, buf, bufsize);
|
||||
err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0);
|
||||
err = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes);
|
||||
if (err < 0) {
|
||||
debugfs_remove_recursive(bpmp->debugfs_mirror);
|
||||
bpmp->debugfs_mirror = NULL;
|
||||
goto free;
|
||||
} else if (nbytes > sz) {
|
||||
err = -EINVAL;
|
||||
goto free;
|
||||
}
|
||||
|
||||
seqbuf_init(&seqbuf, virt, nbytes);
|
||||
err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0);
|
||||
free:
|
||||
dma_free_coherent(bpmp->dev, sz, virt, phys);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
|
||||
{
|
||||
dma_addr_t phys;
|
||||
void *virt;
|
||||
const size_t sz = SZ_256K;
|
||||
size_t nbytes;
|
||||
int ret;
|
||||
struct dentry *root;
|
||||
bool inband;
|
||||
int err;
|
||||
|
||||
if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))
|
||||
inband = tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUG);
|
||||
|
||||
if (!inband && !tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))
|
||||
return 0;
|
||||
|
||||
root = debugfs_create_dir("bpmp", NULL);
|
||||
if (!root)
|
||||
return -ENOMEM;
|
||||
|
||||
virt = dma_alloc_coherent(bpmp->dev, sz, &phys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!virt) {
|
||||
ret = -ENOMEM;
|
||||
bpmp->debugfs_mirror = debugfs_create_dir("debug", root);
|
||||
if (!bpmp->debugfs_mirror) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes);
|
||||
if (ret < 0)
|
||||
goto free;
|
||||
if (inband)
|
||||
err = bpmp_populate_debugfs_inband(bpmp, bpmp->debugfs_mirror,
|
||||
"/");
|
||||
else
|
||||
err = bpmp_populate_debugfs_shmem(bpmp);
|
||||
|
||||
ret = create_debugfs_mirror(bpmp, virt, nbytes, root);
|
||||
free:
|
||||
dma_free_coherent(bpmp->dev, sz, virt, phys);
|
||||
out:
|
||||
if (ret < 0)
|
||||
debugfs_remove(root);
|
||||
if (err < 0)
|
||||
debugfs_remove_recursive(root);
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
@ -515,10 +515,10 @@ bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq)
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
ret = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (ret || msg.rx.ret)
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err || msg.rx.ret)
|
||||
return false;
|
||||
|
||||
return resp.status == 0;
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Texas Instruments System Control Interface Protocol Driver
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Nishanth Menon
|
||||
*/
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* The system works in a message response protocol
|
||||
* See: http://processors.wiki.ti.com/index.php/TISCI for details
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
|
||||
*/
|
||||
|
||||
#ifndef __TI_SCI_H
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/armada-37xx-rwtm-mailbox.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
@ -69,6 +70,18 @@ struct mox_rwtm {
|
||||
/* public key burned in eFuse */
|
||||
int has_pubkey;
|
||||
u8 pubkey[135];
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
/*
|
||||
* Signature process. This is currently done via debugfs, because it
|
||||
* does not conform to the sysfs standard "one file per attribute".
|
||||
* It should be rewritten via crypto API once akcipher API is available
|
||||
* from userspace.
|
||||
*/
|
||||
struct dentry *debugfs_root;
|
||||
u32 last_sig[34];
|
||||
int last_sig_done;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mox_kobject {
|
||||
@ -279,6 +292,152 @@ unlock_mutex:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int rwtm_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct mox_rwtm *rwtm = file->private_data;
|
||||
ssize_t ret;
|
||||
|
||||
/* only allow one read, of 136 bytes, from position 0 */
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
if (len < 136)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rwtm->last_sig_done)
|
||||
return -ENODATA;
|
||||
|
||||
/* 2 arrays of 17 32-bit words are 136 bytes */
|
||||
ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig, 136);
|
||||
rwtm->last_sig_done = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t do_sign_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
struct mox_rwtm *rwtm = file->private_data;
|
||||
struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
loff_t dummy = 0;
|
||||
ssize_t ret;
|
||||
|
||||
/* the input is a SHA-512 hash, so exactly 64 bytes have to be read */
|
||||
if (len != 64)
|
||||
return -EINVAL;
|
||||
|
||||
/* if last result is not zero user has not read that information yet */
|
||||
if (rwtm->last_sig_done)
|
||||
return -EBUSY;
|
||||
|
||||
if (!mutex_trylock(&rwtm->busy))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* Here we have to send:
|
||||
* 1. Address of the input to sign.
|
||||
* The input is an array of 17 32-bit words, the first (most
|
||||
* significat) is 0, the rest 16 words are copied from the SHA-512
|
||||
* hash given by the user and converted from BE to LE.
|
||||
* 2. Address of the buffer where ECDSA signature value R shall be
|
||||
* stored by the rWTM firmware.
|
||||
* 3. Address of the buffer where ECDSA signature value S shall be
|
||||
* stored by the rWTM firmware.
|
||||
*/
|
||||
memset(rwtm->buf, 0, 4);
|
||||
ret = simple_write_to_buffer(rwtm->buf + 4, 64, &dummy, buf, len);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
be32_to_cpu_array(rwtm->buf, rwtm->buf, 17);
|
||||
|
||||
msg.command = MBOX_CMD_SIGN;
|
||||
msg.args[0] = 1;
|
||||
msg.args[1] = rwtm->buf_phys;
|
||||
msg.args[2] = rwtm->buf_phys + 68;
|
||||
msg.args[3] = rwtm->buf_phys + 2 * 68;
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
ret = wait_for_completion_interruptible(&rwtm->cmd_done);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
ret = MBOX_STS_VALUE(reply->retval);
|
||||
if (MBOX_STS_ERROR(reply->retval) != MBOX_STS_SUCCESS)
|
||||
goto unlock_mutex;
|
||||
|
||||
/*
|
||||
* Here we read the R and S values of the ECDSA signature
|
||||
* computed by the rWTM firmware and convert their words from
|
||||
* LE to BE.
|
||||
*/
|
||||
memcpy(rwtm->last_sig, rwtm->buf + 68, 136);
|
||||
cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig, 34);
|
||||
rwtm->last_sig_done = 1;
|
||||
|
||||
mutex_unlock(&rwtm->busy);
|
||||
return len;
|
||||
unlock_mutex:
|
||||
mutex_unlock(&rwtm->busy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations do_sign_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rwtm_debug_open,
|
||||
.read = do_sign_read,
|
||||
.write = do_sign_write,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static int rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
struct dentry *root, *entry;
|
||||
|
||||
root = debugfs_create_dir("turris-mox-rwtm", NULL);
|
||||
|
||||
if (IS_ERR(root))
|
||||
return PTR_ERR(root);
|
||||
|
||||
entry = debugfs_create_file_unsafe("do_sign", 0600, root, rwtm,
|
||||
&do_sign_fops);
|
||||
if (IS_ERR(entry))
|
||||
goto err_remove;
|
||||
|
||||
rwtm->debugfs_root = root;
|
||||
|
||||
return 0;
|
||||
err_remove:
|
||||
debugfs_remove_recursive(root);
|
||||
return PTR_ERR(entry);
|
||||
}
|
||||
|
||||
static void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
debugfs_remove_recursive(rwtm->debugfs_root);
|
||||
}
|
||||
#else
|
||||
static inline int rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mox_rwtm *rwtm;
|
||||
@ -340,6 +499,12 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
goto free_channel;
|
||||
}
|
||||
|
||||
ret = rwtm_register_debugfs(rwtm);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed creating debugfs entries: %i\n", ret);
|
||||
goto free_channel;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_channel:
|
||||
@ -355,6 +520,7 @@ static int turris_mox_rwtm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mox_rwtm *rwtm = platform_get_drvdata(pdev);
|
||||
|
||||
rwtm_unregister_debugfs(rwtm);
|
||||
sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
|
||||
kobject_put(rwtm_to_kobj(rwtm));
|
||||
mbox_free_channel(rwtm->mbox);
|
||||
|
@ -487,6 +487,7 @@ static void mtk_drm_crtc_hw_config(struct mtk_drm_crtc *mtk_crtc)
|
||||
cmdq_pkt_clear_event(cmdq_handle, mtk_crtc->cmdq_event);
|
||||
cmdq_pkt_wfe(cmdq_handle, mtk_crtc->cmdq_event);
|
||||
mtk_crtc_ddp_config(crtc, cmdq_handle);
|
||||
cmdq_pkt_finalize(cmdq_handle);
|
||||
cmdq_pkt_flush_async(cmdq_handle, ddp_cmdq_cb, cmdq_handle);
|
||||
}
|
||||
#endif
|
||||
|
@ -559,6 +559,22 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
||||
gi2c->adap.dev.of_node = dev->of_node;
|
||||
strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
|
||||
|
||||
ret = geni_icc_get(&gi2c->se, "qup-memory");
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Set the bus quota for core and cpu to a reasonable value for
|
||||
* register access.
|
||||
* Set quota for DDR based on bus speed.
|
||||
*/
|
||||
gi2c->se.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW;
|
||||
gi2c->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
|
||||
gi2c->se.icc_paths[GENI_TO_DDR].avg_bw = Bps_to_icc(gi2c->clk_freq_out);
|
||||
|
||||
ret = geni_icc_set_bw(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = geni_se_resources_on(&gi2c->se);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error turning on resources %d\n", ret);
|
||||
@ -581,6 +597,10 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = geni_icc_disable(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
|
||||
|
||||
gi2c->suspended = 1;
|
||||
@ -625,7 +645,7 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
|
||||
gi2c->suspended = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return geni_icc_disable(&gi2c->se);
|
||||
}
|
||||
|
||||
static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
|
||||
@ -633,6 +653,10 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
|
||||
int ret;
|
||||
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
|
||||
|
||||
ret = geni_icc_enable(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = geni_se_resources_on(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -266,11 +266,7 @@ int qcom_icc_bcm_voter_commit(struct bcm_voter *voter)
|
||||
if (!commit_idx[0])
|
||||
goto out;
|
||||
|
||||
ret = rpmh_invalidate(voter->dev);
|
||||
if (ret) {
|
||||
pr_err("Error invalidating RPMH client (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
rpmh_invalidate(voter->dev);
|
||||
|
||||
ret = rpmh_write_batch(voter->dev, RPMH_ACTIVE_ONLY_STATE,
|
||||
cmds, commit_idx);
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Texas Instruments' K3 Interrupt Aggregator irqchip driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2018-2019 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Lokesh Vutla <lokeshvutla@ti.com>
|
||||
*/
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Texas Instruments' K3 Interrupt Router irqchip driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2018-2019 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Lokesh Vutla <lokeshvutla@ti.com>
|
||||
*/
|
||||
|
||||
|
@ -5,6 +5,12 @@
|
||||
|
||||
menuconfig MEMORY
|
||||
bool "Memory Controller drivers"
|
||||
help
|
||||
This option allows to enable specific memory controller drivers,
|
||||
useful mostly on embedded systems. These could be controllers
|
||||
for DRAM (SDR, DDR), ROM, SRAM and others. The drivers features
|
||||
vary from memory tuning and frequency scaling to enabling
|
||||
access to attached peripherals through memory bus.
|
||||
|
||||
if MEMORY
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
* - BE kernel + LE firmware image
|
||||
* - BE kernel + BE firmware image
|
||||
*
|
||||
* The DPCU always runs in big endian mode. The firwmare image, however, can
|
||||
* The DPCU always runs in big endian mode. The firmware image, however, can
|
||||
* be in either format. Also, communication between host CPU and DCPU is
|
||||
* always in little endian.
|
||||
*/
|
||||
@ -188,7 +188,7 @@ struct brcmstb_dpfe_priv {
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const char *error_text[] = {
|
||||
static const char * const error_text[] = {
|
||||
"Success", "Header code incorrect", "Unknown command or argument",
|
||||
"Incorrect checksum", "Malformed command", "Timed out",
|
||||
};
|
||||
@ -379,9 +379,8 @@ static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response,
|
||||
void __iomem *ptr = NULL;
|
||||
|
||||
/* There is no need to use this function for API v3 or later. */
|
||||
if (unlikely(priv->dpfe_api->version >= 3)) {
|
||||
if (unlikely(priv->dpfe_api->version >= 3))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK;
|
||||
offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK;
|
||||
|
@ -66,6 +66,7 @@ struct l2_ctl_device_attribute {
|
||||
struct device_attribute dev_attr;
|
||||
enum l2_ctl_stall id;
|
||||
};
|
||||
|
||||
#define to_l2_ctl_dev_attr(_dev_attr) \
|
||||
container_of(_dev_attr, struct l2_ctl_device_attribute, dev_attr)
|
||||
|
||||
@ -242,6 +243,7 @@ static ssize_t l2_ctl_latency_store(struct device *dev,
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static L2_CTL_ATTR_RW(l2_ws_latency, l2_ctl_latency, L2_WS_STALL);
|
||||
static L2_CTL_ATTR_RW(l2_tag_latency, l2_ctl_latency, L2_TAG_STALL);
|
||||
static L2_CTL_ATTR_RW(l2_data_latency, l2_ctl_latency, L2_DATA_STALL);
|
||||
|
@ -102,14 +102,12 @@ static int da8xx_ddrctl_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct da8xx_ddrctl_config_knob *knob;
|
||||
const struct da8xx_ddrctl_setting *setting;
|
||||
struct device_node *node;
|
||||
struct resource *res;
|
||||
void __iomem *ddrctl;
|
||||
struct device *dev;
|
||||
u32 reg;
|
||||
|
||||
dev = &pdev->dev;
|
||||
node = dev->of_node;
|
||||
|
||||
setting = da8xx_ddrctl_get_board_settings();
|
||||
if (!setting) {
|
||||
|
@ -1,16 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* TI AM33XX EMIF PM Assembly Offsets
|
||||
*
|
||||
* Copyright (C) 2016-2017 Texas Instruments Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/ti-emif-sram.h>
|
||||
|
||||
|
@ -282,10 +282,9 @@ static void set_lpmode(struct emif_data *emif, u8 lpmode)
|
||||
* the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field to 0x4.
|
||||
*/
|
||||
if ((emif->plat_data->ip_rev == EMIF_4D) &&
|
||||
(EMIF_LP_MODE_PWR_DN == lpmode)) {
|
||||
(lpmode == EMIF_LP_MODE_PWR_DN)) {
|
||||
WARN_ONCE(1,
|
||||
"REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by"
|
||||
"erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n");
|
||||
"REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n");
|
||||
/* rollback LP_MODE to Self-refresh mode */
|
||||
lpmode = EMIF_LP_MODE_SELF_REFRESH;
|
||||
}
|
||||
@ -714,7 +713,7 @@ static u32 get_ext_phy_ctrl_2_intelliphy_4d5(void)
|
||||
u32 fifo_we_slave_ratio;
|
||||
|
||||
fifo_we_slave_ratio = DIV_ROUND_CLOSEST(
|
||||
EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck);
|
||||
EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256, t_ck);
|
||||
|
||||
return fifo_we_slave_ratio | fifo_we_slave_ratio << 11 |
|
||||
fifo_we_slave_ratio << 22;
|
||||
@ -725,7 +724,7 @@ static u32 get_ext_phy_ctrl_3_intelliphy_4d5(void)
|
||||
u32 fifo_we_slave_ratio;
|
||||
|
||||
fifo_we_slave_ratio = DIV_ROUND_CLOSEST(
|
||||
EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck);
|
||||
EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256, t_ck);
|
||||
|
||||
return fifo_we_slave_ratio >> 10 | fifo_we_slave_ratio << 1 |
|
||||
fifo_we_slave_ratio << 12 | fifo_we_slave_ratio << 23;
|
||||
@ -736,7 +735,7 @@ static u32 get_ext_phy_ctrl_4_intelliphy_4d5(void)
|
||||
u32 fifo_we_slave_ratio;
|
||||
|
||||
fifo_we_slave_ratio = DIV_ROUND_CLOSEST(
|
||||
EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck);
|
||||
EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256, t_ck);
|
||||
|
||||
return fifo_we_slave_ratio >> 9 | fifo_we_slave_ratio << 2 |
|
||||
fifo_we_slave_ratio << 13;
|
||||
@ -975,8 +974,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
|
||||
EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART)) {
|
||||
if (emif->temperature_level >= SDRAM_TEMP_HIGH_DERATE_REFRESH) {
|
||||
dev_err(emif->dev,
|
||||
"%s:NOT Extended temperature capable memory."
|
||||
"Converting MR4=0x%02x as shutdown event\n",
|
||||
"%s:NOT Extended temperature capable memory. Converting MR4=0x%02x as shutdown event\n",
|
||||
__func__, emif->temperature_level);
|
||||
/*
|
||||
* Temperature far too high - do kernel_power_off()
|
||||
@ -1318,9 +1316,9 @@ static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
|
||||
if (of_find_property(np_emif, "cal-resistor-per-cs", &len))
|
||||
dev_info->cal_resistors_per_cs = true;
|
||||
|
||||
if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s4"))
|
||||
if (of_device_is_compatible(np_ddr, "jedec,lpddr2-s4"))
|
||||
dev_info->type = DDR_TYPE_LPDDR2_S4;
|
||||
else if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s2"))
|
||||
else if (of_device_is_compatible(np_ddr, "jedec,lpddr2-s2"))
|
||||
dev_info->type = DDR_TYPE_LPDDR2_S2;
|
||||
|
||||
of_property_read_u32(np_ddr, "density", &density);
|
||||
@ -1563,11 +1561,8 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
|
||||
goto error;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(emif->dev, "%s: error getting IRQ resource - %d\n",
|
||||
__func__, irq);
|
||||
if (irq < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
emif_onetime_settings(emif);
|
||||
emif_debugfs_init(emif);
|
||||
|
@ -53,6 +53,7 @@ int fsl_ifc_find(phys_addr_t addr_base)
|
||||
|
||||
for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
|
||||
u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr);
|
||||
|
||||
if (cspr & CSPR_V && (cspr & CSPR_BA) ==
|
||||
convert_ifc_address(addr_base))
|
||||
return i;
|
||||
@ -153,8 +154,8 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
|
||||
/* read for chip select error */
|
||||
cs_err = ifc_in32(&ifc->cm_evter_stat);
|
||||
if (cs_err) {
|
||||
dev_err(ctrl->dev, "transaction sent to IFC is not mapped to"
|
||||
"any memory bank 0x%08X\n", cs_err);
|
||||
dev_err(ctrl->dev, "transaction sent to IFC is not mapped to any memory bank 0x%08X\n",
|
||||
cs_err);
|
||||
/* clear the chip select error */
|
||||
ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
|
||||
|
||||
@ -163,24 +164,24 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
|
||||
err_addr = ifc_in32(&ifc->cm_erattr1);
|
||||
|
||||
if (status & IFC_CM_ERATTR0_ERTYP_READ)
|
||||
dev_err(ctrl->dev, "Read transaction error"
|
||||
"CM_ERATTR0 0x%08X\n", status);
|
||||
dev_err(ctrl->dev, "Read transaction error CM_ERATTR0 0x%08X\n",
|
||||
status);
|
||||
else
|
||||
dev_err(ctrl->dev, "Write transaction error"
|
||||
"CM_ERATTR0 0x%08X\n", status);
|
||||
dev_err(ctrl->dev, "Write transaction error CM_ERATTR0 0x%08X\n",
|
||||
status);
|
||||
|
||||
err_axiid = (status & IFC_CM_ERATTR0_ERAID) >>
|
||||
IFC_CM_ERATTR0_ERAID_SHIFT;
|
||||
dev_err(ctrl->dev, "AXI ID of the error"
|
||||
"transaction 0x%08X\n", err_axiid);
|
||||
dev_err(ctrl->dev, "AXI ID of the error transaction 0x%08X\n",
|
||||
err_axiid);
|
||||
|
||||
err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >>
|
||||
IFC_CM_ERATTR0_ESRCID_SHIFT;
|
||||
dev_err(ctrl->dev, "SRC ID of the error"
|
||||
"transaction 0x%08X\n", err_srcid);
|
||||
dev_err(ctrl->dev, "SRC ID of the error transaction 0x%08X\n",
|
||||
err_srcid);
|
||||
|
||||
dev_err(ctrl->dev, "Transaction Address corresponding to error"
|
||||
"ERADDR 0x%08X\n", err_addr);
|
||||
dev_err(ctrl->dev, "Transaction Address corresponding to error ERADDR 0x%08X\n",
|
||||
err_addr);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
@ -199,7 +200,7 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
|
||||
* the resources needed for the controller only. The
|
||||
* resources for the NAND banks themselves are allocated
|
||||
* in the chip probe function.
|
||||
*/
|
||||
*/
|
||||
static int fsl_ifc_ctrl_probe(struct platform_device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -250,8 +251,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
|
||||
/* get the Controller level irq */
|
||||
fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
|
||||
if (fsl_ifc_ctrl_dev->irq == 0) {
|
||||
dev_err(&dev->dev, "failed to get irq resource "
|
||||
"for IFC\n");
|
||||
dev_err(&dev->dev, "failed to get irq resource for IFC\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
@ -22,6 +23,8 @@
|
||||
#define NEMC_SMCRn(n) (0x14 + (((n) - 1) * 4))
|
||||
#define NEMC_NFCSR 0x50
|
||||
|
||||
#define NEMC_REG_LEN 0x54
|
||||
|
||||
#define NEMC_SMCR_SMT BIT(0)
|
||||
#define NEMC_SMCR_BW_SHIFT 6
|
||||
#define NEMC_SMCR_BW_MASK (0x3 << NEMC_SMCR_BW_SHIFT)
|
||||
@ -288,7 +291,19 @@ static int jz4780_nemc_probe(struct platform_device *pdev)
|
||||
nemc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
nemc->base = devm_ioremap_resource(dev, res);
|
||||
|
||||
/*
|
||||
* The driver currently only uses the registers up to offset
|
||||
* NEMC_REG_LEN. Since the EFUSE registers are in the middle of the
|
||||
* NEMC registers, we only request the registers we will use for now;
|
||||
* that way the EFUSE driver can probe too.
|
||||
*/
|
||||
if (!devm_request_mem_region(dev, res->start, NEMC_REG_LEN, dev_name(dev))) {
|
||||
dev_err(dev, "unable to request I/O memory region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
nemc->base = devm_ioremap(dev, res->start, NEMC_REG_LEN);
|
||||
if (IS_ERR(nemc->base)) {
|
||||
dev_err(dev, "failed to get I/O memory\n");
|
||||
return PTR_ERR(nemc->base);
|
||||
|
@ -60,7 +60,7 @@ struct mtk_smi_common_plat {
|
||||
|
||||
struct mtk_smi_larb_gen {
|
||||
int port_in_larb[MTK_LARB_NR_MAX + 1];
|
||||
void (*config_port)(struct device *);
|
||||
void (*config_port)(struct device *dev);
|
||||
unsigned int larb_direct_to_common_mask;
|
||||
bool has_gals;
|
||||
};
|
||||
|
@ -124,32 +124,32 @@ static int devbus_get_timing_params(struct devbus *devbus,
|
||||
* The bus width is encoded into the register as 0 for 8 bits,
|
||||
* and 1 for 16 bits, so we do the necessary conversion here.
|
||||
*/
|
||||
if (r->bus_width == 8)
|
||||
if (r->bus_width == 8) {
|
||||
r->bus_width = 0;
|
||||
else if (r->bus_width == 16)
|
||||
} else if (r->bus_width == 16) {
|
||||
r->bus_width = 1;
|
||||
else {
|
||||
} else {
|
||||
dev_err(devbus->dev, "invalid bus width %d\n", r->bus_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps",
|
||||
&r->badr_skew);
|
||||
&r->badr_skew);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps",
|
||||
&r->turn_off);
|
||||
&r->turn_off);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps",
|
||||
&r->acc_first);
|
||||
&r->acc_first);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps",
|
||||
&r->acc_next);
|
||||
&r->acc_next);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -175,17 +175,17 @@ static int devbus_get_timing_params(struct devbus *devbus,
|
||||
}
|
||||
|
||||
err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps",
|
||||
&w->ale_wr);
|
||||
&w->ale_wr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps",
|
||||
&w->wr_low);
|
||||
&w->wr_low);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps",
|
||||
&w->wr_high);
|
||||
&w->wr_high);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -4,11 +4,10 @@
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments, Inc.
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
* Copyright (C) 2020 Krzysztof Kozlowski <krzk@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/export.h>
|
||||
@ -19,7 +18,7 @@
|
||||
/**
|
||||
* of_get_min_tck() - extract min timing values for ddr
|
||||
* @np: pointer to ddr device tree node
|
||||
* @device: device requesting for min timing values
|
||||
* @dev: device requesting for min timing values
|
||||
*
|
||||
* Populates the lpddr2_min_tck structure by extracting data
|
||||
* from device tree node. Returns a pointer to the populated
|
||||
@ -27,7 +26,7 @@
|
||||
* default min timings provided by JEDEC.
|
||||
*/
|
||||
const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
|
||||
struct device *dev)
|
||||
struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct lpddr2_min_tck *min;
|
||||
@ -56,13 +55,13 @@ const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
|
||||
return min;
|
||||
|
||||
default_min_tck:
|
||||
dev_warn(dev, "%s: using default min-tck values\n", __func__);
|
||||
dev_warn(dev, "Using default min-tck values\n");
|
||||
return &lpddr2_jedec_min_tck;
|
||||
}
|
||||
EXPORT_SYMBOL(of_get_min_tck);
|
||||
|
||||
static int of_do_get_timings(struct device_node *np,
|
||||
struct lpddr2_timings *tim)
|
||||
struct lpddr2_timings *tim)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -84,7 +83,7 @@ static int of_do_get_timings(struct device_node *np,
|
||||
ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
|
||||
ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
|
||||
ret |= of_property_read_u32(np, "tDQSCK-max-derated",
|
||||
&tim->tDQSCK_max_derated);
|
||||
&tim->tDQSCK_max_derated);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -103,7 +102,9 @@ static int of_do_get_timings(struct device_node *np,
|
||||
* while populating, returns default timings provided by JEDEC.
|
||||
*/
|
||||
const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
|
||||
struct device *dev, u32 device_type, u32 *nr_frequencies)
|
||||
struct device *dev,
|
||||
u32 device_type,
|
||||
u32 *nr_frequencies)
|
||||
{
|
||||
struct lpddr2_timings *timings = NULL;
|
||||
u32 arr_sz = 0, i = 0;
|
||||
@ -116,7 +117,7 @@ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
|
||||
tim_compat = "jedec,lpddr2-timings";
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "%s: un-supported memory type\n", __func__);
|
||||
dev_warn(dev, "Unsupported memory type\n");
|
||||
}
|
||||
|
||||
for_each_child_of_node(np_ddr, np_tim)
|
||||
@ -145,7 +146,7 @@ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
|
||||
return timings;
|
||||
|
||||
default_timings:
|
||||
dev_warn(dev, "%s: using default timings\n", __func__);
|
||||
dev_warn(dev, "Using default memory timings\n");
|
||||
*nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings);
|
||||
return lpddr2_jedec_timings;
|
||||
}
|
||||
@ -154,7 +155,7 @@ EXPORT_SYMBOL(of_get_ddr_timings);
|
||||
/**
|
||||
* of_lpddr3_get_min_tck() - extract min timing values for lpddr3
|
||||
* @np: pointer to ddr device tree node
|
||||
* @device: device requesting for min timing values
|
||||
* @dev: device requesting for min timing values
|
||||
*
|
||||
* Populates the lpddr3_min_tck structure by extracting data
|
||||
* from device tree node. Returns a pointer to the populated
|
||||
@ -193,8 +194,7 @@ const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np,
|
||||
ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD);
|
||||
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: errors while parsing min-tck values\n",
|
||||
__func__);
|
||||
dev_warn(dev, "Errors while parsing min-tck values\n");
|
||||
devm_kfree(dev, min);
|
||||
goto default_min_tck;
|
||||
}
|
||||
@ -202,7 +202,7 @@ const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np,
|
||||
return min;
|
||||
|
||||
default_min_tck:
|
||||
dev_warn(dev, "%s: using default min-tck values\n", __func__);
|
||||
dev_warn(dev, "Using default min-tck values\n");
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(of_lpddr3_get_min_tck);
|
||||
@ -264,7 +264,7 @@ const struct lpddr3_timings
|
||||
tim_compat = "jedec,lpddr3-timings";
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "%s: un-supported memory type\n", __func__);
|
||||
dev_warn(dev, "Unsupported memory type\n");
|
||||
}
|
||||
|
||||
for_each_child_of_node(np_ddr, np_tim)
|
||||
@ -293,7 +293,7 @@ const struct lpddr3_timings
|
||||
return timings;
|
||||
|
||||
default_timings:
|
||||
dev_warn(dev, "%s: failed to get timings\n", __func__);
|
||||
dev_warn(dev, "Failed to get timings\n");
|
||||
*nr_frequencies = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3,22 +3,23 @@
|
||||
* OpenFirmware helpers for memory drivers
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments, Inc.
|
||||
* Copyright (C) 2020 Krzysztof Kozlowski <krzk@kernel.org>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MEMORY_OF_REG_H
|
||||
#define __LINUX_MEMORY_OF_REG_H
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_DDR)
|
||||
extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
|
||||
struct device *dev);
|
||||
extern const struct lpddr2_timings
|
||||
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
|
||||
u32 device_type, u32 *nr_frequencies);
|
||||
extern const struct lpddr3_min_tck
|
||||
*of_lpddr3_get_min_tck(struct device_node *np, struct device *dev);
|
||||
extern const struct lpddr3_timings
|
||||
*of_lpddr3_get_ddr_timings(struct device_node *np_ddr,
|
||||
struct device *dev, u32 device_type, u32 *nr_frequencies);
|
||||
const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
|
||||
struct device *dev);
|
||||
const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
|
||||
struct device *dev,
|
||||
u32 device_type, u32 *nr_frequencies);
|
||||
const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np,
|
||||
struct device *dev);
|
||||
const struct lpddr3_timings *
|
||||
of_lpddr3_get_ddr_timings(struct device_node *np_ddr,
|
||||
struct device *dev, u32 device_type, u32 *nr_frequencies);
|
||||
#else
|
||||
static inline const struct lpddr2_min_tck
|
||||
*of_get_min_tck(struct device_node *np, struct device *dev)
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/omap-gpmc.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#include <linux/platform_data/mtd-nand-omap2.h>
|
||||
|
||||
@ -108,8 +109,8 @@
|
||||
#define ENABLE_PREFETCH (0x1 << 7)
|
||||
#define DMA_MPU_MODE 2
|
||||
|
||||
#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf)
|
||||
#define GPMC_REVISION_MINOR(l) (l & 0xf)
|
||||
#define GPMC_REVISION_MAJOR(l) (((l) >> 4) & 0xf)
|
||||
#define GPMC_REVISION_MINOR(l) ((l) & 0xf)
|
||||
|
||||
#define GPMC_HAS_WR_ACCESS 0x1
|
||||
#define GPMC_HAS_WR_DATA_MUX_BUS 0x2
|
||||
@ -140,27 +141,27 @@
|
||||
#define GPMC_CONFIG1_WRITEMULTIPLE_SUPP (1 << 28)
|
||||
#define GPMC_CONFIG1_WRITETYPE_ASYNC (0 << 27)
|
||||
#define GPMC_CONFIG1_WRITETYPE_SYNC (1 << 27)
|
||||
#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) ((val & 3) << 25)
|
||||
#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) (((val) & 3) << 25)
|
||||
/** CLKACTIVATIONTIME Max Ticks */
|
||||
#define GPMC_CONFIG1_CLKACTIVATIONTIME_MAX 2
|
||||
#define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23)
|
||||
#define GPMC_CONFIG1_PAGE_LEN(val) (((val) & 3) << 23)
|
||||
/** ATTACHEDDEVICEPAGELENGTH Max Value */
|
||||
#define GPMC_CONFIG1_ATTACHEDDEVICEPAGELENGTH_MAX 2
|
||||
#define GPMC_CONFIG1_WAIT_READ_MON (1 << 22)
|
||||
#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21)
|
||||
#define GPMC_CONFIG1_WAIT_MON_TIME(val) ((val & 3) << 18)
|
||||
#define GPMC_CONFIG1_WAIT_MON_TIME(val) (((val) & 3) << 18)
|
||||
/** WAITMONITORINGTIME Max Ticks */
|
||||
#define GPMC_CONFIG1_WAITMONITORINGTIME_MAX 2
|
||||
#define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16)
|
||||
#define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12)
|
||||
#define GPMC_CONFIG1_WAIT_PIN_SEL(val) (((val) & 3) << 16)
|
||||
#define GPMC_CONFIG1_DEVICESIZE(val) (((val) & 3) << 12)
|
||||
#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1)
|
||||
/** DEVICESIZE Max Value */
|
||||
#define GPMC_CONFIG1_DEVICESIZE_MAX 1
|
||||
#define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10)
|
||||
#define GPMC_CONFIG1_DEVICETYPE(val) (((val) & 3) << 10)
|
||||
#define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0)
|
||||
#define GPMC_CONFIG1_MUXTYPE(val) ((val & 3) << 8)
|
||||
#define GPMC_CONFIG1_MUXTYPE(val) (((val) & 3) << 8)
|
||||
#define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4)
|
||||
#define GPMC_CONFIG1_FCLK_DIV(val) (val & 3)
|
||||
#define GPMC_CONFIG1_FCLK_DIV(val) ((val) & 3)
|
||||
#define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1))
|
||||
#define GPMC_CONFIG1_FCLK_DIV3 (GPMC_CONFIG1_FCLK_DIV(2))
|
||||
#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3))
|
||||
@ -245,7 +246,7 @@ static DEFINE_SPINLOCK(gpmc_mem_lock);
|
||||
static unsigned int gpmc_cs_num = GPMC_CS_NUM;
|
||||
static unsigned int gpmc_nr_waitpins;
|
||||
static resource_size_t phys_base, mem_size;
|
||||
static unsigned gpmc_capability;
|
||||
static unsigned int gpmc_capability;
|
||||
static void __iomem *gpmc_base;
|
||||
|
||||
static struct clk *gpmc_l3_clk;
|
||||
@ -291,15 +292,14 @@ static unsigned long gpmc_get_fclk_period(void)
|
||||
|
||||
/**
|
||||
* gpmc_get_clk_period - get period of selected clock domain in ps
|
||||
* @cs Chip Select Region.
|
||||
* @cd Clock Domain.
|
||||
* @cs: Chip Select Region.
|
||||
* @cd: Clock Domain.
|
||||
*
|
||||
* GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup
|
||||
* prior to calling this function with GPMC_CD_CLK.
|
||||
*/
|
||||
static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd)
|
||||
{
|
||||
|
||||
unsigned long tick_ps = gpmc_get_fclk_period();
|
||||
u32 l;
|
||||
int div;
|
||||
@ -319,7 +319,6 @@ static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd)
|
||||
}
|
||||
|
||||
return tick_ps;
|
||||
|
||||
}
|
||||
|
||||
static unsigned int gpmc_ns_to_clk_ticks(unsigned int time_ns, int cs,
|
||||
@ -411,7 +410,7 @@ static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p)
|
||||
* @reg: GPMC_CS_CONFIGn register offset.
|
||||
* @st_bit: Start Bit
|
||||
* @end_bit: End Bit. Must be >= @st_bit.
|
||||
* @ma:x Maximum parameter value (before optional @shift).
|
||||
* @max: Maximum parameter value (before optional @shift).
|
||||
* If 0, maximum is as high as @st_bit and @end_bit allow.
|
||||
* @name: DTS node name, w/o "gpmc,"
|
||||
* @cd: Clock Domain of timing parameter.
|
||||
@ -511,7 +510,7 @@ static void gpmc_cs_show_timings(int cs, const char *desc)
|
||||
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 4, 4, "time-para-granularity");
|
||||
GPMC_GET_RAW(GPMC_CS_CONFIG1, 8, 9, "mux-add-data");
|
||||
GPMC_GET_RAW_SHIFT_MAX(GPMC_CS_CONFIG1, 12, 13, 1,
|
||||
GPMC_CONFIG1_DEVICESIZE_MAX, "device-width");
|
||||
GPMC_CONFIG1_DEVICESIZE_MAX, "device-width");
|
||||
GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin");
|
||||
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write");
|
||||
GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read");
|
||||
@ -625,9 +624,8 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max
|
||||
|
||||
l = gpmc_cs_read_reg(cs, reg);
|
||||
#ifdef CONFIG_OMAP_GPMC_DEBUG
|
||||
pr_info(
|
||||
"GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
|
||||
cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000,
|
||||
pr_info("GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
|
||||
cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000,
|
||||
(l >> st_bit) & mask, time);
|
||||
#endif
|
||||
l &= ~(mask << st_bit);
|
||||
@ -662,7 +660,6 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max
|
||||
*/
|
||||
static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring)
|
||||
{
|
||||
|
||||
int div = gpmc_ns_to_ticks(wait_monitoring);
|
||||
|
||||
div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1;
|
||||
@ -674,7 +671,6 @@ static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring)
|
||||
div = 1;
|
||||
|
||||
return div;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -728,7 +724,6 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t,
|
||||
if (!s->sync_read && !s->sync_write &&
|
||||
(s->wait_on_read || s->wait_on_write)
|
||||
) {
|
||||
|
||||
div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring);
|
||||
if (div < 0) {
|
||||
pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n",
|
||||
@ -958,7 +953,7 @@ static int gpmc_cs_remap(int cs, u32 base)
|
||||
* Make sure we ignore any device offsets from the GPMC partition
|
||||
* allocated for the chip select and that the new base confirms
|
||||
* to the GPMC 16MB minimum granularity.
|
||||
*/
|
||||
*/
|
||||
base &= ~(SZ_16M - 1);
|
||||
|
||||
gpmc_cs_get_memconf(cs, &old_base, &size);
|
||||
@ -1087,7 +1082,7 @@ static struct gpmc_nand_ops nand_ops = {
|
||||
|
||||
/**
|
||||
* gpmc_omap_get_nand_ops - Get the GPMC NAND interface
|
||||
* @regs: the GPMC NAND register map exclusive for NAND use.
|
||||
* @reg: the GPMC NAND register map exclusive for NAND use.
|
||||
* @cs: GPMC chip select number on which the NAND sits. The
|
||||
* register map returned will be specific to this chip select.
|
||||
*
|
||||
@ -1242,7 +1237,7 @@ int gpmc_omap_onenand_set_timings(struct device *dev, int cs, int freq,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpmc_omap_onenand_set_timings);
|
||||
|
||||
int gpmc_get_client_irq(unsigned irq_config)
|
||||
int gpmc_get_client_irq(unsigned int irq_config)
|
||||
{
|
||||
if (!gpmc_irq_domain) {
|
||||
pr_warn("%s called before GPMC IRQ domain available\n",
|
||||
@ -1465,7 +1460,6 @@ static void gpmc_mem_exit(void)
|
||||
continue;
|
||||
gpmc_cs_delete_mem(cs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void gpmc_mem_init(void)
|
||||
@ -1634,17 +1628,14 @@ static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t,
|
||||
/* oe_on */
|
||||
temp = dev_t->t_oeasu;
|
||||
if (mux)
|
||||
temp = max_t(u32, temp,
|
||||
gpmc_t->adv_rd_off + dev_t->t_aavdh);
|
||||
temp = max_t(u32, temp, gpmc_t->adv_rd_off + dev_t->t_aavdh);
|
||||
gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp);
|
||||
|
||||
/* access */
|
||||
temp = max_t(u32, dev_t->t_iaa, /* XXX: remove t_iaa in async ? */
|
||||
gpmc_t->oe_on + dev_t->t_oe);
|
||||
temp = max_t(u32, temp,
|
||||
gpmc_t->cs_on + dev_t->t_ce);
|
||||
temp = max_t(u32, temp,
|
||||
gpmc_t->adv_on + dev_t->t_aa);
|
||||
gpmc_t->oe_on + dev_t->t_oe);
|
||||
temp = max_t(u32, temp, gpmc_t->cs_on + dev_t->t_ce);
|
||||
temp = max_t(u32, temp, gpmc_t->adv_on + dev_t->t_aa);
|
||||
gpmc_t->access = gpmc_round_ps_to_ticks(temp);
|
||||
|
||||
gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1);
|
||||
@ -1753,10 +1744,11 @@ static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: remove this function once all peripherals are confirmed to
|
||||
/*
|
||||
* TODO: remove this function once all peripherals are confirmed to
|
||||
* work with generic timing. Simultaneously gpmc_cs_set_timings()
|
||||
* has to be modified to handle timings in ps instead of ns
|
||||
*/
|
||||
*/
|
||||
static void gpmc_convert_ps_to_ns(struct gpmc_timings *t)
|
||||
{
|
||||
t->cs_on /= 1000;
|
||||
@ -2089,7 +2081,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
|
||||
gpmc_cs_disable_mem(cs);
|
||||
|
||||
/*
|
||||
* FIXME: gpmc_cs_request() will map the CS to an arbitary
|
||||
* FIXME: gpmc_cs_request() will map the CS to an arbitrary
|
||||
* location in the gpmc address space. When booting with
|
||||
* device-tree we want the NOR flash to be mapped to the
|
||||
* location specified in the device-tree blob. So remap the
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Memory controller driver for ARM PrimeCell PL172
|
||||
* PrimeCell MultiPort Memory Controller (PL172)
|
||||
@ -6,10 +7,6 @@
|
||||
*
|
||||
* Based on:
|
||||
* TI AEMIF driver, Copyright (C) 2010 - 2013 Texas Instruments Inc.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/amba/bus.h>
|
||||
@ -24,7 +21,7 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#define MPMC_STATIC_CFG(n) (0x200 + 0x20 * n)
|
||||
#define MPMC_STATIC_CFG(n) (0x200 + 0x20 * (n))
|
||||
#define MPMC_STATIC_CFG_MW_8BIT 0x0
|
||||
#define MPMC_STATIC_CFG_MW_16BIT 0x1
|
||||
#define MPMC_STATIC_CFG_MW_32BIT 0x2
|
||||
@ -34,17 +31,17 @@
|
||||
#define MPMC_STATIC_CFG_EW BIT(8)
|
||||
#define MPMC_STATIC_CFG_B BIT(19)
|
||||
#define MPMC_STATIC_CFG_P BIT(20)
|
||||
#define MPMC_STATIC_WAIT_WEN(n) (0x204 + 0x20 * n)
|
||||
#define MPMC_STATIC_WAIT_WEN(n) (0x204 + 0x20 * (n))
|
||||
#define MPMC_STATIC_WAIT_WEN_MAX 0x0f
|
||||
#define MPMC_STATIC_WAIT_OEN(n) (0x208 + 0x20 * n)
|
||||
#define MPMC_STATIC_WAIT_OEN(n) (0x208 + 0x20 * (n))
|
||||
#define MPMC_STATIC_WAIT_OEN_MAX 0x0f
|
||||
#define MPMC_STATIC_WAIT_RD(n) (0x20c + 0x20 * n)
|
||||
#define MPMC_STATIC_WAIT_RD(n) (0x20c + 0x20 * (n))
|
||||
#define MPMC_STATIC_WAIT_RD_MAX 0x1f
|
||||
#define MPMC_STATIC_WAIT_PAGE(n) (0x210 + 0x20 * n)
|
||||
#define MPMC_STATIC_WAIT_PAGE(n) (0x210 + 0x20 * (n))
|
||||
#define MPMC_STATIC_WAIT_PAGE_MAX 0x1f
|
||||
#define MPMC_STATIC_WAIT_WR(n) (0x214 + 0x20 * n)
|
||||
#define MPMC_STATIC_WAIT_WR(n) (0x214 + 0x20 * (n))
|
||||
#define MPMC_STATIC_WAIT_WR_MAX 0x1f
|
||||
#define MPMC_STATIC_WAIT_TURN(n) (0x218 + 0x20 * n)
|
||||
#define MPMC_STATIC_WAIT_TURN(n) (0x218 + 0x20 * (n))
|
||||
#define MPMC_STATIC_WAIT_TURN_MAX 0x0f
|
||||
|
||||
/* Maximum number of static chip selects */
|
||||
|
@ -23,5 +23,12 @@ config EXYNOS5422_DMC
|
||||
config EXYNOS_SROM
|
||||
bool "Exynos SROM controller driver" if COMPILE_TEST
|
||||
depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM)
|
||||
help
|
||||
This adds driver for Samsung Exynos SoC SROM controller. The driver
|
||||
in basic operation mode only saves and restores SROM registers
|
||||
during suspend. If however appropriate device tree configuration
|
||||
is provided, the driver enables support for external memory
|
||||
or external devices.
|
||||
If unsure, say Y on devices with Samsung Exynos SocS.
|
||||
|
||||
endif
|
||||
|
@ -47,9 +47,9 @@ struct exynos_srom {
|
||||
struct exynos_srom_reg_dump *reg_offset;
|
||||
};
|
||||
|
||||
static struct exynos_srom_reg_dump *exynos_srom_alloc_reg_dump(
|
||||
const unsigned long *rdump,
|
||||
unsigned long nr_rdump)
|
||||
static struct exynos_srom_reg_dump *
|
||||
exynos_srom_alloc_reg_dump(const unsigned long *rdump,
|
||||
unsigned long nr_rdump)
|
||||
{
|
||||
struct exynos_srom_reg_dump *rd;
|
||||
unsigned int i;
|
||||
@ -116,7 +116,7 @@ static int exynos_srom_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
srom = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct exynos_srom), GFP_KERNEL);
|
||||
sizeof(struct exynos_srom), GFP_KERNEL);
|
||||
if (!srom)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -130,7 +130,7 @@ static int exynos_srom_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, srom);
|
||||
|
||||
srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets,
|
||||
ARRAY_SIZE(exynos_srom_offsets));
|
||||
ARRAY_SIZE(exynos_srom_offsets));
|
||||
if (!srom->reg_offset) {
|
||||
iounmap(srom->reg_base);
|
||||
return -ENOMEM;
|
||||
@ -157,16 +157,16 @@ static int exynos_srom_probe(struct platform_device *pdev)
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void exynos_srom_save(void __iomem *base,
|
||||
struct exynos_srom_reg_dump *rd,
|
||||
unsigned int num_regs)
|
||||
struct exynos_srom_reg_dump *rd,
|
||||
unsigned int num_regs)
|
||||
{
|
||||
for (; num_regs > 0; --num_regs, ++rd)
|
||||
rd->value = readl(base + rd->offset);
|
||||
}
|
||||
|
||||
static void exynos_srom_restore(void __iomem *base,
|
||||
const struct exynos_srom_reg_dump *rd,
|
||||
unsigned int num_regs)
|
||||
const struct exynos_srom_reg_dump *rd,
|
||||
unsigned int num_regs)
|
||||
{
|
||||
for (; num_regs > 0; --num_regs, ++rd)
|
||||
writel(rd->value, base + rd->offset);
|
||||
@ -177,7 +177,7 @@ static int exynos_srom_suspend(struct device *dev)
|
||||
struct exynos_srom *srom = dev_get_drvdata(dev);
|
||||
|
||||
exynos_srom_save(srom->reg_base, srom->reg_offset,
|
||||
ARRAY_SIZE(exynos_srom_offsets));
|
||||
ARRAY_SIZE(exynos_srom_offsets));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -186,7 +186,7 @@ static int exynos_srom_resume(struct device *dev)
|
||||
struct exynos_srom *srom = dev_get_drvdata(dev);
|
||||
|
||||
exynos_srom_restore(srom->reg_base, srom->reg_offset,
|
||||
ARRAY_SIZE(exynos_srom_offsets));
|
||||
ARRAY_SIZE(exynos_srom_offsets));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -270,12 +270,14 @@ static int find_target_freq_idx(struct exynos5_dmc *dmc,
|
||||
* This function switches between these banks according to the
|
||||
* currently used clock source.
|
||||
*/
|
||||
static void exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set)
|
||||
static int exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(dmc->clk_regmap, CDREX_LPDDR3PHY_CON3, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (set)
|
||||
reg |= EXYNOS5_TIMING_SET_SWI;
|
||||
@ -283,6 +285,8 @@ static void exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set)
|
||||
reg &= ~EXYNOS5_TIMING_SET_SWI;
|
||||
|
||||
regmap_write(dmc->clk_regmap, CDREX_LPDDR3PHY_CON3, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -516,7 +520,7 @@ exynos5_dmc_switch_to_bypass_configuration(struct exynos5_dmc *dmc,
|
||||
/*
|
||||
* Delays are long enough, so use them for the new coming clock.
|
||||
*/
|
||||
exynos5_switch_timing_regs(dmc, USE_MX_MSPLL_TIMINGS);
|
||||
ret = exynos5_switch_timing_regs(dmc, USE_MX_MSPLL_TIMINGS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -577,7 +581,9 @@ exynos5_dmc_change_freq_and_volt(struct exynos5_dmc *dmc,
|
||||
|
||||
clk_set_rate(dmc->fout_bpll, target_rate);
|
||||
|
||||
exynos5_switch_timing_regs(dmc, USE_BPLL_TIMINGS);
|
||||
ret = exynos5_switch_timing_regs(dmc, USE_BPLL_TIMINGS);
|
||||
if (ret)
|
||||
goto disable_clocks;
|
||||
|
||||
ret = clk_set_parent(dmc->mout_mclk_cdrex, dmc->mout_bpll);
|
||||
if (ret)
|
||||
@ -1392,7 +1398,7 @@ static int exynos5_dmc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(dmc->base_drexi1);
|
||||
|
||||
dmc->clk_regmap = syscon_regmap_lookup_by_phandle(np,
|
||||
"samsung,syscon-clk");
|
||||
"samsung,syscon-clk");
|
||||
if (IS_ERR(dmc->clk_regmap))
|
||||
return PTR_ERR(dmc->clk_regmap);
|
||||
|
||||
@ -1471,7 +1477,6 @@ static int exynos5_dmc_probe(struct platform_device *pdev)
|
||||
exynos5_dmc_df_profile.polling_ms = 500;
|
||||
}
|
||||
|
||||
|
||||
dmc->df = devm_devfreq_add_device(dev, &exynos5_dmc_df_profile,
|
||||
DEVFREQ_GOV_SIMPLE_ONDEMAND,
|
||||
&dmc->gov_data);
|
||||
|
@ -36,3 +36,17 @@ config TEGRA124_EMC
|
||||
Tegra124 chips. The EMC controls the external DRAM on the board.
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
config TEGRA210_EMC_TABLE
|
||||
bool
|
||||
depends on ARCH_TEGRA_210_SOC
|
||||
|
||||
config TEGRA210_EMC
|
||||
tristate "NVIDIA Tegra210 External Memory Controller driver"
|
||||
depends on TEGRA_MC && ARCH_TEGRA_210_SOC
|
||||
select TEGRA210_EMC_TABLE
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra210 chips. The EMC controls the external DRAM on the board.
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
@ -13,5 +13,9 @@ obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
|
||||
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
|
||||
obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
|
||||
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
|
||||
obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o
|
||||
obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o tegra186-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra186-emc.o
|
||||
|
||||
tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o
|
||||
|
@ -34,6 +34,7 @@
|
||||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
#define MC_EMEM_ARB_MISC2 0xc8
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
|
@ -984,6 +984,7 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
|
||||
static const struct of_device_id tegra_emc_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-emc" },
|
||||
{ .compatible = "nvidia,tegra132-emc" },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1178,11 +1179,11 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, emc,
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
|
||||
&tegra_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("min_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_min_rate_fops);
|
||||
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("max_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_max_rate_fops);
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(emc->clk)) {
|
||||
err = PTR_ERR(emc->clk);
|
||||
dev_err(&pdev->dev, "failed to get EMC clock: %d\n", err);
|
||||
return err;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, emc);
|
||||
@ -201,7 +201,7 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
||||
err = tegra_bpmp_transfer(emc->bpmp, &msg);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to EMC DVFS pairs: %d\n", err);
|
||||
return err;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
emc->debugfs.min_rate = ULONG_MAX;
|
||||
@ -211,8 +211,10 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
||||
|
||||
emc->dvfs = devm_kmalloc_array(&pdev->dev, emc->num_dvfs,
|
||||
sizeof(*emc->dvfs), GFP_KERNEL);
|
||||
if (!emc->dvfs)
|
||||
return -ENOMEM;
|
||||
if (!emc->dvfs) {
|
||||
err = -ENOMEM;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "%u DVFS pairs:\n", emc->num_dvfs);
|
||||
|
||||
@ -237,15 +239,10 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
||||
"failed to set rate range [%lu-%lu] for %pC\n",
|
||||
emc->debugfs.min_rate, emc->debugfs.max_rate,
|
||||
emc->clk);
|
||||
return err;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
emc->debugfs.root = debugfs_create_dir("emc", NULL);
|
||||
if (!emc->debugfs.root) {
|
||||
dev_err(&pdev->dev, "failed to create debugfs directory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
|
||||
emc, &tegra186_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
@ -254,6 +251,10 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
||||
emc, &tegra186_emc_debug_max_rate_fops);
|
||||
|
||||
return 0;
|
||||
|
||||
put_bpmp:
|
||||
tegra_bpmp_put(emc->bpmp);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra186_emc_remove(struct platform_device *pdev)
|
||||
@ -267,10 +268,10 @@ static int tegra186_emc_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra186_emc_of_match[] = {
|
||||
#if defined(CONFIG_ARCH_TEGRA186_SOC)
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
|
||||
{ .compatible = "nvidia,tegra186-emc" },
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA194_SOC)
|
||||
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
{ .compatible = "nvidia,tegra194-emc" },
|
||||
#endif
|
||||
{ /* sentinel */ }
|
||||
|
@ -1570,12 +1570,12 @@ static const struct of_device_id tegra186_mc_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra186_mc_of_match);
|
||||
|
||||
static int tegra186_mc_suspend(struct device *dev)
|
||||
static int __maybe_unused tegra186_mc_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_mc_resume(struct device *dev)
|
||||
static int __maybe_unused tegra186_mc_resume(struct device *dev)
|
||||
{
|
||||
struct tegra186_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -144,7 +144,6 @@ struct emc_timing {
|
||||
|
||||
struct tegra_emc {
|
||||
struct device *dev;
|
||||
struct completion clk_handshake_complete;
|
||||
struct notifier_block clk_nb;
|
||||
struct clk *clk;
|
||||
void __iomem *regs;
|
||||
@ -162,17 +161,13 @@ struct tegra_emc {
|
||||
static irqreturn_t tegra_emc_isr(int irq, void *data)
|
||||
{
|
||||
struct tegra_emc *emc = data;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 status;
|
||||
|
||||
status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask;
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* notify about EMC-CAR handshake completion */
|
||||
if (status & EMC_CLKCHANGE_COMPLETE_INT)
|
||||
complete(&emc->clk_handshake_complete);
|
||||
|
||||
/* notify about HW problem */
|
||||
if (status & EMC_REFRESH_OVERFLOW_INT)
|
||||
dev_err_ratelimited(emc->dev,
|
||||
@ -224,14 +219,13 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
|
||||
/* wait until programming has settled */
|
||||
readl_relaxed(emc->regs + emc_timing_registers[i - 1]);
|
||||
|
||||
reinit_completion(&emc->clk_handshake_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int err;
|
||||
u32 v;
|
||||
|
||||
dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush);
|
||||
|
||||
@ -242,11 +236,12 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
|
||||
return 0;
|
||||
}
|
||||
|
||||
timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
|
||||
msecs_to_jiffies(100));
|
||||
if (timeout == 0) {
|
||||
dev_err(emc->dev, "EMC-CAR handshake failed\n");
|
||||
return -EIO;
|
||||
err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, v,
|
||||
v & EMC_CLKCHANGE_COMPLETE_INT,
|
||||
1, 100);
|
||||
if (err) {
|
||||
dev_err(emc->dev, "emc-car handshake timeout: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -412,7 +407,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
|
||||
|
||||
static int emc_setup_hw(struct tegra_emc *emc)
|
||||
{
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 emc_cfg, emc_dbg;
|
||||
|
||||
emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
|
||||
@ -647,11 +642,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("min_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_min_rate_fops);
|
||||
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("max_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_max_rate_fops);
|
||||
}
|
||||
|
||||
@ -686,7 +681,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
init_completion(&emc->clk_handshake_complete);
|
||||
emc->clk_nb.notifier_call = tegra_emc_clk_change_notify;
|
||||
emc->dev = &pdev->dev;
|
||||
|
||||
|
1775
drivers/memory/tegra/tegra210-emc-cc-r21021.c
Normal file
1775
drivers/memory/tegra/tegra210-emc-cc-r21021.c
Normal file
File diff suppressed because it is too large
Load Diff
2100
drivers/memory/tegra/tegra210-emc-core.c
Normal file
2100
drivers/memory/tegra/tegra210-emc-core.c
Normal file
File diff suppressed because it is too large
Load Diff
90
drivers/memory/tegra/tegra210-emc-table.c
Normal file
90
drivers/memory/tegra/tegra210-emc-table.c
Normal file
@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
||||
#include "tegra210-emc.h"
|
||||
|
||||
#define TEGRA_EMC_MAX_FREQS 16
|
||||
|
||||
static int tegra210_emc_table_device_init(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
struct tegra210_emc *emc = dev_get_drvdata(dev);
|
||||
struct tegra210_emc_timing *timings;
|
||||
unsigned int i, count = 0;
|
||||
|
||||
timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
|
||||
if (!timings) {
|
||||
dev_err(dev, "failed to map EMC table\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
|
||||
if (timings[i].revision == 0)
|
||||
break;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
/* only the nominal and derated tables are expected */
|
||||
if (emc->derated) {
|
||||
dev_warn(dev, "excess EMC table '%s'\n", rmem->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (emc->nominal) {
|
||||
if (count != emc->num_timings) {
|
||||
dev_warn(dev, "%u derated vs. %u nominal entries\n",
|
||||
count, emc->num_timings);
|
||||
memunmap(timings);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
emc->derated = timings;
|
||||
} else {
|
||||
emc->num_timings = count;
|
||||
emc->nominal = timings;
|
||||
}
|
||||
|
||||
out:
|
||||
/* keep track of which table this is */
|
||||
rmem->priv = timings;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
struct tegra210_emc_timing *timings = rmem->priv;
|
||||
struct tegra210_emc *emc = dev_get_drvdata(dev);
|
||||
|
||||
if ((emc->nominal && timings != emc->nominal) &&
|
||||
(emc->derated && timings != emc->derated))
|
||||
dev_warn(dev, "trying to release unassigned EMC table '%s'\n",
|
||||
rmem->name);
|
||||
|
||||
memunmap(timings);
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops tegra210_emc_table_ops = {
|
||||
.device_init = tegra210_emc_table_device_init,
|
||||
.device_release = tegra210_emc_table_device_release,
|
||||
};
|
||||
|
||||
static int tegra210_emc_table_init(struct reserved_mem *rmem)
|
||||
{
|
||||
pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base,
|
||||
(unsigned long)rmem->size);
|
||||
|
||||
rmem->ops = &tegra210_emc_table_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table",
|
||||
tegra210_emc_table_init);
|
1016
drivers/memory/tegra/tegra210-emc.h
Normal file
1016
drivers/memory/tegra/tegra210-emc.h
Normal file
File diff suppressed because it is too large
Load Diff
50
drivers/memory/tegra/tegra210-mc.h
Normal file
50
drivers/memory/tegra/tegra210-mc.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef TEGRA210_MC_H
|
||||
#define TEGRA210_MC_H
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
/* register definitions */
|
||||
#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4
|
||||
#define MC_LATENCY_ALLOWANCE_HC_0 0x310
|
||||
#define MC_LATENCY_ALLOWANCE_HC_1 0x314
|
||||
#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320
|
||||
#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328
|
||||
#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344
|
||||
#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374
|
||||
#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c
|
||||
#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380
|
||||
#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390
|
||||
#define MC_LATENCY_ALLOWANCE_VIC_0 0x394
|
||||
#define MC_LATENCY_ALLOWANCE_VI2_0 0x398
|
||||
#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4
|
||||
#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8
|
||||
#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8
|
||||
#define MC_MLL_MPCORER_PTSA_RATE 0x44c
|
||||
#define MC_FTOP_PTSA_RATE 0x50c
|
||||
#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0
|
||||
#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4
|
||||
#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0
|
||||
#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4
|
||||
#define MC_PTSA_GRANT_DECREMENT 0x960
|
||||
#define MC_EMEM_ARB_DHYST_CTRL 0xbcc
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec
|
||||
|
||||
#endif
|
@ -11,7 +11,6 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
@ -327,7 +326,6 @@ struct emc_timing {
|
||||
struct tegra_emc {
|
||||
struct device *dev;
|
||||
struct tegra_mc *mc;
|
||||
struct completion clk_handshake_complete;
|
||||
struct notifier_block clk_nb;
|
||||
struct clk *clk;
|
||||
void __iomem *regs;
|
||||
@ -374,52 +372,10 @@ static int emc_seq_update_timing(struct tegra_emc *emc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emc_complete_clk_change(struct tegra_emc *emc)
|
||||
{
|
||||
struct emc_timing *timing = emc->new_timing;
|
||||
unsigned int dram_num;
|
||||
bool failed = false;
|
||||
int err;
|
||||
|
||||
/* re-enable auto-refresh */
|
||||
dram_num = tegra_mc_get_emem_device_count(emc->mc);
|
||||
writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num),
|
||||
emc->regs + EMC_REFCTRL);
|
||||
|
||||
/* restore auto-calibration */
|
||||
if (emc->vref_cal_toggle)
|
||||
writel_relaxed(timing->emc_auto_cal_interval,
|
||||
emc->regs + EMC_AUTO_CAL_INTERVAL);
|
||||
|
||||
/* restore dynamic self-refresh */
|
||||
if (timing->emc_cfg_dyn_self_ref) {
|
||||
emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE;
|
||||
writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
|
||||
}
|
||||
|
||||
/* set number of clocks to wait after each ZQ command */
|
||||
if (emc->zcal_long)
|
||||
writel_relaxed(timing->emc_zcal_cnt_long,
|
||||
emc->regs + EMC_ZCAL_WAIT_CNT);
|
||||
|
||||
/* wait for writes to settle */
|
||||
udelay(2);
|
||||
|
||||
/* update restored timing */
|
||||
err = emc_seq_update_timing(emc);
|
||||
if (err)
|
||||
failed = true;
|
||||
|
||||
/* restore early ACK */
|
||||
mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE);
|
||||
|
||||
WRITE_ONCE(emc->bad_state, failed);
|
||||
}
|
||||
|
||||
static irqreturn_t tegra_emc_isr(int irq, void *data)
|
||||
{
|
||||
struct tegra_emc *emc = data;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 status;
|
||||
|
||||
status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask;
|
||||
@ -434,18 +390,6 @@ static irqreturn_t tegra_emc_isr(int irq, void *data)
|
||||
/* clear interrupts */
|
||||
writel_relaxed(status, emc->regs + EMC_INTSTATUS);
|
||||
|
||||
/* notify about EMC-CAR handshake completion */
|
||||
if (status & EMC_CLKCHANGE_COMPLETE_INT) {
|
||||
if (completion_done(&emc->clk_handshake_complete)) {
|
||||
dev_err_ratelimited(emc->dev,
|
||||
"bogus handshake interrupt\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
emc_complete_clk_change(emc);
|
||||
complete(&emc->clk_handshake_complete);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -801,29 +745,58 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
|
||||
*/
|
||||
mc_readl(emc->mc, MC_EMEM_ARB_OVERRIDE);
|
||||
|
||||
reinit_completion(&emc->clk_handshake_complete);
|
||||
|
||||
emc->new_timing = timing;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_complete_timing_change(struct tegra_emc *emc,
|
||||
unsigned long rate)
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct emc_timing *timing = emc_find_timing(emc, rate);
|
||||
unsigned int dram_num;
|
||||
int err;
|
||||
u32 v;
|
||||
|
||||
timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
|
||||
msecs_to_jiffies(100));
|
||||
if (timeout == 0) {
|
||||
dev_err(emc->dev, "emc-car handshake failed\n");
|
||||
return -EIO;
|
||||
err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, v,
|
||||
v & EMC_CLKCHANGE_COMPLETE_INT,
|
||||
1, 100);
|
||||
if (err) {
|
||||
dev_err(emc->dev, "emc-car handshake timeout: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (READ_ONCE(emc->bad_state))
|
||||
return -EIO;
|
||||
/* re-enable auto-refresh */
|
||||
dram_num = tegra_mc_get_emem_device_count(emc->mc);
|
||||
writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num),
|
||||
emc->regs + EMC_REFCTRL);
|
||||
|
||||
return 0;
|
||||
/* restore auto-calibration */
|
||||
if (emc->vref_cal_toggle)
|
||||
writel_relaxed(timing->emc_auto_cal_interval,
|
||||
emc->regs + EMC_AUTO_CAL_INTERVAL);
|
||||
|
||||
/* restore dynamic self-refresh */
|
||||
if (timing->emc_cfg_dyn_self_ref) {
|
||||
emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE;
|
||||
writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
|
||||
}
|
||||
|
||||
/* set number of clocks to wait after each ZQ command */
|
||||
if (emc->zcal_long)
|
||||
writel_relaxed(timing->emc_zcal_cnt_long,
|
||||
emc->regs + EMC_ZCAL_WAIT_CNT);
|
||||
|
||||
/* wait for writes to settle */
|
||||
udelay(2);
|
||||
|
||||
/* update restored timing */
|
||||
err = emc_seq_update_timing(emc);
|
||||
if (!err)
|
||||
emc->bad_state = false;
|
||||
|
||||
/* restore early ACK */
|
||||
mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int emc_unprepare_timing_change(struct tegra_emc *emc,
|
||||
@ -1033,7 +1006,7 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev)
|
||||
|
||||
static int emc_setup_hw(struct tegra_emc *emc)
|
||||
{
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 fbio_cfg5, emc_cfg, emc_dbg;
|
||||
enum emc_dram_type dram_type;
|
||||
|
||||
@ -1275,11 +1248,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("min_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_min_rate_fops);
|
||||
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("max_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_max_rate_fops);
|
||||
}
|
||||
|
||||
@ -1321,7 +1294,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||
if (!emc->mc)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
init_completion(&emc->clk_handshake_complete);
|
||||
emc->clk_nb.notifier_call = emc_clk_change_notify;
|
||||
emc->dev = &pdev->dev;
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
#define WSTROBE_SHIFT 20
|
||||
#define WSETUP_SHIFT 26
|
||||
#define EW_SHIFT 30
|
||||
#define SS_SHIFT 31
|
||||
#define SSTROBE_SHIFT 31
|
||||
|
||||
#define TA(x) ((x) << TA_SHIFT)
|
||||
#define RHOLD(x) ((x) << RHOLD_SHIFT)
|
||||
@ -37,7 +37,7 @@
|
||||
#define WSTROBE(x) ((x) << WSTROBE_SHIFT)
|
||||
#define WSETUP(x) ((x) << WSETUP_SHIFT)
|
||||
#define EW(x) ((x) << EW_SHIFT)
|
||||
#define SS(x) ((x) << SS_SHIFT)
|
||||
#define SSTROBE(x) ((x) << SSTROBE_SHIFT)
|
||||
|
||||
#define ASIZE_MAX 0x1
|
||||
#define TA_MAX 0x3
|
||||
@ -48,7 +48,7 @@
|
||||
#define WSTROBE_MAX 0x3f
|
||||
#define WSETUP_MAX 0xf
|
||||
#define EW_MAX 0x1
|
||||
#define SS_MAX 0x1
|
||||
#define SSTROBE_MAX 0x1
|
||||
#define NUM_CS 4
|
||||
|
||||
#define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT)
|
||||
@ -59,7 +59,7 @@
|
||||
#define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT)
|
||||
#define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT)
|
||||
#define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT)
|
||||
#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT)
|
||||
#define SSTROBE_VAL(x) (((x) & SSTROBE(SSTROBE_MAX)) >> SSTROBE_SHIFT)
|
||||
|
||||
#define NRCSR_OFFSET 0x00
|
||||
#define AWCCR_OFFSET 0x04
|
||||
@ -67,7 +67,7 @@
|
||||
|
||||
#define ACR_ASIZE_MASK 0x3
|
||||
#define ACR_EW_MASK BIT(30)
|
||||
#define ACR_SS_MASK BIT(31)
|
||||
#define ACR_SSTROBE_MASK BIT(31)
|
||||
#define ASIZE_16BIT 1
|
||||
|
||||
#define CONFIG_MASK (TA(TA_MAX) | \
|
||||
@ -77,7 +77,7 @@
|
||||
WHOLD(WHOLD_MAX) | \
|
||||
WSTROBE(WSTROBE_MAX) | \
|
||||
WSETUP(WSETUP_MAX) | \
|
||||
EW(EW_MAX) | SS(SS_MAX) | \
|
||||
EW(EW_MAX) | SSTROBE(SSTROBE_MAX) | \
|
||||
ASIZE_MAX)
|
||||
|
||||
/**
|
||||
@ -204,7 +204,7 @@ static int aemif_config_abus(struct platform_device *pdev, int csnum)
|
||||
if (data->enable_ew)
|
||||
set |= ACR_EW_MASK;
|
||||
if (data->enable_ss)
|
||||
set |= ACR_SS_MASK;
|
||||
set |= ACR_SSTROBE_MASK;
|
||||
|
||||
val = readl(aemif->base + offset);
|
||||
val &= ~CONFIG_MASK;
|
||||
@ -246,7 +246,7 @@ static void aemif_get_hw_params(struct platform_device *pdev, int csnum)
|
||||
data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val), clk_rate);
|
||||
data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val), clk_rate);
|
||||
data->enable_ew = EW_VAL(val);
|
||||
data->enable_ss = SS_VAL(val);
|
||||
data->enable_ss = SSTROBE_VAL(val);
|
||||
data->asize = val & ASIZE_MAX;
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,7 @@ MODULE_DEVICE_TABLE(of, ti_emif_of_match);
|
||||
static int ti_emif_resume(struct device *dev)
|
||||
{
|
||||
unsigned long tmp =
|
||||
__raw_readl((void *)emif_instance->ti_emif_sram_virt);
|
||||
__raw_readl((void __iomem *)emif_instance->ti_emif_sram_virt);
|
||||
|
||||
/*
|
||||
* Check to see if what we are copying is already present in the
|
||||
|
@ -15,9 +15,9 @@
|
||||
#define RCU_RST_STAT 0x0024
|
||||
#define RCU_RST_REQ 0x0048
|
||||
|
||||
#define REG_OFFSET GENMASK(31, 16)
|
||||
#define BIT_OFFSET GENMASK(15, 8)
|
||||
#define STAT_BIT_OFFSET GENMASK(7, 0)
|
||||
#define REG_OFFSET_MASK GENMASK(31, 16)
|
||||
#define BIT_OFFSET_MASK GENMASK(15, 8)
|
||||
#define STAT_BIT_OFFSET_MASK GENMASK(7, 0)
|
||||
|
||||
#define to_reset_data(x) container_of(x, struct intel_reset_data, rcdev)
|
||||
|
||||
@ -51,11 +51,11 @@ static u32 id_to_reg_and_bit_offsets(struct intel_reset_data *data,
|
||||
unsigned long id, u32 *rst_req,
|
||||
u32 *req_bit, u32 *stat_bit)
|
||||
{
|
||||
*rst_req = FIELD_GET(REG_OFFSET, id);
|
||||
*req_bit = FIELD_GET(BIT_OFFSET, id);
|
||||
*rst_req = FIELD_GET(REG_OFFSET_MASK, id);
|
||||
*req_bit = FIELD_GET(BIT_OFFSET_MASK, id);
|
||||
|
||||
if (data->soc_data->legacy)
|
||||
*stat_bit = FIELD_GET(STAT_BIT_OFFSET, id);
|
||||
*stat_bit = FIELD_GET(STAT_BIT_OFFSET_MASK, id);
|
||||
else
|
||||
*stat_bit = *req_bit;
|
||||
|
||||
@ -141,14 +141,14 @@ static int intel_reset_xlate(struct reset_controller_dev *rcdev,
|
||||
if (spec->args[1] > 31)
|
||||
return -EINVAL;
|
||||
|
||||
id = FIELD_PREP(REG_OFFSET, spec->args[0]);
|
||||
id |= FIELD_PREP(BIT_OFFSET, spec->args[1]);
|
||||
id = FIELD_PREP(REG_OFFSET_MASK, spec->args[0]);
|
||||
id |= FIELD_PREP(BIT_OFFSET_MASK, spec->args[1]);
|
||||
|
||||
if (data->soc_data->legacy) {
|
||||
if (spec->args[2] > 31)
|
||||
return -EINVAL;
|
||||
|
||||
id |= FIELD_PREP(STAT_BIT_OFFSET, spec->args[2]);
|
||||
id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, spec->args[2]);
|
||||
}
|
||||
|
||||
return id;
|
||||
@ -210,11 +210,11 @@ static int intel_reset_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->reboot_id = FIELD_PREP(REG_OFFSET, rb_id[0]);
|
||||
data->reboot_id |= FIELD_PREP(BIT_OFFSET, rb_id[1]);
|
||||
data->reboot_id = FIELD_PREP(REG_OFFSET_MASK, rb_id[0]);
|
||||
data->reboot_id |= FIELD_PREP(BIT_OFFSET_MASK, rb_id[1]);
|
||||
|
||||
if (data->soc_data->legacy)
|
||||
data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET, rb_id[2]);
|
||||
data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, rb_id[2]);
|
||||
|
||||
data->restart_nb.notifier_call = intel_reset_restart_handler;
|
||||
data->restart_nb.priority = 128;
|
||||
|
@ -11,6 +11,7 @@
|
||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
@ -18,10 +19,9 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/reset/reset-simple.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "reset-simple.h"
|
||||
|
||||
static inline struct reset_simple_data *
|
||||
to_reset_simple_data(struct reset_controller_dev *rcdev)
|
||||
{
|
||||
@ -64,6 +64,24 @@ static int reset_simple_deassert(struct reset_controller_dev *rcdev,
|
||||
return reset_simple_update(rcdev, id, false);
|
||||
}
|
||||
|
||||
static int reset_simple_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct reset_simple_data *data = to_reset_simple_data(rcdev);
|
||||
int ret;
|
||||
|
||||
if (!data->reset_us)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = reset_simple_assert(rcdev, id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(data->reset_us, data->reset_us * 2);
|
||||
|
||||
return reset_simple_deassert(rcdev, id);
|
||||
}
|
||||
|
||||
static int reset_simple_status(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
@ -81,6 +99,7 @@ static int reset_simple_status(struct reset_controller_dev *rcdev,
|
||||
const struct reset_control_ops reset_simple_ops = {
|
||||
.assert = reset_simple_assert,
|
||||
.deassert = reset_simple_deassert,
|
||||
.reset = reset_simple_reset,
|
||||
.status = reset_simple_status,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(reset_simple_ops);
|
||||
|
@ -11,13 +11,12 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/reset/reset-simple.h>
|
||||
#include <linux/reset/socfpga.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "reset-simple.h"
|
||||
|
||||
#define SOCFPGA_NR_BANKS 8
|
||||
|
||||
static int a10_reset_init(struct device_node *np)
|
||||
|
@ -14,13 +14,12 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/reset/reset-simple.h>
|
||||
#include <linux/reset/sunxi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "reset-simple.h"
|
||||
|
||||
static int sunxi_reset_init(struct device_node *np)
|
||||
{
|
||||
struct reset_simple_data *data;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Texas Instrument's System Control Interface (TI-SCI) reset driver
|
||||
*
|
||||
* Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* TI SYSCON regmap reset driver
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
* Suman Anna <afd@ti.com>
|
||||
*
|
||||
|
@ -9,8 +9,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "reset-simple.h"
|
||||
#include <linux/reset/reset-simple.h>
|
||||
|
||||
#define MAX_CLKS 2
|
||||
#define MAX_RSTS 2
|
||||
|
@ -8,20 +8,12 @@ config IMX_GPCV2_PM_DOMAINS
|
||||
select PM_GENERIC_DOMAINS
|
||||
default y if SOC_IMX7D
|
||||
|
||||
config IMX_SCU_SOC
|
||||
bool "i.MX System Controller Unit SoC info support"
|
||||
depends on IMX_SCU
|
||||
select SOC_BUS
|
||||
help
|
||||
If you say yes here you get support for the NXP i.MX System
|
||||
Controller Unit SoC info module, it will provide the SoC info
|
||||
like SoC family, ID and revision etc.
|
||||
|
||||
config SOC_IMX8M
|
||||
bool "i.MX8M SoC family support"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
default ARCH_MXC && ARM64
|
||||
select SOC_BUS
|
||||
select ARM_GIC_V3 if ARCH_MXC
|
||||
help
|
||||
If you say yes here you get support for the NXP i.MX8M family
|
||||
support, it will provide the SoC info like SoC family,
|
||||
|
@ -5,4 +5,3 @@ endif
|
||||
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
|
||||
obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
|
||||
obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o
|
||||
obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o
|
||||
|
@ -12,6 +12,7 @@
|
||||
#define CMDQ_WRITE_ENABLE_MASK BIT(0)
|
||||
#define CMDQ_POLL_ENABLE_MASK BIT(0)
|
||||
#define CMDQ_EOC_IRQ_EN BIT(0)
|
||||
#define CMDQ_REG_TYPE 1
|
||||
|
||||
struct cmdq_instruction {
|
||||
union {
|
||||
@ -21,8 +22,17 @@ struct cmdq_instruction {
|
||||
union {
|
||||
u16 offset;
|
||||
u16 event;
|
||||
u16 reg_dst;
|
||||
};
|
||||
union {
|
||||
u8 subsys;
|
||||
struct {
|
||||
u8 sop:5;
|
||||
u8 arg_c_t:1;
|
||||
u8 src_t:1;
|
||||
u8 dst_t:1;
|
||||
};
|
||||
};
|
||||
u8 subsys;
|
||||
u8 op;
|
||||
};
|
||||
|
||||
@ -243,6 +253,21 @@ int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event)
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_clear_event);
|
||||
|
||||
int cmdq_pkt_set_event(struct cmdq_pkt *pkt, u16 event)
|
||||
{
|
||||
struct cmdq_instruction inst = {};
|
||||
|
||||
if (event >= CMDQ_MAX_EVENT)
|
||||
return -EINVAL;
|
||||
|
||||
inst.op = CMDQ_CODE_WFE;
|
||||
inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE;
|
||||
inst.event = event;
|
||||
|
||||
return cmdq_pkt_append_command(pkt, inst);
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_set_event);
|
||||
|
||||
int cmdq_pkt_poll(struct cmdq_pkt *pkt, u8 subsys,
|
||||
u16 offset, u32 value)
|
||||
{
|
||||
@ -278,7 +303,19 @@ int cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys,
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_poll_mask);
|
||||
|
||||
static int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
|
||||
int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value)
|
||||
{
|
||||
struct cmdq_instruction inst = {};
|
||||
|
||||
inst.op = CMDQ_CODE_LOGIC;
|
||||
inst.dst_t = CMDQ_REG_TYPE;
|
||||
inst.reg_dst = reg_idx;
|
||||
inst.value = value;
|
||||
return cmdq_pkt_append_command(pkt, inst);
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_assign);
|
||||
|
||||
int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
|
||||
{
|
||||
struct cmdq_instruction inst = { {0} };
|
||||
int err;
|
||||
@ -297,6 +334,7 @@ static int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_finalize);
|
||||
|
||||
static void cmdq_pkt_flush_async_cb(struct cmdq_cb_data data)
|
||||
{
|
||||
@ -331,10 +369,6 @@ int cmdq_pkt_flush_async(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb,
|
||||
unsigned long flags = 0;
|
||||
struct cmdq_client *client = (struct cmdq_client *)pkt->cl;
|
||||
|
||||
err = cmdq_pkt_finalize(pkt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pkt->cb.cb = cb;
|
||||
pkt->cb.data = data;
|
||||
pkt->async_cb.cb = cmdq_pkt_flush_async_cb;
|
||||
|
@ -89,7 +89,7 @@ config QCOM_RMTFS_MEM
|
||||
|
||||
config QCOM_RPMH
|
||||
bool "Qualcomm RPM-Hardened (RPMH) Communication"
|
||||
depends on ARCH_QCOM && ARM64 || COMPILE_TEST
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
help
|
||||
Support for communication with the hardened-RPM blocks in
|
||||
Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
|
||||
|
@ -278,13 +278,15 @@ static void pdr_indack_work(struct work_struct *work)
|
||||
|
||||
list_for_each_entry_safe(ind, tmp, &pdr->indack_list, node) {
|
||||
pds = ind->pds;
|
||||
pdr_send_indack_msg(pdr, pds, ind->transaction_id);
|
||||
|
||||
mutex_lock(&pdr->status_lock);
|
||||
pds->state = ind->curr_state;
|
||||
pdr->status(pds->state, pds->service_path, pdr->priv);
|
||||
mutex_unlock(&pdr->status_lock);
|
||||
|
||||
/* Ack the indication after clients release the PD resources */
|
||||
pdr_send_indack_msg(pdr, pds, ind->transaction_id);
|
||||
|
||||
mutex_lock(&pdr->list_lock);
|
||||
list_del(&ind->node);
|
||||
mutex_unlock(&pdr->list_lock);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
@ -90,8 +91,14 @@ struct geni_wrapper {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
|
||||
struct geni_icc_path to_core;
|
||||
};
|
||||
|
||||
static const char * const icc_path_names[] = {"qup-core", "qup-config",
|
||||
"qup-memory"};
|
||||
|
||||
static struct geni_wrapper *earlycon_wrapper;
|
||||
|
||||
#define QUP_HW_VER_REG 0x4
|
||||
|
||||
/* Common SE registers */
|
||||
@ -720,11 +727,132 @@ void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len)
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_rx_dma_unprep);
|
||||
|
||||
int geni_icc_get(struct geni_se *se, const char *icc_ddr)
|
||||
{
|
||||
int i, err;
|
||||
const char *icc_names[] = {"qup-core", "qup-config", icc_ddr};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
|
||||
if (!icc_names[i])
|
||||
continue;
|
||||
|
||||
se->icc_paths[i].path = devm_of_icc_get(se->dev, icc_names[i]);
|
||||
if (IS_ERR(se->icc_paths[i].path))
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
err = PTR_ERR(se->icc_paths[i].path);
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err_ratelimited(se->dev, "Failed to get ICC path '%s': %d\n",
|
||||
icc_names[i], err);
|
||||
return err;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_get);
|
||||
|
||||
int geni_icc_set_bw(struct geni_se *se)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
|
||||
ret = icc_set_bw(se->icc_paths[i].path,
|
||||
se->icc_paths[i].avg_bw, se->icc_paths[i].avg_bw);
|
||||
if (ret) {
|
||||
dev_err_ratelimited(se->dev, "ICC BW voting failed on path '%s': %d\n",
|
||||
icc_path_names[i], ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_set_bw);
|
||||
|
||||
void geni_icc_set_tag(struct geni_se *se, u32 tag)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++)
|
||||
icc_set_tag(se->icc_paths[i].path, tag);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_set_tag);
|
||||
|
||||
/* To do: Replace this by icc_bulk_enable once it's implemented in ICC core */
|
||||
int geni_icc_enable(struct geni_se *se)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
|
||||
ret = icc_enable(se->icc_paths[i].path);
|
||||
if (ret) {
|
||||
dev_err_ratelimited(se->dev, "ICC enable failed on path '%s': %d\n",
|
||||
icc_path_names[i], ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_enable);
|
||||
|
||||
int geni_icc_disable(struct geni_se *se)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
|
||||
ret = icc_disable(se->icc_paths[i].path);
|
||||
if (ret) {
|
||||
dev_err_ratelimited(se->dev, "ICC disable failed on path '%s': %d\n",
|
||||
icc_path_names[i], ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_disable);
|
||||
|
||||
void geni_remove_earlycon_icc_vote(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct geni_wrapper *wrapper;
|
||||
struct device_node *parent;
|
||||
struct device_node *child;
|
||||
|
||||
if (!earlycon_wrapper)
|
||||
return;
|
||||
|
||||
wrapper = earlycon_wrapper;
|
||||
parent = of_get_next_parent(wrapper->dev->of_node);
|
||||
for_each_child_of_node(parent, child) {
|
||||
if (!of_device_is_compatible(child, "qcom,geni-se-qup"))
|
||||
continue;
|
||||
|
||||
pdev = of_find_device_by_node(child);
|
||||
if (!pdev)
|
||||
continue;
|
||||
|
||||
wrapper = platform_get_drvdata(pdev);
|
||||
icc_put(wrapper->to_core.path);
|
||||
wrapper->to_core.path = NULL;
|
||||
|
||||
}
|
||||
of_node_put(parent);
|
||||
|
||||
earlycon_wrapper = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_remove_earlycon_icc_vote);
|
||||
|
||||
static int geni_se_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct geni_wrapper *wrapper;
|
||||
struct console __maybe_unused *bcon;
|
||||
bool __maybe_unused has_earlycon = false;
|
||||
int ret;
|
||||
|
||||
wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
|
||||
@ -747,6 +875,43 @@ static int geni_se_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_EARLYCON
|
||||
for_each_console(bcon) {
|
||||
if (!strcmp(bcon->name, "qcom_geni")) {
|
||||
has_earlycon = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_earlycon)
|
||||
goto exit;
|
||||
|
||||
wrapper->to_core.path = devm_of_icc_get(dev, "qup-core");
|
||||
if (IS_ERR(wrapper->to_core.path))
|
||||
return PTR_ERR(wrapper->to_core.path);
|
||||
/*
|
||||
* Put minmal BW request on core clocks on behalf of early console.
|
||||
* The vote will be removed earlycon exit function.
|
||||
*
|
||||
* Note: We are putting vote on each QUP wrapper instead only to which
|
||||
* earlycon is connected because QUP core clock of different wrapper
|
||||
* share same voltage domain. If core1 is put to 0, then core2 will
|
||||
* also run at 0, if not voted. Default ICC vote will be removed ASA
|
||||
* we touch any of the core clock.
|
||||
* core1 = core2 = max(core1, core2)
|
||||
*/
|
||||
ret = icc_set_bw(wrapper->to_core.path, GENI_DEFAULT_BW,
|
||||
GENI_DEFAULT_BW);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: ICC BW voting failed for core: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (of_get_compatible_child(pdev->dev.of_node, "qcom,geni-debug-uart"))
|
||||
earlycon_wrapper = wrapper;
|
||||
of_node_put(pdev->dev.of_node);
|
||||
exit:
|
||||
#endif
|
||||
dev_set_drvdata(dev, wrapper);
|
||||
dev_dbg(dev, "GENI SE Driver probed\n");
|
||||
return devm_of_platform_populate(dev);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user