mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
soc: drivers for 6.16
Updates are across the usual driver subsystems with SoC specific drivers: - added soc specicific drivers for sophgo cv1800 and sg2044, qualcomm sm8750, and amlogic c3 and s4 chips. - cache controller updates for sifive chips, plus binding changes for other cache descriptions. - memory controller drivers for mediatek mt6893, stm32 and cleanups for a few more drivers - reset controller drivers for T-Head TH1502, Sophgo sg2044 and Renesas RZ/V2H(P) - SCMI firmware updates to better deal with buggy firmware, plus better support for Qualcomm X1E and NXP i.MX specific interfaces - a new platform driver for the crypto firmware on Cznic Turris Omnia/MOX - cleanups for the TEE firmware subsystem and amdtee driver - minor updates and fixes for freescale/nxp, qualcomm, google, aspeed, wondermedia, ti, nxp, renesas, hisilicon, mediatek, broadcom and samsung SoCs -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmg51FsACgkQmmx57+YA GNnJ0w//Y2OzxBE/Mek7n7d4ibrdlSF9SX8JbL2W++o8Mb3YEJwCtGyhD7OQVYn3 ZuaiY0aHi9PugoHYrfqT8Q6o0GgYrPMgWwHRH+cPvzRng1FKFObdPqpv2+xXdEcu zhMM0AkhQ3/kegXOydBWaDETSc9dc74RWegKAc2onxD51NkjbP1MJMOO+gdSnwGq +NeDe4omberJR/2He9K0XDxK0wtIsNVZ+CYXF0gQlGwmxkKcTgJQnUm6gqChYR9T KhZedI0f61XTk54LTX3OCcbMpdQHxl8l7qI25+HHnMaUuqUSNj+ZKm8YeboywkLW T8J0oS6Dyaiypv1oYrmtEPbtCniullSpfE7mrMljlljuLYMIWkFllSXAlC5EjjOG GkNcW0k13cRGZ0sBliWoorzXB5qQRkQhQn1KBPBA/n8fEuFFf+GTbmjE2Q+BixkN Jthhyk+PZbZIOid7Y7ecxTEs/RR8SLRmTYwWr/szVUW7WRp3NqWvyZl9X/nYylf8 aw3ugwcRCNKd4ba35y0XR0x8jy4wRQtlQqqxpkiJgREAmS9U9WBWH8bxSj3i5jGu nvMMtHmcDZ3gDGdUT6is7susOZLRuc8E7Y5dyn+NB4UoAxUDqRAn8ZGSNiOCcjQa 07vWhT4NTLEJeexm3gdfTU1sRtrIjas+qxNg8IpPvRyGOoefJ7w= =RhpA -----END PGP SIGNATURE----- Merge tag 'soc-drivers-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull SoC driver updates from Arnd Bergmann: "Updates are across the usual driver subsystems with SoC specific drivers: - added soc specicific drivers for sophgo cv1800 and sg2044, qualcomm sm8750, and amlogic c3 and s4 chips. - cache controller updates for sifive chips, plus binding changes for other cache descriptions. - memory controller drivers for mediatek mt6893, stm32 and cleanups for a few more drivers - reset controller drivers for T-Head TH1502, Sophgo sg2044 and Renesas RZ/V2H(P) - SCMI firmware updates to better deal with buggy firmware, plus better support for Qualcomm X1E and NXP i.MX specific interfaces - a new platform driver for the crypto firmware on Cznic Turris Omnia/MOX - cleanups for the TEE firmware subsystem and amdtee driver - minor updates and fixes for freescale/nxp, qualcomm, google, aspeed, wondermedia, ti, nxp, renesas, hisilicon, mediatek, broadcom and samsung SoCs" * tag 'soc-drivers-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (133 commits) soc: aspeed: Add NULL check in aspeed_lpc_enable_snoop() soc: aspeed: lpc: Fix impossible judgment condition ARM: aspeed: Don't select SRAM docs: firmware: qcom_scm: Fix kernel-doc warning soc: fsl: qe: Consolidate chained IRQ handler install/remove firmware: qcom: scm: Allow QSEECOM for HP EliteBook Ultra G1q dt-bindings: mfd: qcom,tcsr: Add compatible for ipq5018 dt-bindings: cache: add QiLai compatible to ax45mp memory: stm32_omm: Fix error handling in stm32_omm_disable_child() dt-bindings: cache: Convert marvell,tauros2-cache to DT schema dt-bindings: cache: Convert marvell,{feroceon,kirkwood}-cache to DT schema soc: samsung: exynos-pmu: enable CPU hotplug support for gs101 MAINTAINERS: Add google,gs101-pmu-intr-gen.yaml binding file dt-bindings: soc: samsung: exynos-pmu: gs101: add google,pmu-intr-gen phandle dt-bindings: soc: google: Add gs101-pmu-intr-gen binding documentation bus: fsl-mc: Use strscpy() instead of strscpy_pad() soc: fsl: qbman: Remove const from portal->cgrs allocation type bus: fsl_mc: Fix driver_managed_dma check bus: fsl-mc: increase MC_CMD_COMPLETION_TIMEOUT_MS value bus: fsl-mc: drop useless cleanup ...
This commit is contained in:
commit
297d9111e9
@ -31,6 +31,42 @@ Description: SCMI Raw asynchronous message injection/snooping facility; write
|
||||
(receiving an EOF at each message boundary).
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/message_poll
|
||||
Date: June 2025
|
||||
KernelVersion: 6.16
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw message injection/snooping facility using polling mode;
|
||||
write a complete SCMI command message (header included) in
|
||||
little-endian binary format to have it sent to the configured
|
||||
backend SCMI server for instance <n>, using polling mode on
|
||||
the reception path. (if transport is polling capable)
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived within the configured timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/message_poll_async
|
||||
Date: June 2025
|
||||
KernelVersion: 6.16
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw asynchronous message injection/snooping facility using
|
||||
polling-mode; write a complete SCMI asynchronous command message
|
||||
(header included) in little-endian binary format to have it sent
|
||||
to the configured backend SCMI server for instance <n>, using
|
||||
polling-mode on the reception path of the immediate part of the
|
||||
asynchronous command. (if transport is polling capable)
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived within the configured timeout.
|
||||
Any additional delayed response received afterwards can be read
|
||||
from this same entry too if it arrived within the configured
|
||||
timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/errors
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
@ -115,3 +151,58 @@ Description: SCMI Raw asynchronous message injection/snooping facility; write
|
||||
exist only if the transport is configured to have more than
|
||||
one default channel.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/channels/<m>/message_poll
|
||||
Date: June 2025
|
||||
KernelVersion: 6.16
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw message injection/snooping facility using polling mode;
|
||||
write a complete SCMI command message (header included) in
|
||||
little-endian binary format to have it sent to the configured
|
||||
backend SCMI server for instance <n> through the <m> transport
|
||||
channel, using polling mode on the reception path.
|
||||
(if transport is polling capable)
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived on channel <m> within the configured
|
||||
timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Channel identifier <m> matches the SCMI protocol number which
|
||||
has been associated with this transport channel in the DT
|
||||
description, with base protocol number 0x10 being the default
|
||||
channel for this instance.
|
||||
Note that these per-channel entries rooted at <..>/channels
|
||||
exist only if the transport is configured to have more than
|
||||
one default channel.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/channels/<m>/message_poll_async
|
||||
Date: June 2025
|
||||
KernelVersion: 6.16
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw asynchronous message injection/snooping facility using
|
||||
polling-mode; write a complete SCMI asynchronous command message
|
||||
(header included) in little-endian binary format to have it sent
|
||||
to the configured backend SCMI server for instance <n> through
|
||||
the <m> transport channel, using polling mode on the reception
|
||||
path of the immediate part of the asynchronous command.
|
||||
(if transport is polling capable)
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived on channel <m> within the configured
|
||||
timeout.
|
||||
Any additional delayed response received afterwards can be read
|
||||
from this same entry too if it arrived within the configured
|
||||
timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Channel identifier <m> matches the SCMI protocol number which
|
||||
has been associated with this transport channel in the DT
|
||||
description, with base protocol number 0x10 being the default
|
||||
channel for this instance.
|
||||
Note that these per-channel entries rooted at <..>/channels
|
||||
exist only if the transport is configured to have more than
|
||||
one default channel.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
@ -1,14 +0,0 @@
|
||||
What: /sys/kernel/debug/turris-mox-rwtm/do_sign
|
||||
Date: Jun 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: Marek Behún <kabel@kernel.org>
|
||||
Description:
|
||||
|
||||
======= ===========================================================
|
||||
(Write) 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).
|
||||
(Read) The resulting signature, 136 bytes. This contains the
|
||||
R and S values of the ECDSA signature, both in
|
||||
big-endian format.
|
||||
======= ===========================================================
|
@ -12,15 +12,6 @@ Contact: Marek Behún <kabel@kernel.org>
|
||||
Description: (Read) MAC addresses burned into eFuses of this Turris Mox board.
|
||||
Format: %pM
|
||||
|
||||
What: /sys/firmware/turris-mox-rwtm/pubkey
|
||||
Date: August 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: Marek Behún <kabel@kernel.org>
|
||||
Description: (Read) ECDSA public key (in pubkey hex compressed form) computed
|
||||
as pair to the ECDSA private key burned into eFuses of this
|
||||
Turris Mox Board.
|
||||
Format: string
|
||||
|
||||
What: /sys/firmware/turris-mox-rwtm/ram_size
|
||||
Date: August 2019
|
||||
KernelVersion: 5.4
|
||||
|
@ -28,6 +28,9 @@ select:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- andestech,qilai-ax45mp-cache
|
||||
- renesas,r9a07g043f-ax45mp-cache
|
||||
- const: andestech,ax45mp-cache
|
||||
- const: cache
|
||||
|
||||
@ -65,12 +68,27 @@ required:
|
||||
- cache-size
|
||||
- cache-unified
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: andestech,qilai-ax45mp-cache
|
||||
|
||||
then:
|
||||
properties:
|
||||
cache-sets:
|
||||
const: 2048
|
||||
cache-size:
|
||||
const: 2097152
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
cache-controller@13400000 {
|
||||
compatible = "andestech,ax45mp-cache", "cache";
|
||||
compatible = "renesas,r9a07g043f-ax45mp-cache", "andestech,ax45mp-cache",
|
||||
"cache";
|
||||
reg = <0x13400000 0x100000>;
|
||||
interrupts = <508 IRQ_TYPE_LEVEL_HIGH>;
|
||||
cache-line-size = <64>;
|
||||
|
@ -1,16 +0,0 @@
|
||||
* Marvell Feroceon Cache
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be either "marvell,feroceon-cache" or
|
||||
"marvell,kirkwood-cache".
|
||||
|
||||
Optional properties:
|
||||
- reg : Address of the L2 cache control register. Mandatory for
|
||||
"marvell,kirkwood-cache", not used by "marvell,feroceon-cache"
|
||||
|
||||
|
||||
Example:
|
||||
l2: l2-cache@20128 {
|
||||
compatible = "marvell,kirkwood-cache";
|
||||
reg = <0x20128 0x4>;
|
||||
};
|
45
Documentation/devicetree/bindings/cache/marvell,kirkwood-cache.yaml
vendored
Normal file
45
Documentation/devicetree/bindings/cache/marvell,kirkwood-cache.yaml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/cache/marvell,kirkwood-cache.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Marvell Feroceon/Kirkwood Cache
|
||||
|
||||
maintainers:
|
||||
- Andrew Lunn <andrew@lunn.ch>
|
||||
- Gregory Clement <gregory.clement@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- marvell,feroceon-cache
|
||||
- marvell,kirkwood-cache
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: marvell,kirkwood-cache
|
||||
then:
|
||||
required:
|
||||
- reg
|
||||
else:
|
||||
properties:
|
||||
reg: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
l2-cache@20128 {
|
||||
compatible = "marvell,kirkwood-cache";
|
||||
reg = <0x20128 0x4>;
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
* Marvell Tauros2 Cache
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "marvell,tauros2-cache".
|
||||
- marvell,tauros2-cache-features : Specify the features supported for the
|
||||
tauros2 cache.
|
||||
The features including
|
||||
CACHE_TAUROS2_PREFETCH_ON (1 << 0)
|
||||
CACHE_TAUROS2_LINEFILL_BURST8 (1 << 1)
|
||||
The definition can be found at
|
||||
arch/arm/include/asm/hardware/cache-tauros2.h
|
||||
|
||||
Example:
|
||||
L2: l2-cache {
|
||||
compatible = "marvell,tauros2-cache";
|
||||
marvell,tauros2-cache-features = <0x3>;
|
||||
};
|
39
Documentation/devicetree/bindings/cache/marvell,tauros2-cache.yaml
vendored
Normal file
39
Documentation/devicetree/bindings/cache/marvell,tauros2-cache.yaml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/cache/marvell,tauros2-cache.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Marvell Tauros2 Cache
|
||||
|
||||
maintainers:
|
||||
- Andrew Lunn <andrew@lunn.ch>
|
||||
- Gregory Clement <gregory.clement@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: marvell,tauros2-cache
|
||||
|
||||
marvell,tauros2-cache-features:
|
||||
description: >
|
||||
Specify the features supported for the tauros2 cache. The features include:
|
||||
|
||||
- CACHE_TAUROS2_PREFETCH_ON (1 << 0)
|
||||
- CACHE_TAUROS2_LINEFILL_BURST8 (1 << 1)
|
||||
|
||||
The definition can be found at arch/arm/include/asm/hardware/cache-tauros2.h
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 0x3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- marvell,tauros2-cache-features
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
l2-cache {
|
||||
compatible = "marvell,tauros2-cache";
|
||||
marvell,tauros2-cache-features = <0x3>;
|
||||
};
|
@ -40,6 +40,7 @@ properties:
|
||||
- qcom,sm8450-llcc
|
||||
- qcom,sm8550-llcc
|
||||
- qcom,sm8650-llcc
|
||||
- qcom,sm8750-llcc
|
||||
- qcom,x1e80100-llcc
|
||||
|
||||
reg:
|
||||
@ -274,6 +275,7 @@ allOf:
|
||||
- qcom,sm8450-llcc
|
||||
- qcom,sm8550-llcc
|
||||
- qcom,sm8650-llcc
|
||||
- qcom,sm8750-llcc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
|
@ -39,6 +39,7 @@ properties:
|
||||
- const: cache
|
||||
- items:
|
||||
- enum:
|
||||
- eswin,eic7700-l3-cache
|
||||
- starfive,jh7100-ccache
|
||||
- starfive,jh7110-ccache
|
||||
- const: sifive,ccache0
|
||||
@ -55,10 +56,10 @@ properties:
|
||||
enum: [2, 3]
|
||||
|
||||
cache-sets:
|
||||
enum: [1024, 2048]
|
||||
enum: [1024, 2048, 4096]
|
||||
|
||||
cache-size:
|
||||
const: 2097152
|
||||
enum: [2097152, 4194304]
|
||||
|
||||
cache-unified: true
|
||||
|
||||
@ -89,6 +90,7 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- eswin,eic7700-l3-cache
|
||||
- sifive,fu740-c000-ccache
|
||||
- starfive,jh7100-ccache
|
||||
- starfive,jh7110-ccache
|
||||
@ -108,6 +110,22 @@ allOf:
|
||||
Must contain entries for DirError, DataError and DataFail signals.
|
||||
maxItems: 3
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: eswin,eic7700-l3-cache
|
||||
|
||||
then:
|
||||
properties:
|
||||
cache-size:
|
||||
const: 4194304
|
||||
|
||||
else:
|
||||
properties:
|
||||
cache-size:
|
||||
const: 2097152
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -122,11 +140,31 @@ allOf:
|
||||
cache-sets:
|
||||
const: 2048
|
||||
|
||||
else:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- microchip,mpfs-ccache
|
||||
- sifive,fu540-c000-ccache
|
||||
|
||||
then:
|
||||
properties:
|
||||
cache-sets:
|
||||
const: 1024
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- eswin,eic7700-l3-cache
|
||||
|
||||
then:
|
||||
properties:
|
||||
cache-sets:
|
||||
const: 4096
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -27,6 +27,15 @@ properties:
|
||||
mboxes:
|
||||
maxItems: 1
|
||||
|
||||
pmic:
|
||||
description: Child node describing the main PMIC.
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: samsung,s2mpg10-pmic
|
||||
|
||||
shmem:
|
||||
description:
|
||||
List of phandle pointing to the shared memory (SHM) area. The memory
|
||||
@ -43,8 +52,34 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
power-management {
|
||||
compatible = "google,gs101-acpm-ipc";
|
||||
mboxes = <&ap2apm_mailbox>;
|
||||
shmem = <&apm_sram>;
|
||||
|
||||
pmic {
|
||||
compatible = "samsung,s2mpg10-pmic";
|
||||
interrupts-extended = <&gpa0 6 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
regulators {
|
||||
LDO1 {
|
||||
regulator-name = "vdd_ldo1";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
BUCK1 {
|
||||
regulator-name = "vdd_mif";
|
||||
regulator-min-microvolt = <450000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -11,6 +11,18 @@ maintainers:
|
||||
- Peng Fan <peng.fan@nxp.com>
|
||||
|
||||
properties:
|
||||
protocol@80:
|
||||
description:
|
||||
SCMI LMM protocol which is for boot, shutdown, and reset of other logical
|
||||
machines (LM). It is usually used to allow one LM to manage another used
|
||||
as an offload or accelerator engine.
|
||||
$ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0x80
|
||||
|
||||
protocol@81:
|
||||
$ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
@ -19,6 +31,17 @@ properties:
|
||||
reg:
|
||||
const: 0x81
|
||||
|
||||
protocol@82:
|
||||
description:
|
||||
SCMI CPU Protocol which allows an agent to start or stop a CPU. It is
|
||||
used to manage auxiliary CPUs in a LM.
|
||||
$ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0x82
|
||||
|
||||
protocol@84:
|
||||
$ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
|
@ -0,0 +1,37 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwinfo/via,vt8500-scc-id.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: VIA/WonderMedia SoC system configuration information
|
||||
|
||||
maintainers:
|
||||
- Alexey Charkov <alchark@gmail.com>
|
||||
|
||||
description:
|
||||
The system configuration controller on VIA/WonderMedia SoC's contains a chip
|
||||
identifier and revision used to differentiate between different hardware
|
||||
versions of on-chip IP blocks having their own peculiarities which may or
|
||||
may not be captured by their respective DT compatible strings
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: via,vt8500-scc-id
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
chipid@d8120000 {
|
||||
compatible = "via,vt8500-scc-id";
|
||||
reg = <0xd8120000 0x4>;
|
||||
};
|
@ -33,6 +33,7 @@ properties:
|
||||
- mediatek,mt2712-smi-common
|
||||
- mediatek,mt6779-smi-common
|
||||
- mediatek,mt6795-smi-common
|
||||
- mediatek,mt6893-smi-common
|
||||
- mediatek,mt8167-smi-common
|
||||
- mediatek,mt8173-smi-common
|
||||
- mediatek,mt8183-smi-common
|
||||
|
@ -21,6 +21,7 @@ properties:
|
||||
- mediatek,mt2712-smi-larb
|
||||
- mediatek,mt6779-smi-larb
|
||||
- mediatek,mt6795-smi-larb
|
||||
- mediatek,mt6893-smi-larb
|
||||
- mediatek,mt8167-smi-larb
|
||||
- mediatek,mt8173-smi-larb
|
||||
- mediatek,mt8183-smi-larb
|
||||
|
@ -0,0 +1,226 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/st,stm32mp25-omm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: STM32 Octo Memory Manager (OMM)
|
||||
|
||||
maintainers:
|
||||
- Patrice Chotard <patrice.chotard@foss.st.com>
|
||||
|
||||
description: |
|
||||
The STM32 Octo Memory Manager is a low-level interface that enables an
|
||||
efficient OCTOSPI pin assignment with a full I/O matrix (before alternate
|
||||
function map) and multiplex of single/dual/quad/octal SPI interfaces over
|
||||
the same bus. It Supports up to:
|
||||
- Two single/dual/quad/octal SPI interfaces
|
||||
- Two ports for pin assignment
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32mp25-omm
|
||||
|
||||
"#address-cells":
|
||||
const: 2
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
ranges:
|
||||
description: |
|
||||
Reflects the memory layout per OSPI instance.
|
||||
Format:
|
||||
<chip-select> 0 <registers base address> <size>
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: OMM registers
|
||||
- description: OMM memory map area
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: regs
|
||||
- const: memory_map
|
||||
|
||||
memory-region:
|
||||
description:
|
||||
Memory region shared between the 2 OCTOSPI instance.
|
||||
One or two phandle to a node describing a memory mapped region
|
||||
depending of child number.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
memory-region-names:
|
||||
description:
|
||||
Identify to which OSPI instance the memory region belongs to.
|
||||
items:
|
||||
enum: [ospi1, ospi2]
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clocks:
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: omm
|
||||
- const: ospi1
|
||||
- const: ospi2
|
||||
|
||||
resets:
|
||||
maxItems: 3
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: omm
|
||||
- const: ospi1
|
||||
- const: ospi2
|
||||
|
||||
access-controllers:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
st,syscfg-amcr:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: |
|
||||
The Address Mapping Control Register (AMCR) is used to split the 256MB
|
||||
memory map area shared between the 2 OSPI instance. The Octo Memory
|
||||
Manager sets the AMCR depending of the memory-region configuration.
|
||||
The memory split bitmask description is:
|
||||
- 000: OCTOSPI1 (256 Mbytes), OCTOSPI2 unmapped
|
||||
- 001: OCTOSPI1 (192 Mbytes), OCTOSPI2 (64 Mbytes)
|
||||
- 010: OCTOSPI1 (128 Mbytes), OCTOSPI2 (128 Mbytes)
|
||||
- 011: OCTOSPI1 (64 Mbytes), OCTOSPI2 (192 Mbytes)
|
||||
- 1xx: OCTOSPI1 unmapped, OCTOSPI2 (256 Mbytes)
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to syscfg
|
||||
- description: register offset within syscfg
|
||||
- description: register bitmask for memory split
|
||||
|
||||
st,omm-req2ack-ns:
|
||||
description:
|
||||
In multiplexed mode (MUXEN = 1), this field defines the time in
|
||||
nanoseconds between two transactions.
|
||||
default: 0
|
||||
|
||||
st,omm-cssel-ovr:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Configure the chip select selector override for the 2 OCTOSPIs.
|
||||
- 0: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS1
|
||||
- 1: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS1
|
||||
- 2: OCTOSPI1 chip select send to NCS1 OCTOSPI2 chip select send to NCS2
|
||||
- 3: OCTOSPI1 chip select send to NCS2 OCTOSPI2 chip select send to NCS2
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 0
|
||||
|
||||
st,omm-mux:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Configure the muxing between the 2 OCTOSPIs busses and the 2 output ports.
|
||||
- 0: direct mode
|
||||
- 1: mux OCTOSPI1 and OCTOSPI2 to port 1
|
||||
- 2: swapped mode
|
||||
- 3: mux OCTOSPI1 and OCTOSPI2 to port 2
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 0
|
||||
|
||||
patternProperties:
|
||||
^spi@[0-9]:
|
||||
type: object
|
||||
$ref: /schemas/spi/st,stm32mp25-ospi.yaml#
|
||||
description: Required spi child node
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- st,syscfg-amcr
|
||||
- ranges
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/st,stm32mp25-rcc.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/reset/st,stm32mp25-rcc.h>
|
||||
ommanager@40500000 {
|
||||
compatible = "st,stm32mp25-omm";
|
||||
reg = <0x40500000 0x400>, <0x60000000 0x10000000>;
|
||||
reg-names = "regs", "memory_map";
|
||||
ranges = <0 0 0x40430000 0x400>,
|
||||
<1 0 0x40440000 0x400>;
|
||||
memory-region = <&mm_ospi1>, <&mm_ospi2>;
|
||||
memory-region-names = "ospi1", "ospi2";
|
||||
pinctrl-0 = <&ospi_port1_clk_pins_a
|
||||
&ospi_port1_io03_pins_a
|
||||
&ospi_port1_cs0_pins_a>;
|
||||
pinctrl-1 = <&ospi_port1_clk_sleep_pins_a
|
||||
&ospi_port1_io03_sleep_pins_a
|
||||
&ospi_port1_cs0_sleep_pins_a>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
clocks = <&rcc CK_BUS_OSPIIOM>,
|
||||
<&scmi_clk CK_SCMI_OSPI1>,
|
||||
<&scmi_clk CK_SCMI_OSPI2>;
|
||||
clock-names = "omm", "ospi1", "ospi2";
|
||||
resets = <&rcc OSPIIOM_R>,
|
||||
<&scmi_reset RST_SCMI_OSPI1>,
|
||||
<&scmi_reset RST_SCMI_OSPI2>;
|
||||
reset-names = "omm", "ospi1", "ospi2";
|
||||
access-controllers = <&rifsc 111>;
|
||||
power-domains = <&CLUSTER_PD>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
st,syscfg-amcr = <&syscfg 0x2c00 0x7>;
|
||||
st,omm-req2ack-ns = <0>;
|
||||
st,omm-mux = <0>;
|
||||
st,omm-cssel-ovr = <0>;
|
||||
|
||||
spi@0 {
|
||||
compatible = "st,stm32mp25-ospi";
|
||||
reg = <0 0 0x400>;
|
||||
memory-region = <&mm_ospi1>;
|
||||
interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&hpdma 2 0x62 0x00003121 0x0>,
|
||||
<&hpdma 2 0x42 0x00003112 0x0>;
|
||||
dma-names = "tx", "rx";
|
||||
clocks = <&scmi_clk CK_SCMI_OSPI1>;
|
||||
resets = <&scmi_reset RST_SCMI_OSPI1>, <&scmi_reset RST_SCMI_OSPI1DLL>;
|
||||
access-controllers = <&rifsc 74>;
|
||||
power-domains = <&CLUSTER_PD>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
st,syscfg-dlyb = <&syscfg 0x1000>;
|
||||
};
|
||||
|
||||
spi@1 {
|
||||
compatible = "st,stm32mp25-ospi";
|
||||
reg = <1 0 0x400>;
|
||||
memory-region = <&mm_ospi1>;
|
||||
interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&hpdma 3 0x62 0x00003121 0x0>,
|
||||
<&hpdma 3 0x42 0x00003112 0x0>;
|
||||
dma-names = "tx", "rx";
|
||||
clocks = <&scmi_clk CK_KER_OSPI2>;
|
||||
resets = <&scmi_reset RST_SCMI_OSPI2>, <&scmi_reset RST_SCMI_OSPI1DLL>;
|
||||
access-controllers = <&rifsc 75>;
|
||||
power-domains = <&CLUSTER_PD>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
st,syscfg-dlyb = <&syscfg 0x1000>;
|
||||
};
|
||||
};
|
@ -41,6 +41,7 @@ properties:
|
||||
- qcom,sm8450-tcsr
|
||||
- qcom,tcsr-apq8064
|
||||
- qcom,tcsr-apq8084
|
||||
- qcom,tcsr-ipq5018
|
||||
- qcom,tcsr-ipq5332
|
||||
- qcom,tcsr-ipq5424
|
||||
- qcom,tcsr-ipq6018
|
||||
|
@ -12,14 +12,20 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson8b-reset # Reset Controller on Meson8b and compatible SoCs
|
||||
- amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs
|
||||
- amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs
|
||||
- amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs
|
||||
- amlogic,meson-s4-reset # Reset Controller on S4 and compatible SoCs
|
||||
- amlogic,c3-reset # Reset Controller on C3 and compatible SoCs
|
||||
- amlogic,t7-reset
|
||||
oneOf:
|
||||
- enum:
|
||||
- amlogic,meson8b-reset # Reset Controller on Meson8b and compatible SoCs
|
||||
- amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs
|
||||
- amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs
|
||||
- amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs
|
||||
- amlogic,meson-s4-reset # Reset Controller on S4 and compatible SoCs
|
||||
- amlogic,c3-reset # Reset Controller on C3 and compatible SoCs
|
||||
- amlogic,t7-reset
|
||||
- items:
|
||||
- enum:
|
||||
- amlogic,a4-reset
|
||||
- amlogic,a5-reset
|
||||
- const: amlogic,meson-s4-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/renesas,rzv2h-usb2phy-reset.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/V2H(P) USB2PHY Port reset Control
|
||||
|
||||
maintainers:
|
||||
- Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
|
||||
|
||||
description:
|
||||
The RZ/V2H(P) USB2PHY Control mainly controls Port reset and power down of the
|
||||
USB2.0 PHY.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P)
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
'#reset-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- resets
|
||||
- power-domains
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/renesas,r9a09g057-cpg.h>
|
||||
|
||||
reset-controller@15830000 {
|
||||
compatible = "renesas,r9a09g057-usb2phy-reset";
|
||||
reg = <0x15830000 0x10000>;
|
||||
clocks = <&cpg CPG_MOD 0xb6>;
|
||||
resets = <&cpg 0xaf>;
|
||||
power-domains = <&cpg>;
|
||||
#reset-cells = <0>;
|
||||
};
|
@ -11,7 +11,12 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sophgo,sg2042-reset
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- sophgo,sg2044-reset
|
||||
- const: sophgo,sg2042-reset
|
||||
- const: sophgo,sg2042-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/thead,th1520-reset.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: T-HEAD TH1520 SoC Reset Controller
|
||||
|
||||
description:
|
||||
The T-HEAD TH1520 reset controller is a hardware block that asserts/deasserts
|
||||
resets for SoC subsystems.
|
||||
|
||||
maintainers:
|
||||
- Michal Wilczynski <m.wilczynski@samsung.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- thead,th1520-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#reset-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
rst: reset-controller@ffef528000 {
|
||||
compatible = "thead,th1520-reset";
|
||||
reg = <0xff 0xef528000 0x0 0x1000>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
};
|
@ -22,6 +22,8 @@ properties:
|
||||
- amlogic,meson-axg-clk-measure
|
||||
- amlogic,meson-g12a-clk-measure
|
||||
- amlogic,meson-sm1-clk-measure
|
||||
- amlogic,c3-clk-measure
|
||||
- amlogic,s4-clk-measure
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -0,0 +1,35 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/google/google,gs101-pmu-intr-gen.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Google Power Management Unit (PMU) Interrupt Generation
|
||||
|
||||
description: |
|
||||
PMU interrupt generator for handshaking between PMU through interrupts.
|
||||
|
||||
maintainers:
|
||||
- Peter Griffin <peter.griffin@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: google,gs101-pmu-intr-gen
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
pmu_intr_gen: syscon@17470000 {
|
||||
compatible = "google,gs101-pmu-intr-gen", "syscon";
|
||||
reg = <0x17470000 0x10000>;
|
||||
};
|
@ -23,6 +23,7 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- mediatek,mt6893-dvfsrc
|
||||
- mediatek,mt8183-dvfsrc
|
||||
- mediatek,mt8195-dvfsrc
|
||||
- items:
|
||||
|
@ -36,6 +36,13 @@ properties:
|
||||
- const: err
|
||||
- const: wakeup
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ram
|
||||
|
||||
qcom,ipc:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
@ -46,6 +53,14 @@ properties:
|
||||
description:
|
||||
Three entries specifying the outgoing ipc bit used for signaling the RPM.
|
||||
|
||||
clock-controller:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,rpmcc
|
||||
|
||||
patternProperties:
|
||||
"^regulators(-[01])?$":
|
||||
type: object
|
||||
|
@ -44,7 +44,13 @@ description: |
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,rpmh-rsc
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sc7180-rpmh-apps-rsc
|
||||
- qcom,sdm845-rpmh-apps-rsc
|
||||
- const: qcom,rpmh-rsc
|
||||
- const: qcom,rpmh-rsc
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
@ -124,7 +130,21 @@ required:
|
||||
- qcom,tcs-offset
|
||||
- reg
|
||||
- reg-names
|
||||
- power-domains
|
||||
|
||||
allOf:
|
||||
# Some platforms may lack a OSI-mode PSCI implementation, which implies the
|
||||
# system power domain can't provide feedback about entering power collapse
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc7180-rpmh-apps-rsc
|
||||
- qcom,sdm845-rpmh-apps-rsc
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -129,6 +129,11 @@ properties:
|
||||
description:
|
||||
Node for reboot method
|
||||
|
||||
google,pmu-intr-gen-syscon:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to PMU interrupt generation interface.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -189,6 +194,16 @@ allOf:
|
||||
properties:
|
||||
dp-phy: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- google,gs101-pmu
|
||||
then:
|
||||
required:
|
||||
- google,pmu-intr-gen-syscon
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos5250.h>
|
||||
|
@ -0,0 +1,86 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sophgo/sophgo,cv1800b-rtc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Real Time Clock of the Sophgo CV1800 SoC
|
||||
|
||||
description:
|
||||
The RTC (Real Time Clock) is an independently powered module in the chip. It
|
||||
contains a 32KHz oscillator and a Power-On-Reset (POR) sub-module, which can
|
||||
be used for time display and scheduled alarm produce. In addition, the
|
||||
hardware state machine provides triggering and timing control for chip
|
||||
power-on, power-off and reset.
|
||||
|
||||
Furthermore, the 8051 subsystem is located within RTCSYS and is independently
|
||||
powered. System software can use the 8051 to manage wake conditions and wake
|
||||
the system while the system is asleep, and communicate with external devices
|
||||
through peripheral controllers.
|
||||
|
||||
Technical Reference Manual available at
|
||||
https://github.com/sophgo/sophgo-doc/tree/main/SG200X/TRM
|
||||
|
||||
maintainers:
|
||||
- sophgo@lists.linux.dev
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/rtc/rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: sophgo,cv1800b-rtc
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: RTC Alarm
|
||||
- description: RTC Longpress
|
||||
- description: VBAT DET
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: alarm
|
||||
- const: longpress
|
||||
- const: vbat
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: RTC clock source
|
||||
- description: DW8051 MCU clock source
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: rtc
|
||||
- const: mcu
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/sophgo,cv1800.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
rtc@5025000 {
|
||||
compatible = "sophgo,cv1800b-rtc", "syscon";
|
||||
reg = <0x5025000 0x2000>;
|
||||
interrupts = <17 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<18 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<19 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "alarm", "longpress", "vbat";
|
||||
clocks = <&clk CLK_RTC_25M>,
|
||||
<&clk CLK_SRC_RTC_SYS_0>;
|
||||
clock-names = "rtc", "mcu";
|
||||
};
|
32
MAINTAINERS
32
MAINTAINERS
@ -2537,6 +2537,7 @@ F: include/dt-bindings/bus/moxtet.h
|
||||
F: include/linux/armada-37xx-rwtm-mailbox.h
|
||||
F: include/linux/moxtet.h
|
||||
F: include/linux/turris-omnia-mcu-interface.h
|
||||
F: include/linux/turris-signing-key.h
|
||||
|
||||
ARM/FARADAY FA526 PORT
|
||||
M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
|
||||
@ -3110,10 +3111,10 @@ F: arch/arm/include/debug/renesas-scif.S
|
||||
F: arch/arm/mach-shmobile/
|
||||
F: arch/arm64/boot/dts/renesas/
|
||||
F: arch/riscv/boot/dts/renesas/
|
||||
F: drivers/nvmem/rcar-efuse.c
|
||||
F: drivers/pmdomain/renesas/
|
||||
F: drivers/soc/renesas/
|
||||
F: include/linux/soc/renesas/
|
||||
N: rcar
|
||||
K: \brenesas,
|
||||
|
||||
ARM/RISCPC ARCHITECTURE
|
||||
@ -3480,6 +3481,7 @@ M: Alexey Charkov <alchark@gmail.com>
|
||||
M: Krzysztof Kozlowski <krzk@kernel.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Odd Fixes
|
||||
F: Documentation/devicetree/bindings/hwinfo/via,vt8500-scc-id.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/wm,wm8505-i2c.yaml
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/via,vt8500-intc.yaml
|
||||
F: Documentation/devicetree/bindings/pwm/via,vt8500-pwm.yaml
|
||||
@ -3490,6 +3492,7 @@ F: drivers/i2c/busses/i2c-viai2c-wmt.c
|
||||
F: drivers/mmc/host/wmt-sdmmc.c
|
||||
F: drivers/pwm/pwm-vt8500.c
|
||||
F: drivers/rtc/rtc-vt8500.c
|
||||
F: drivers/soc/vt8500/
|
||||
F: drivers/tty/serial/vt8500_serial.c
|
||||
F: drivers/video/fbdev/vt8500lcdfb.*
|
||||
F: drivers/video/fbdev/wm8505fb*
|
||||
@ -10194,6 +10197,7 @@ L: linux-samsung-soc@vger.kernel.org
|
||||
S: Maintained
|
||||
C: irc://irc.oftc.net/pixel6-kernel-dev
|
||||
F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
|
||||
F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml
|
||||
F: arch/arm64/boot/dts/exynos/google/
|
||||
F: drivers/clk/samsung/clk-gs101.c
|
||||
F: drivers/phy/samsung/phy-gs101-ufs.c
|
||||
@ -21015,6 +21019,14 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/renesas,r9a09g057-gbeth.yaml
|
||||
F: drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
|
||||
|
||||
RENESAS RZ/V2H(P) USB2PHY PORT RESET DRIVER
|
||||
M: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
|
||||
M: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml
|
||||
F: drivers/reset/reset-rzv2h-usb2phy.c
|
||||
|
||||
RENESAS RZ/V2M I2C DRIVER
|
||||
M: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
@ -21227,6 +21239,7 @@ F: Documentation/devicetree/bindings/firmware/thead,th1520-aon.yaml
|
||||
F: Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml
|
||||
F: Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml
|
||||
F: Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml
|
||||
F: Documentation/devicetree/bindings/reset/thead,th1520-reset.yaml
|
||||
F: arch/riscv/boot/dts/thead/
|
||||
F: drivers/clk/thead/clk-th1520-ap.c
|
||||
F: drivers/firmware/thead,th1520-aon.c
|
||||
@ -21234,8 +21247,10 @@ F: drivers/mailbox/mailbox-th1520.c
|
||||
F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
|
||||
F: drivers/pinctrl/pinctrl-th1520.c
|
||||
F: drivers/pmdomain/thead/
|
||||
F: drivers/reset/reset-th1520.c
|
||||
F: include/dt-bindings/clock/thead,th1520-clk-ap.h
|
||||
F: include/dt-bindings/power/thead,th1520-power.h
|
||||
F: include/dt-bindings/reset/thead,th1520-reset.h
|
||||
F: include/linux/firmware/thead/thead,th1520-aon.h
|
||||
|
||||
RNBD BLOCK DRIVERS
|
||||
@ -23296,6 +23311,12 @@ L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-stm32*
|
||||
|
||||
ST STM32 OCTO MEMORY MANAGER
|
||||
M: Patrice Chotard <patrice.chotard@foss.st.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/memory-controllers/st,stm32mp25-omm.yaml
|
||||
F: drivers/memory/stm32_omm.c
|
||||
|
||||
ST STM32 SPI DRIVER
|
||||
M: Alain Volmat <alain.volmat@foss.st.com>
|
||||
L: linux-spi@vger.kernel.org
|
||||
@ -23945,6 +23966,15 @@ F: include/linux/sc[mp]i_protocol.h
|
||||
F: include/trace/events/scmi.h
|
||||
F: include/uapi/linux/virtio_scmi.h
|
||||
|
||||
SYSTEM CONTROL MANAGEMENT INTERFACE (SCMI) i.MX Extension Message Protocol drivers
|
||||
M: Peng Fan <peng.fan@nxp.com>
|
||||
L: arm-scmi@vger.kernel.org
|
||||
L: imx@lists.linux.dev
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/firmware/nxp,*scmi.yaml
|
||||
F: drivers/firmware/arm_scmi/vendors/imx/
|
||||
|
||||
SYSTEM RESET/SHUTDOWN DRIVERS
|
||||
M: Sebastian Reichel <sre@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
|
@ -2,7 +2,6 @@
|
||||
menuconfig ARCH_ASPEED
|
||||
bool "Aspeed BMC architectures"
|
||||
depends on (CPU_LITTLE_ENDIAN && ARCH_MULTI_V5) || ARCH_MULTI_V6 || ARCH_MULTI_V7
|
||||
select SRAM
|
||||
select WATCHDOG
|
||||
select ASPEED_WATCHDOG
|
||||
select MFD_SYSCON
|
||||
|
@ -395,10 +395,7 @@ static struct attribute *gisb_arb_sysfs_attrs[] = {
|
||||
&dev_attr_gisb_arb_timeout.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group gisb_arb_sysfs_attr_group = {
|
||||
.attrs = gisb_arb_sysfs_attrs,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(gisb_arb_sysfs);
|
||||
|
||||
static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
|
||||
{ .compatible = "brcm,gisb-arb", .data = gisb_offsets_bcm7445 },
|
||||
@ -490,10 +487,6 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&pdev->dev.kobj, &gisb_arb_sysfs_attr_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
platform_set_drvdata(pdev, gdev);
|
||||
|
||||
list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list);
|
||||
@ -550,6 +543,7 @@ static struct platform_driver brcmstb_gisb_arb_driver = {
|
||||
.name = "brcm-gisb-arb",
|
||||
.of_match_table = brcmstb_gisb_arb_of_match,
|
||||
.pm = &brcmstb_gisb_arb_pm_ops,
|
||||
.dev_groups = gisb_arb_sysfs_groups,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -806,8 +806,6 @@ int dprc_cleanup(struct fsl_mc_device *mc_dev)
|
||||
dev_set_msi_domain(&mc_dev->dev, NULL);
|
||||
}
|
||||
|
||||
fsl_mc_cleanup_all_resource_pools(mc_dev);
|
||||
|
||||
/* if this step fails we cannot go further with cleanup as there is no way of
|
||||
* communicating with the firmware
|
||||
*/
|
||||
|
@ -489,7 +489,7 @@ int dprc_set_obj_irq(struct fsl_mc_io *mc_io,
|
||||
cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr);
|
||||
cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num);
|
||||
cmd_params->obj_id = cpu_to_le32(obj_id);
|
||||
strscpy_pad(cmd_params->obj_type, obj_type, 16);
|
||||
strscpy(cmd_params->obj_type, obj_type);
|
||||
|
||||
/* send command to mc*/
|
||||
return mc_send_command(mc_io, &cmd);
|
||||
@ -561,7 +561,7 @@ int dprc_get_obj_region(struct fsl_mc_io *mc_io,
|
||||
cmd_params = (struct dprc_cmd_get_obj_region *)cmd.params;
|
||||
cmd_params->obj_id = cpu_to_le32(obj_id);
|
||||
cmd_params->region_index = region_index;
|
||||
strscpy_pad(cmd_params->obj_type, obj_type, 16);
|
||||
strscpy(cmd_params->obj_type, obj_type);
|
||||
|
||||
/* send command to mc*/
|
||||
err = mc_send_command(mc_io, &cmd);
|
||||
|
@ -555,27 +555,6 @@ void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
|
||||
enum fsl_mc_pool_type pool_type)
|
||||
{
|
||||
struct fsl_mc_resource *resource;
|
||||
struct fsl_mc_resource *next;
|
||||
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
|
||||
struct fsl_mc_resource_pool *res_pool =
|
||||
&mc_bus->resource_pools[pool_type];
|
||||
|
||||
list_for_each_entry_safe(resource, next, &res_pool->free_list, node)
|
||||
devm_kfree(&mc_bus_dev->dev, resource);
|
||||
}
|
||||
|
||||
void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
|
||||
{
|
||||
int pool_type;
|
||||
|
||||
for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
|
||||
fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* fsl_mc_allocator_probe - callback invoked when an allocatable device is
|
||||
* being added to the system
|
||||
|
@ -139,9 +139,9 @@ static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *e
|
||||
|
||||
static int fsl_mc_dma_configure(struct device *dev)
|
||||
{
|
||||
const struct device_driver *drv = READ_ONCE(dev->driver);
|
||||
struct device *dma_dev = dev;
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
|
||||
u32 input_id = mc_dev->icid;
|
||||
int ret;
|
||||
|
||||
@ -153,8 +153,8 @@ static int fsl_mc_dma_configure(struct device *dev)
|
||||
else
|
||||
ret = acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id);
|
||||
|
||||
/* @mc_drv may not be valid when we're called from the IOMMU layer */
|
||||
if (!ret && dev->driver && !mc_drv->driver_managed_dma) {
|
||||
/* @drv may not be valid when we're called from the IOMMU layer */
|
||||
if (!ret && drv && !to_fsl_mc_driver(drv)->driver_managed_dma) {
|
||||
ret = iommu_device_use_default_domain(dev);
|
||||
if (ret)
|
||||
arch_teardown_dma_ops(dev);
|
||||
@ -906,8 +906,10 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
|
||||
|
||||
error_cleanup_dev:
|
||||
kfree(mc_dev->regions);
|
||||
kfree(mc_bus);
|
||||
kfree(mc_dev);
|
||||
if (mc_bus)
|
||||
kfree(mc_bus);
|
||||
else
|
||||
kfree(mc_dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -629,8 +629,6 @@ int __init fsl_mc_allocator_driver_init(void);
|
||||
|
||||
void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
|
||||
|
||||
void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
|
||||
|
||||
int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
|
||||
enum fsl_mc_pool_type pool_type,
|
||||
struct fsl_mc_resource
|
||||
|
@ -48,6 +48,7 @@ enum fsl_mc_cmd_index {
|
||||
DPRC_GET_POOL,
|
||||
DPRC_GET_POOL_COUNT,
|
||||
DPRC_GET_CONNECTION,
|
||||
DPRC_GET_MEM,
|
||||
DPCI_GET_LINK_STATE,
|
||||
DPCI_GET_PEER_ATTR,
|
||||
DPAIOP_GET_SL_VERSION,
|
||||
@ -194,6 +195,12 @@ static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
|
||||
.token = true,
|
||||
.size = 32,
|
||||
},
|
||||
[DPRC_GET_MEM] = {
|
||||
.cmdid_value = 0x16D0,
|
||||
.cmdid_mask = 0xFFF0,
|
||||
.token = true,
|
||||
.size = 12,
|
||||
},
|
||||
|
||||
[DPCI_GET_LINK_STATE] = {
|
||||
.cmdid_value = 0x0E10,
|
||||
@ -275,13 +282,13 @@ static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
|
||||
.size = 8,
|
||||
},
|
||||
[DPSW_GET_TAILDROP] = {
|
||||
.cmdid_value = 0x0A80,
|
||||
.cmdid_value = 0x0A90,
|
||||
.cmdid_mask = 0xFFF0,
|
||||
.token = true,
|
||||
.size = 14,
|
||||
},
|
||||
[DPSW_SET_TAILDROP] = {
|
||||
.cmdid_value = 0x0A90,
|
||||
.cmdid_value = 0x0A80,
|
||||
.cmdid_mask = 0xFFF0,
|
||||
.token = true,
|
||||
.size = 24,
|
||||
|
@ -214,12 +214,19 @@ int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
|
||||
if (error < 0)
|
||||
goto error_cleanup_resource;
|
||||
|
||||
dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev,
|
||||
&dpmcp_dev->dev,
|
||||
DL_FLAG_AUTOREMOVE_CONSUMER);
|
||||
if (!dpmcp_dev->consumer_link) {
|
||||
error = -EINVAL;
|
||||
goto error_cleanup_mc_io;
|
||||
/* If the DPRC device itself tries to allocate a portal (usually for
|
||||
* UAPI interaction), don't add a device link between them since the
|
||||
* DPMCP device is an actual child device of the DPRC and a reverse
|
||||
* dependency is not allowed.
|
||||
*/
|
||||
if (mc_dev != mc_bus_dev) {
|
||||
dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev,
|
||||
&dpmcp_dev->dev,
|
||||
DL_FLAG_AUTOREMOVE_CONSUMER);
|
||||
if (!dpmcp_dev->consumer_link) {
|
||||
error = -EINVAL;
|
||||
goto error_cleanup_mc_io;
|
||||
}
|
||||
}
|
||||
|
||||
*new_mc_io = mc_io;
|
||||
|
@ -19,7 +19,7 @@
|
||||
/*
|
||||
* Timeout in milliseconds to wait for the completion of an MC command
|
||||
*/
|
||||
#define MC_CMD_COMPLETION_TIMEOUT_MS 500
|
||||
#define MC_CMD_COMPLETION_TIMEOUT_MS 15000
|
||||
|
||||
/*
|
||||
* usleep_range() min and max values used to throttle down polling
|
||||
|
@ -677,51 +677,6 @@ static int sysc_parse_and_check_child_range(struct sysc *ddata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Interconnect instances to probe before l4_per instances */
|
||||
static struct resource early_bus_ranges[] = {
|
||||
/* am3/4 l4_wkup */
|
||||
{ .start = 0x44c00000, .end = 0x44c00000 + 0x300000, },
|
||||
/* omap4/5 and dra7 l4_cfg */
|
||||
{ .start = 0x4a000000, .end = 0x4a000000 + 0x300000, },
|
||||
/* omap4 l4_wkup */
|
||||
{ .start = 0x4a300000, .end = 0x4a300000 + 0x30000, },
|
||||
/* omap5 and dra7 l4_wkup without dra7 dcan segment */
|
||||
{ .start = 0x4ae00000, .end = 0x4ae00000 + 0x30000, },
|
||||
};
|
||||
|
||||
static atomic_t sysc_defer = ATOMIC_INIT(10);
|
||||
|
||||
/**
|
||||
* sysc_defer_non_critical - defer non_critical interconnect probing
|
||||
* @ddata: device driver data
|
||||
*
|
||||
* We want to probe l4_cfg and l4_wkup interconnect instances before any
|
||||
* l4_per instances as l4_per instances depend on resources on l4_cfg and
|
||||
* l4_wkup interconnects.
|
||||
*/
|
||||
static int sysc_defer_non_critical(struct sysc *ddata)
|
||||
{
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
if (!atomic_read(&sysc_defer))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(early_bus_ranges); i++) {
|
||||
res = &early_bus_ranges[i];
|
||||
if (ddata->module_pa >= res->start &&
|
||||
ddata->module_pa <= res->end) {
|
||||
atomic_set(&sysc_defer, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_dec_if_positive(&sysc_defer);
|
||||
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
static struct device_node *stdout_path;
|
||||
|
||||
static void sysc_init_stdout_path(struct sysc *ddata)
|
||||
@ -947,10 +902,6 @@ static int sysc_map_and_check_registers(struct sysc *ddata)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = sysc_defer_non_critical(ddata);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
sysc_check_children(ddata);
|
||||
|
||||
if (!of_property_present(np, "reg"))
|
||||
|
2
drivers/cache/sifive_ccache.c
vendored
2
drivers/cache/sifive_ccache.c
vendored
@ -118,6 +118,8 @@ static void ccache_config_read(void)
|
||||
}
|
||||
|
||||
static const struct of_device_id sifive_ccache_ids[] = {
|
||||
{ .compatible = "eswin,eic7700-l3-cache",
|
||||
.data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS) },
|
||||
{ .compatible = "sifive,fu540-c000-ccache" },
|
||||
{ .compatible = "sifive,fu740-c000-ccache" },
|
||||
{ .compatible = "starfive,jh7100-ccache",
|
||||
|
@ -267,6 +267,23 @@ config TURRIS_MOX_RWTM
|
||||
other manufacturing data and also utilize the Entropy Bit Generator
|
||||
for hardware random number generation.
|
||||
|
||||
if TURRIS_MOX_RWTM
|
||||
|
||||
config TURRIS_MOX_RWTM_KEYCTL
|
||||
bool "Turris Mox rWTM ECDSA message signing"
|
||||
default y
|
||||
depends on KEYS
|
||||
depends on ASYMMETRIC_KEY_TYPE
|
||||
select CZNIC_PLATFORMS
|
||||
select TURRIS_SIGNING_KEY
|
||||
help
|
||||
Say Y here to add support for ECDSA message signing with board private
|
||||
key (each Turris Mox has an ECDSA private key generated in the secure
|
||||
coprocessor when manufactured). This functionality is exposed via the
|
||||
keyctl() syscall.
|
||||
|
||||
endif # TURRIS_MOX_RWTM
|
||||
|
||||
source "drivers/firmware/arm_ffa/Kconfig"
|
||||
source "drivers/firmware/broadcom/Kconfig"
|
||||
source "drivers/firmware/cirrus/Kconfig"
|
||||
|
@ -69,6 +69,19 @@ config ARM_SCMI_DEBUG_COUNTERS
|
||||
such useful debug counters. This can be helpful for debugging and
|
||||
SCMI monitoring.
|
||||
|
||||
config ARM_SCMI_QUIRKS
|
||||
bool "Enable SCMI Quirks framework"
|
||||
depends on JUMP_LABEL || COMPILE_TEST
|
||||
default y
|
||||
help
|
||||
Enables support for SCMI Quirks framework to workaround SCMI platform
|
||||
firmware bugs on system already deployed in the wild.
|
||||
|
||||
The framework allows the definition of platform-specific code quirks
|
||||
that will be associated and enabled only on the desired platforms
|
||||
depending on the SCMI firmware advertised versions and/or machine
|
||||
compatibles.
|
||||
|
||||
source "drivers/firmware/arm_scmi/transports/Kconfig"
|
||||
source "drivers/firmware/arm_scmi/vendors/imx/Kconfig"
|
||||
|
||||
|
@ -3,6 +3,7 @@ scmi-bus-y = bus.o
|
||||
scmi-core-objs := $(scmi-bus-y)
|
||||
|
||||
scmi-driver-y = driver.o notify.o
|
||||
scmi-driver-$(CONFIG_ARM_SCMI_QUIRKS) += quirks.o
|
||||
scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
|
||||
|
@ -201,55 +201,51 @@ scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
|
||||
scmi_protocol_device_unrequest(entry);
|
||||
}
|
||||
|
||||
static const struct scmi_device_id *
|
||||
scmi_dev_match_id(struct scmi_device *scmi_dev, const struct scmi_driver *scmi_drv)
|
||||
static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev,
|
||||
const struct scmi_device_id *id_table)
|
||||
{
|
||||
const struct scmi_device_id *id = scmi_drv->id_table;
|
||||
if (!id_table || !id_table->name)
|
||||
return 0;
|
||||
|
||||
if (!id)
|
||||
return NULL;
|
||||
/* Always skip transport devices from matching */
|
||||
for (; id_table->protocol_id && id_table->name; id_table++)
|
||||
if (id_table->protocol_id == scmi_dev->protocol_id &&
|
||||
strncmp(scmi_dev->name, "__scmi_transport_device", 23) &&
|
||||
!strcmp(id_table->name, scmi_dev->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (; id->protocol_id; id++)
|
||||
if (id->protocol_id == scmi_dev->protocol_id) {
|
||||
if (!id->name)
|
||||
return id;
|
||||
else if (!strcmp(id->name, scmi_dev->name))
|
||||
return id;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
static int scmi_dev_match_id(struct scmi_device *scmi_dev,
|
||||
const struct scmi_driver *scmi_drv)
|
||||
{
|
||||
return scmi_dev_match_by_id_table(scmi_dev, scmi_drv->id_table);
|
||||
}
|
||||
|
||||
static int scmi_dev_match(struct device *dev, const struct device_driver *drv)
|
||||
{
|
||||
const struct scmi_driver *scmi_drv = to_scmi_driver(drv);
|
||||
struct scmi_device *scmi_dev = to_scmi_dev(dev);
|
||||
const struct scmi_device_id *id;
|
||||
|
||||
id = scmi_dev_match_id(scmi_dev, scmi_drv);
|
||||
if (id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
return scmi_dev_match_id(scmi_dev, scmi_drv);
|
||||
}
|
||||
|
||||
static int scmi_match_by_id_table(struct device *dev, const void *data)
|
||||
{
|
||||
struct scmi_device *sdev = to_scmi_dev(dev);
|
||||
struct scmi_device *scmi_dev = to_scmi_dev(dev);
|
||||
const struct scmi_device_id *id_table = data;
|
||||
|
||||
return sdev->protocol_id == id_table->protocol_id &&
|
||||
(id_table->name && !strcmp(sdev->name, id_table->name));
|
||||
return scmi_dev_match_by_id_table(scmi_dev, id_table);
|
||||
}
|
||||
|
||||
static struct scmi_device *scmi_child_dev_find(struct device *parent,
|
||||
int prot_id, const char *name)
|
||||
{
|
||||
struct scmi_device_id id_table;
|
||||
struct scmi_device_id id_table[2] = { 0 };
|
||||
struct device *dev;
|
||||
|
||||
id_table.protocol_id = prot_id;
|
||||
id_table.name = name;
|
||||
id_table[0].protocol_id = prot_id;
|
||||
id_table[0].name = name;
|
||||
|
||||
dev = device_find_child(parent, &id_table, scmi_match_by_id_table);
|
||||
if (!dev)
|
||||
@ -463,6 +459,20 @@ put_dev:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct scmi_device *
|
||||
_scmi_device_create(struct device_node *np, struct device *parent,
|
||||
int protocol, const char *name)
|
||||
{
|
||||
struct scmi_device *sdev;
|
||||
|
||||
sdev = __scmi_device_create(np, parent, protocol, name);
|
||||
if (!sdev)
|
||||
pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n",
|
||||
of_node_full_name(parent->of_node), protocol, name);
|
||||
|
||||
return sdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_device_create - A method to create one or more SCMI devices
|
||||
*
|
||||
@ -495,7 +505,7 @@ struct scmi_device *scmi_device_create(struct device_node *np,
|
||||
struct scmi_device *scmi_dev = NULL;
|
||||
|
||||
if (name)
|
||||
return __scmi_device_create(np, parent, protocol, name);
|
||||
return _scmi_device_create(np, parent, protocol, name);
|
||||
|
||||
mutex_lock(&scmi_requested_devices_mtx);
|
||||
phead = idr_find(&scmi_requested_devices, protocol);
|
||||
@ -509,18 +519,13 @@ struct scmi_device *scmi_device_create(struct device_node *np,
|
||||
list_for_each_entry(rdev, phead, node) {
|
||||
struct scmi_device *sdev;
|
||||
|
||||
sdev = __scmi_device_create(np, parent,
|
||||
rdev->id_table->protocol_id,
|
||||
rdev->id_table->name);
|
||||
/* Report errors and carry on... */
|
||||
sdev = _scmi_device_create(np, parent,
|
||||
rdev->id_table->protocol_id,
|
||||
rdev->id_table->name);
|
||||
if (sdev)
|
||||
scmi_dev = sdev;
|
||||
else
|
||||
pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n",
|
||||
of_node_full_name(parent->of_node),
|
||||
rdev->id_table->protocol_id,
|
||||
rdev->id_table->name);
|
||||
}
|
||||
|
||||
mutex_unlock(&scmi_requested_devices_mtx);
|
||||
|
||||
return scmi_dev;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
#include "quirks.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
|
||||
@ -429,6 +430,23 @@ static void iter_clk_describe_prepare_message(void *message,
|
||||
msg->rate_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
#define QUIRK_OUT_OF_SPEC_TRIPLET \
|
||||
({ \
|
||||
/* \
|
||||
* A known quirk: a triplet is returned but num_returned != 3 \
|
||||
* Check for a safe payload size and fix. \
|
||||
*/ \
|
||||
if (st->num_returned != 3 && st->num_remaining == 0 && \
|
||||
st->rx_len == sizeof(*r) + sizeof(__le32) * 2 * 3) { \
|
||||
st->num_returned = 3; \
|
||||
st->num_remaining = 0; \
|
||||
} else { \
|
||||
dev_err(p->dev, \
|
||||
"Cannot fix out-of-spec reply !\n"); \
|
||||
return -EPROTO; \
|
||||
} \
|
||||
})
|
||||
|
||||
static int
|
||||
iter_clk_describe_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
@ -450,19 +468,8 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st,
|
||||
p->clk->name, st->num_returned, st->num_remaining,
|
||||
st->rx_len);
|
||||
|
||||
/*
|
||||
* A known quirk: a triplet is returned but num_returned != 3
|
||||
* Check for a safe payload size and fix.
|
||||
*/
|
||||
if (st->num_returned != 3 && st->num_remaining == 0 &&
|
||||
st->rx_len == sizeof(*r) + sizeof(__le32) * 2 * 3) {
|
||||
st->num_returned = 3;
|
||||
st->num_remaining = 0;
|
||||
} else {
|
||||
dev_err(p->dev,
|
||||
"Cannot fix out-of-spec reply !\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
SCMI_QUIRK(clock_rates_triplet_out_of_spec,
|
||||
QUIRK_OUT_OF_SPEC_TRIPLET);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -475,6 +475,7 @@ static int __tag##_probe(struct platform_device *pdev) \
|
||||
if (ret) \
|
||||
goto err; \
|
||||
\
|
||||
spdev->dev.parent = dev; \
|
||||
ret = platform_device_add(spdev); \
|
||||
if (ret) \
|
||||
goto err; \
|
||||
|
@ -11,7 +11,7 @@
|
||||
* various power domain DVFS including the core/cluster, certain system
|
||||
* clocks configuration, thermal sensors and many others.
|
||||
*
|
||||
* Copyright (C) 2018-2024 ARM Ltd.
|
||||
* Copyright (C) 2018-2025 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@ -38,6 +38,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "notify.h"
|
||||
#include "quirks.h"
|
||||
|
||||
#include "raw_mode.h"
|
||||
|
||||
@ -439,14 +440,8 @@ static void scmi_create_protocol_devices(struct device_node *np,
|
||||
struct scmi_info *info,
|
||||
int prot_id, const char *name)
|
||||
{
|
||||
struct scmi_device *sdev;
|
||||
|
||||
mutex_lock(&info->devreq_mtx);
|
||||
sdev = scmi_device_create(np, info->dev, prot_id, name);
|
||||
if (name && !sdev)
|
||||
dev_err(info->dev,
|
||||
"failed to create device for protocol 0x%X (%s)\n",
|
||||
prot_id, name);
|
||||
scmi_device_create(np, info->dev, prot_id, name);
|
||||
mutex_unlock(&info->devreq_mtx);
|
||||
}
|
||||
|
||||
@ -1190,7 +1185,8 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
|
||||
* RX path since it will be already queued at the end of the TX
|
||||
* poll loop.
|
||||
*/
|
||||
if (!xfer->hdr.poll_completion)
|
||||
if (!xfer->hdr.poll_completion ||
|
||||
xfer->hdr.type == MSG_TYPE_DELAYED_RESP)
|
||||
scmi_raw_message_report(info->raw, xfer,
|
||||
SCMI_RAW_REPLY_QUEUE,
|
||||
cinfo->id);
|
||||
@ -1737,6 +1733,39 @@ static int scmi_common_get_max_msg_size(const struct scmi_protocol_handle *ph)
|
||||
return info->desc->max_msg_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_protocol_msg_check - Check protocol message attributes
|
||||
*
|
||||
* @ph: A reference to the protocol handle.
|
||||
* @message_id: The ID of the message to check.
|
||||
* @attributes: A parameter to optionally return the retrieved message
|
||||
* attributes, in case of Success.
|
||||
*
|
||||
* An helper to check protocol message attributes for a specific protocol
|
||||
* and message pair.
|
||||
*
|
||||
* Return: 0 on SUCCESS
|
||||
*/
|
||||
static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
|
||||
u32 message_id, u32 *attributes)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES,
|
||||
sizeof(__le32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(message_id, t->tx.buf);
|
||||
ret = do_xfer(ph, t);
|
||||
if (!ret && attributes)
|
||||
*attributes = get_unaligned_le32(t->rx.buf);
|
||||
xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct scmi_iterator - Iterator descriptor
|
||||
* @msg: A reference to the message TX buffer; filled by @prepare_message with
|
||||
@ -1869,6 +1898,13 @@ struct scmi_msg_resp_desc_fc {
|
||||
__le32 db_preserve_hmask;
|
||||
};
|
||||
|
||||
#define QUIRK_PERF_FC_FORCE \
|
||||
({ \
|
||||
if (pi->proto->id == SCMI_PROTOCOL_PERF && \
|
||||
message_id == 0x8 /* PERF_LEVEL_GET */) \
|
||||
attributes |= BIT(0); \
|
||||
})
|
||||
|
||||
static void
|
||||
scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
|
||||
u8 describe_id, u32 message_id, u32 valid_size,
|
||||
@ -1878,6 +1914,7 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
|
||||
int ret;
|
||||
u32 flags;
|
||||
u64 phys_addr;
|
||||
u32 attributes;
|
||||
u8 size;
|
||||
void __iomem *addr;
|
||||
struct scmi_xfer *t;
|
||||
@ -1886,6 +1923,16 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_msg_resp_desc_fc *resp;
|
||||
const struct scmi_protocol_instance *pi = ph_to_pi(ph);
|
||||
|
||||
/* Check if the MSG_ID supports fastchannel */
|
||||
ret = scmi_protocol_msg_check(ph, message_id, &attributes);
|
||||
SCMI_QUIRK(perf_level_get_fc_force, QUIRK_PERF_FC_FORCE);
|
||||
if (ret || !MSG_SUPPORTS_FASTCHANNEL(attributes)) {
|
||||
dev_dbg(ph->dev,
|
||||
"Skip FC init for 0x%02X/%d domain:%d - ret:%d\n",
|
||||
pi->proto->id, message_id, domain, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!p_addr) {
|
||||
ret = -EINVAL;
|
||||
goto err_out;
|
||||
@ -2003,39 +2050,6 @@ static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db)
|
||||
SCMI_PROTO_FC_RING_DB(64);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_protocol_msg_check - Check protocol message attributes
|
||||
*
|
||||
* @ph: A reference to the protocol handle.
|
||||
* @message_id: The ID of the message to check.
|
||||
* @attributes: A parameter to optionally return the retrieved message
|
||||
* attributes, in case of Success.
|
||||
*
|
||||
* An helper to check protocol message attributes for a specific protocol
|
||||
* and message pair.
|
||||
*
|
||||
* Return: 0 on SUCCESS
|
||||
*/
|
||||
static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
|
||||
u32 message_id, u32 *attributes)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES,
|
||||
sizeof(__le32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(message_id, t->tx.buf);
|
||||
ret = do_xfer(ph, t);
|
||||
if (!ret && attributes)
|
||||
*attributes = get_unaligned_le32(t->rx.buf);
|
||||
xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_proto_helpers_ops helpers_ops = {
|
||||
.extended_name_get = scmi_common_extended_name_get,
|
||||
.get_max_msg_size = scmi_common_get_max_msg_size,
|
||||
@ -2828,9 +2842,8 @@ static int scmi_bus_notifier(struct notifier_block *nb,
|
||||
struct scmi_info *info = bus_nb_to_scmi_info(nb);
|
||||
struct scmi_device *sdev = to_scmi_dev(data);
|
||||
|
||||
/* Skip transport devices and devices of different SCMI instances */
|
||||
if (!strncmp(sdev->name, "__scmi_transport_device", 23) ||
|
||||
sdev->dev.parent != info->dev)
|
||||
/* Skip devices of different SCMI instances */
|
||||
if (sdev->dev.parent != info->dev)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (action) {
|
||||
@ -3101,6 +3114,18 @@ static const struct scmi_desc *scmi_transport_setup(struct device *dev)
|
||||
return &trans->desc;
|
||||
}
|
||||
|
||||
static void scmi_enable_matching_quirks(struct scmi_info *info)
|
||||
{
|
||||
struct scmi_revision_info *rev = &info->version;
|
||||
|
||||
dev_dbg(info->dev, "Looking for quirks matching: %s/%s/0x%08X\n",
|
||||
rev->vendor_id, rev->sub_vendor_id, rev->impl_ver);
|
||||
|
||||
/* Enable applicable quirks */
|
||||
scmi_quirks_enable(info->dev, rev->vendor_id,
|
||||
rev->sub_vendor_id, rev->impl_ver);
|
||||
}
|
||||
|
||||
static int scmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@ -3222,6 +3247,8 @@ static int scmi_probe(struct platform_device *pdev)
|
||||
list_add_tail(&info->node, &scmi_list);
|
||||
mutex_unlock(&scmi_list_mutex);
|
||||
|
||||
scmi_enable_matching_quirks(info);
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
u32 prot_id;
|
||||
|
||||
@ -3380,6 +3407,8 @@ static struct dentry *scmi_debugfs_init(void)
|
||||
|
||||
static int __init scmi_driver_init(void)
|
||||
{
|
||||
scmi_quirks_initialize();
|
||||
|
||||
/* Bail out if no SCMI transport was configured */
|
||||
if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT)))
|
||||
return -EINVAL;
|
||||
|
@ -31,6 +31,8 @@
|
||||
|
||||
#define SCMI_PROTOCOL_VENDOR_BASE 0x80
|
||||
|
||||
#define MSG_SUPPORTS_FASTCHANNEL(x) ((x) & BIT(0))
|
||||
|
||||
enum scmi_common_cmd {
|
||||
PROTOCOL_VERSION = 0x0,
|
||||
PROTOCOL_ATTRIBUTES = 0x1,
|
||||
|
322
drivers/firmware/arm_scmi/quirks.c
Normal file
322
drivers/firmware/arm_scmi/quirks.c
Normal file
@ -0,0 +1,322 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Message Protocol Quirks
|
||||
*
|
||||
* Copyright (C) 2025 ARM Ltd.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Theory of operation
|
||||
*
|
||||
* A framework to define SCMI quirks and their activation conditions based on
|
||||
* existing static_keys kernel facilities.
|
||||
*
|
||||
* Quirks are named and their activation conditions defined using the macro
|
||||
* DEFINE_SCMI_QUIRK() in this file.
|
||||
*
|
||||
* After a quirk is defined, a corresponding entry must also be added to the
|
||||
* global @scmi_quirks_table in this file using __DECLARE_SCMI_QUIRK_ENTRY().
|
||||
*
|
||||
* Additionally a corresponding quirk declaration must be added also to the
|
||||
* quirk.h file using DECLARE_SCMI_QUIRK().
|
||||
*
|
||||
* The needed quirk code-snippet itself will be defined local to the SCMI code
|
||||
* that is meant to fix and will be associated to the previously defined quirk
|
||||
* and related activation conditions using the macro SCMI_QUIRK().
|
||||
*
|
||||
* At runtime, during the SCMI stack probe sequence, once the SCMI Server had
|
||||
* advertised the running platform Vendor, SubVendor and Implementation Version
|
||||
* data, all the defined quirks matching the activation conditions will be
|
||||
* enabled.
|
||||
*
|
||||
* Example
|
||||
*
|
||||
* quirk.c
|
||||
* -------
|
||||
* DEFINE_SCMI_QUIRK(fix_me, "vendor", "subvend", "0x12000-0x30000",
|
||||
* "someone,plat_A", "another,plat_b", "vend,sku");
|
||||
*
|
||||
* static struct scmi_quirk *scmi_quirks_table[] = {
|
||||
* ...
|
||||
* __DECLARE_SCMI_QUIRK_ENTRY(fix_me),
|
||||
* NULL
|
||||
* };
|
||||
*
|
||||
* quirk.h
|
||||
* -------
|
||||
* DECLARE_SCMI_QUIRK(fix_me);
|
||||
*
|
||||
* <somewhere_in_the_scmi_stack.c>
|
||||
* ------------------------------
|
||||
*
|
||||
* #define QUIRK_CODE_SNIPPET_FIX_ME() \
|
||||
* ({ \
|
||||
* if (p->condition) \
|
||||
* a_ptr->calculated_val = 123; \
|
||||
* })
|
||||
*
|
||||
*
|
||||
* int some_function_to_fix(int param, struct something *p)
|
||||
* {
|
||||
* struct some_strut *a_ptr;
|
||||
*
|
||||
* a_ptr = some_load_func(p);
|
||||
* SCMI_QUIRK(fix_me, QUIRK_CODE_SNIPPET_FIX_ME);
|
||||
* some_more_func(a_ptr);
|
||||
* ...
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/stringhash.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "quirks.h"
|
||||
|
||||
#define SCMI_QUIRKS_HT_SZ 4
|
||||
|
||||
struct scmi_quirk {
|
||||
bool enabled;
|
||||
const char *name;
|
||||
char *vendor;
|
||||
char *sub_vendor_id;
|
||||
char *impl_ver_range;
|
||||
u32 start_range;
|
||||
u32 end_range;
|
||||
struct static_key_false *key;
|
||||
struct hlist_node hash;
|
||||
unsigned int hkey;
|
||||
const char *const compats[];
|
||||
};
|
||||
|
||||
#define __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ...) \
|
||||
static struct scmi_quirk scmi_quirk_entry_ ## _qn = { \
|
||||
.name = __stringify(quirk_ ## _qn), \
|
||||
.vendor = _ven, \
|
||||
.sub_vendor_id = _sub, \
|
||||
.impl_ver_range = _impl, \
|
||||
.key = &(scmi_quirk_ ## _qn), \
|
||||
.compats = { __VA_ARGS__ __VA_OPT__(,) NULL }, \
|
||||
}
|
||||
|
||||
#define __DECLARE_SCMI_QUIRK_ENTRY(_qn) (&(scmi_quirk_entry_ ## _qn))
|
||||
|
||||
/*
|
||||
* Define a quirk by name and provide the matching tokens where:
|
||||
*
|
||||
* _qn: A string which will be used to build the quirk and the global
|
||||
* static_key names.
|
||||
* _ven : SCMI Vendor ID string match, NULL means any.
|
||||
* _sub : SCMI SubVendor ID string match, NULL means any.
|
||||
* _impl : SCMI Implementation Version string match, NULL means any.
|
||||
* This string can be used to express version ranges which will be
|
||||
* interpreted as follows:
|
||||
*
|
||||
* NULL [0, 0xFFFFFFFF]
|
||||
* "X" [X, X]
|
||||
* "X-" [X, 0xFFFFFFFF]
|
||||
* "-X" [0, X]
|
||||
* "X-Y" [X, Y]
|
||||
*
|
||||
* with X <= Y and <v> in [X, Y] meaning X <= <v> <= Y
|
||||
*
|
||||
* ... : An optional variadic macros argument used to provide a comma-separated
|
||||
* list of compatible strings matches; when no variadic argument is
|
||||
* provided, ANY compatible will match this quirk.
|
||||
*
|
||||
* This implicitly define also a properly named global static-key that
|
||||
* will be used to dynamically enable the quirk at initialization time.
|
||||
*
|
||||
* Note that it is possible to associate multiple quirks to the same
|
||||
* matching pattern, if your firmware quality is really astounding :P
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Compatibles list NOT provided, so ANY compatible will match:
|
||||
*
|
||||
* DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000");
|
||||
*
|
||||
*
|
||||
* A few compatibles provided to match against:
|
||||
*
|
||||
* DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000",
|
||||
* "xvend,plat_a", "xvend,plat_b", "xvend,sku_name");
|
||||
*/
|
||||
#define DEFINE_SCMI_QUIRK(_qn, _ven, _sub, _impl, ...) \
|
||||
DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
|
||||
__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks
|
||||
* that possibly reside in code that is included in loadable kernel modules
|
||||
* that needs to be able to access the global static keys at runtime to
|
||||
* determine if enabled or not. (see SCMI_QUIRK to understand usage)
|
||||
*/
|
||||
#define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...) \
|
||||
DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \
|
||||
EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn); \
|
||||
__DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__)
|
||||
|
||||
/* Global Quirks Definitions */
|
||||
DEFINE_SCMI_QUIRK(clock_rates_triplet_out_of_spec, NULL, NULL, NULL);
|
||||
DEFINE_SCMI_QUIRK(perf_level_get_fc_force, "Qualcomm", NULL, "0x20000-");
|
||||
|
||||
/*
|
||||
* Quirks Pointers Array
|
||||
*
|
||||
* This is filled at compile-time with the list of pointers to all the currently
|
||||
* defined quirks descriptors.
|
||||
*/
|
||||
static struct scmi_quirk *scmi_quirks_table[] = {
|
||||
__DECLARE_SCMI_QUIRK_ENTRY(clock_rates_triplet_out_of_spec),
|
||||
__DECLARE_SCMI_QUIRK_ENTRY(perf_level_get_fc_force),
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* Quirks HashTable
|
||||
*
|
||||
* A run-time populated hashtable containing all the defined quirks descriptors
|
||||
* hashed by matching pattern.
|
||||
*/
|
||||
static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ);
|
||||
|
||||
static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend)
|
||||
{
|
||||
char *signature, *p;
|
||||
unsigned int hash32;
|
||||
unsigned long hash = 0;
|
||||
|
||||
/* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */
|
||||
signature = kasprintf(GFP_KERNEL, "|%s|%s|", vend ?: "", sub_vend ?: "");
|
||||
if (!signature)
|
||||
return 0;
|
||||
|
||||
pr_debug("SCMI Quirk Signature >>>%s<<<\n", signature);
|
||||
|
||||
p = signature;
|
||||
while (*p)
|
||||
hash = partial_name_hash(tolower(*p++), hash);
|
||||
hash32 = end_name_hash(hash);
|
||||
|
||||
kfree(signature);
|
||||
|
||||
return hash32;
|
||||
}
|
||||
|
||||
static int scmi_quirk_range_parse(struct scmi_quirk *quirk)
|
||||
{
|
||||
const char *last, *first = quirk->impl_ver_range;
|
||||
size_t len;
|
||||
char *sep;
|
||||
int ret;
|
||||
|
||||
quirk->start_range = 0;
|
||||
quirk->end_range = 0xFFFFFFFF;
|
||||
len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
last = first + len - 1;
|
||||
sep = strchr(quirk->impl_ver_range, '-');
|
||||
if (sep)
|
||||
*sep = '\0';
|
||||
|
||||
if (sep == first) /* -X */
|
||||
ret = kstrtouint(first + 1, 0, &quirk->end_range);
|
||||
else /* X OR X- OR X-y */
|
||||
ret = kstrtouint(first, 0, &quirk->start_range);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!sep)
|
||||
quirk->end_range = quirk->start_range;
|
||||
else if (sep != last) /* x-Y */
|
||||
ret = kstrtouint(sep + 1, 0, &quirk->end_range);
|
||||
|
||||
if (quirk->start_range > quirk->end_range)
|
||||
return -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scmi_quirks_initialize(void)
|
||||
{
|
||||
struct scmi_quirk *quirk;
|
||||
int i;
|
||||
|
||||
for (i = 0, quirk = scmi_quirks_table[0]; quirk;
|
||||
i++, quirk = scmi_quirks_table[i]) {
|
||||
int ret;
|
||||
|
||||
ret = scmi_quirk_range_parse(quirk);
|
||||
if (ret) {
|
||||
pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n",
|
||||
quirk->name, quirk->impl_ver_range);
|
||||
continue;
|
||||
}
|
||||
quirk->hkey = scmi_quirk_signature(quirk->vendor,
|
||||
quirk->sub_vendor_id);
|
||||
|
||||
hash_add(scmi_quirks_ht, &quirk->hash, quirk->hkey);
|
||||
|
||||
pr_debug("Registered SCMI QUIRK [%s] -- %p - Key [0x%08X] - %s/%s/[0x%08X-0x%08X]\n",
|
||||
quirk->name, quirk, quirk->hkey,
|
||||
quirk->vendor, quirk->sub_vendor_id,
|
||||
quirk->start_range, quirk->end_range);
|
||||
}
|
||||
|
||||
pr_debug("SCMI Quirks initialized\n");
|
||||
}
|
||||
|
||||
void scmi_quirks_enable(struct device *dev, const char *vend,
|
||||
const char *subv, const u32 impl)
|
||||
{
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
struct scmi_quirk *quirk;
|
||||
unsigned int hkey;
|
||||
|
||||
hkey = scmi_quirk_signature(i > 1 ? vend : NULL,
|
||||
i > 2 ? subv : NULL);
|
||||
|
||||
/*
|
||||
* Note that there could be multiple matches so we
|
||||
* will enable multiple quirk part of a hash collision
|
||||
* domain...BUT we cannot assume that ALL quirks on the
|
||||
* same collision domain are a full match.
|
||||
*/
|
||||
hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) {
|
||||
if (quirk->enabled || quirk->hkey != hkey ||
|
||||
impl < quirk->start_range ||
|
||||
impl > quirk->end_range)
|
||||
continue;
|
||||
|
||||
if (quirk->compats[0] &&
|
||||
!of_machine_compatible_match(quirk->compats))
|
||||
continue;
|
||||
|
||||
dev_info(dev, "Enabling SCMI Quirk [%s]\n",
|
||||
quirk->name);
|
||||
|
||||
dev_dbg(dev,
|
||||
"Quirk matched on: %s/%s/%s/[0x%08X-0x%08X]\n",
|
||||
quirk->compats[0], quirk->vendor,
|
||||
quirk->sub_vendor_id,
|
||||
quirk->start_range, quirk->end_range);
|
||||
|
||||
static_branch_enable(quirk->key);
|
||||
quirk->enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
52
drivers/firmware/arm_scmi/quirks.h
Normal file
52
drivers/firmware/arm_scmi/quirks.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Message Protocol Quirks
|
||||
*
|
||||
* Copyright (C) 2025 ARM Ltd.
|
||||
*/
|
||||
#ifndef _SCMI_QUIRKS_H
|
||||
#define _SCMI_QUIRKS_H
|
||||
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef CONFIG_ARM_SCMI_QUIRKS
|
||||
|
||||
#define DECLARE_SCMI_QUIRK(_qn) \
|
||||
DECLARE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn)
|
||||
|
||||
/*
|
||||
* A helper to associate the actual code snippet to use as a quirk
|
||||
* named as _qn.
|
||||
*/
|
||||
#define SCMI_QUIRK(_qn, _blk) \
|
||||
do { \
|
||||
if (static_branch_unlikely(&(scmi_quirk_ ## _qn))) \
|
||||
(_blk); \
|
||||
} while (0)
|
||||
|
||||
void scmi_quirks_initialize(void);
|
||||
void scmi_quirks_enable(struct device *dev, const char *vend,
|
||||
const char *subv, const u32 impl);
|
||||
|
||||
#else
|
||||
|
||||
#define DECLARE_SCMI_QUIRK(_qn)
|
||||
/* Force quirks compilation even when SCMI Quirks are disabled */
|
||||
#define SCMI_QUIRK(_qn, _blk) \
|
||||
do { \
|
||||
if (0) \
|
||||
(_blk); \
|
||||
} while (0)
|
||||
|
||||
static inline void scmi_quirks_initialize(void) { }
|
||||
static inline void scmi_quirks_enable(struct device *dev, const char *vend,
|
||||
const char *sub_vend, const u32 impl) { }
|
||||
|
||||
#endif /* CONFIG_ARM_SCMI_QUIRKS */
|
||||
|
||||
/* Quirk delarations */
|
||||
DECLARE_SCMI_QUIRK(clock_rates_triplet_out_of_spec);
|
||||
DECLARE_SCMI_QUIRK(perf_level_get_fc_force);
|
||||
|
||||
#endif /* _SCMI_QUIRKS_H */
|
@ -671,11 +671,13 @@ static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw,
|
||||
* @len: Length of the message in @buf.
|
||||
* @chan_id: The channel ID to use.
|
||||
* @async: A flag stating if an asynchronous command is required.
|
||||
* @poll: A flag stating if a polling transmission is required.
|
||||
*
|
||||
* Return: 0 on Success
|
||||
*/
|
||||
static int scmi_raw_message_send(struct scmi_raw_mode_info *raw,
|
||||
void *buf, size_t len, u8 chan_id, bool async)
|
||||
void *buf, size_t len, u8 chan_id,
|
||||
bool async, bool poll)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *xfer;
|
||||
@ -684,6 +686,16 @@ static int scmi_raw_message_send(struct scmi_raw_mode_info *raw,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (poll) {
|
||||
if (is_transport_polling_capable(raw->desc)) {
|
||||
xfer->hdr.poll_completion = true;
|
||||
} else {
|
||||
dev_err(raw->handle->dev,
|
||||
"Failed to send RAW message - Polling NOT supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = scmi_do_xfer_raw_start(raw, xfer, chan_id, async);
|
||||
if (ret)
|
||||
scmi_xfer_raw_put(raw->handle, xfer);
|
||||
@ -801,7 +813,7 @@ static ssize_t scmi_dbg_raw_mode_common_read(struct file *filp,
|
||||
static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp,
|
||||
const char __user *buf,
|
||||
size_t count, loff_t *ppos,
|
||||
bool async)
|
||||
bool async, bool poll)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_dbg_raw_data *rd = filp->private_data;
|
||||
@ -831,7 +843,7 @@ static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp,
|
||||
}
|
||||
|
||||
ret = scmi_raw_message_send(rd->raw, rd->tx.buf, rd->tx_size,
|
||||
rd->chan_id, async);
|
||||
rd->chan_id, async, poll);
|
||||
|
||||
/* Reset ppos for next message ... */
|
||||
rd->tx_size = 0;
|
||||
@ -875,7 +887,8 @@ static ssize_t scmi_dbg_raw_mode_message_write(struct file *filp,
|
||||
const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, false);
|
||||
return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
|
||||
false, false);
|
||||
}
|
||||
|
||||
static __poll_t scmi_dbg_raw_mode_message_poll(struct file *filp,
|
||||
@ -964,7 +977,8 @@ static ssize_t scmi_dbg_raw_mode_message_async_write(struct file *filp,
|
||||
const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, true);
|
||||
return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
|
||||
true, false);
|
||||
}
|
||||
|
||||
static const struct file_operations scmi_dbg_raw_mode_message_async_fops = {
|
||||
@ -976,6 +990,40 @@ static const struct file_operations scmi_dbg_raw_mode_message_async_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static ssize_t scmi_dbg_raw_mode_message_poll_write(struct file *filp,
|
||||
const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
|
||||
false, true);
|
||||
}
|
||||
|
||||
static const struct file_operations scmi_dbg_raw_mode_message_poll_fops = {
|
||||
.open = scmi_dbg_raw_mode_open,
|
||||
.release = scmi_dbg_raw_mode_release,
|
||||
.read = scmi_dbg_raw_mode_message_read,
|
||||
.write = scmi_dbg_raw_mode_message_poll_write,
|
||||
.poll = scmi_dbg_raw_mode_message_poll,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static ssize_t scmi_dbg_raw_mode_message_poll_async_write(struct file *filp,
|
||||
const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
|
||||
true, true);
|
||||
}
|
||||
|
||||
static const struct file_operations scmi_dbg_raw_mode_message_poll_async_fops = {
|
||||
.open = scmi_dbg_raw_mode_open,
|
||||
.release = scmi_dbg_raw_mode_release,
|
||||
.read = scmi_dbg_raw_mode_message_read,
|
||||
.write = scmi_dbg_raw_mode_message_poll_async_write,
|
||||
.poll = scmi_dbg_raw_mode_message_poll,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static ssize_t scmi_test_dbg_raw_mode_notif_read(struct file *filp,
|
||||
char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
@ -1199,6 +1247,12 @@ void *scmi_raw_mode_init(const struct scmi_handle *handle,
|
||||
debugfs_create_file("message_async", 0600, raw->dentry, raw,
|
||||
&scmi_dbg_raw_mode_message_async_fops);
|
||||
|
||||
debugfs_create_file("message_poll", 0600, raw->dentry, raw,
|
||||
&scmi_dbg_raw_mode_message_poll_fops);
|
||||
|
||||
debugfs_create_file("message_poll_async", 0600, raw->dentry, raw,
|
||||
&scmi_dbg_raw_mode_message_poll_async_fops);
|
||||
|
||||
debugfs_create_file("notification", 0400, raw->dentry, raw,
|
||||
&scmi_dbg_raw_mode_notification_fops);
|
||||
|
||||
@ -1230,6 +1284,14 @@ void *scmi_raw_mode_init(const struct scmi_handle *handle,
|
||||
debugfs_create_file_aux_num("message_async", 0600, chd,
|
||||
raw, channels[i],
|
||||
&scmi_dbg_raw_mode_message_async_fops);
|
||||
|
||||
debugfs_create_file_aux_num("message_poll", 0600, chd,
|
||||
raw, channels[i],
|
||||
&scmi_dbg_raw_mode_message_poll_fops);
|
||||
|
||||
debugfs_create_file_aux_num("message_poll_async", 0600,
|
||||
chd, raw, channels[i],
|
||||
&scmi_dbg_raw_mode_message_poll_async_fops);
|
||||
}
|
||||
}
|
||||
|
||||
|
24
drivers/firmware/arm_scmi/vendors/imx/Kconfig
vendored
24
drivers/firmware/arm_scmi/vendors/imx/Kconfig
vendored
@ -12,6 +12,30 @@ config IMX_SCMI_BBM_EXT
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx-sm-bbm.
|
||||
|
||||
config IMX_SCMI_CPU_EXT
|
||||
tristate "i.MX SCMI CPU EXTENSION"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
depends on IMX_SCMI_CPU_DRV
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
This enables i.MX System CPU Protocol to manage cpu
|
||||
start, stop and etc.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx-sm-cpu.
|
||||
|
||||
config IMX_SCMI_LMM_EXT
|
||||
tristate "i.MX SCMI LMM EXTENSION"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
depends on IMX_SCMI_LMM_DRV
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
This enables i.MX System Logical Machine Protocol to
|
||||
manage Logical Machines boot, shutdown and etc.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx-sm-lmm.
|
||||
|
||||
config IMX_SCMI_MISC_EXT
|
||||
tristate "i.MX SCMI MISC EXTENSION"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
|
@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
|
||||
obj-$(CONFIG_IMX_SCMI_CPU_EXT) += imx-sm-cpu.o
|
||||
obj-$(CONFIG_IMX_SCMI_LMM_EXT) += imx-sm-lmm.o
|
||||
obj-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
|
||||
|
276
drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
vendored
Normal file
276
drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System control and Management Interface (SCMI) NXP CPU Protocol
|
||||
*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
#include "../../protocols.h"
|
||||
#include "../../notify.h"
|
||||
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000
|
||||
|
||||
enum scmi_imx_cpu_protocol_cmd {
|
||||
SCMI_IMX_CPU_ATTRIBUTES = 0x3,
|
||||
SCMI_IMX_CPU_START = 0x4,
|
||||
SCMI_IMX_CPU_STOP = 0x5,
|
||||
SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6,
|
||||
SCMI_IMX_CPU_INFO_GET = 0xC,
|
||||
};
|
||||
|
||||
struct scmi_imx_cpu_info {
|
||||
u32 nr_cpu;
|
||||
};
|
||||
|
||||
#define SCMI_IMX_CPU_NR_CPU_MASK GENMASK(15, 0)
|
||||
struct scmi_msg_imx_cpu_protocol_attributes {
|
||||
__le32 attributes;
|
||||
};
|
||||
|
||||
struct scmi_msg_imx_cpu_attributes_out {
|
||||
__le32 attributes;
|
||||
#define CPU_MAX_NAME 16
|
||||
u8 name[CPU_MAX_NAME];
|
||||
};
|
||||
|
||||
struct scmi_imx_cpu_reset_vector_set_in {
|
||||
__le32 cpuid;
|
||||
#define CPU_VEC_FLAGS_RESUME BIT(31)
|
||||
#define CPU_VEC_FLAGS_START BIT(30)
|
||||
#define CPU_VEC_FLAGS_BOOT BIT(29)
|
||||
__le32 flags;
|
||||
__le32 resetvectorlow;
|
||||
__le32 resetvectorhigh;
|
||||
};
|
||||
|
||||
struct scmi_imx_cpu_info_get_out {
|
||||
#define CPU_RUN_MODE_START 0
|
||||
#define CPU_RUN_MODE_HOLD 1
|
||||
#define CPU_RUN_MODE_STOP 2
|
||||
#define CPU_RUN_MODE_SLEEP 3
|
||||
__le32 runmode;
|
||||
__le32 sleepmode;
|
||||
__le32 resetvectorlow;
|
||||
__le32 resetvectorhigh;
|
||||
};
|
||||
|
||||
static int scmi_imx_cpu_validate_cpuid(const struct scmi_protocol_handle *ph,
|
||||
u32 cpuid)
|
||||
{
|
||||
struct scmi_imx_cpu_info *info = ph->get_priv(ph);
|
||||
|
||||
if (cpuid >= info->nr_cpu)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_cpu_start(const struct scmi_protocol_handle *ph,
|
||||
u32 cpuid, bool start)
|
||||
{
|
||||
struct scmi_xfer *t;
|
||||
u8 msg_id;
|
||||
int ret;
|
||||
|
||||
ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (start)
|
||||
msg_id = SCMI_IMX_CPU_START;
|
||||
else
|
||||
msg_id = SCMI_IMX_CPU_STOP;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, msg_id, sizeof(u32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(cpuid, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle *ph,
|
||||
u32 cpuid, u64 vector, bool start,
|
||||
bool boot, bool resume)
|
||||
{
|
||||
struct scmi_imx_cpu_reset_vector_set_in *in;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_RESET_VECTOR_SET, sizeof(*in),
|
||||
0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in = t->tx.buf;
|
||||
in->cpuid = cpu_to_le32(cpuid);
|
||||
in->flags = cpu_to_le32(0);
|
||||
if (start)
|
||||
in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_START);
|
||||
if (boot)
|
||||
in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_BOOT);
|
||||
if (resume)
|
||||
in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_RESUME);
|
||||
in->resetvectorlow = cpu_to_le32(lower_32_bits(vector));
|
||||
in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector));
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_cpu_started(const struct scmi_protocol_handle *ph, u32 cpuid,
|
||||
bool *started)
|
||||
{
|
||||
struct scmi_imx_cpu_info_get_out *out;
|
||||
struct scmi_xfer *t;
|
||||
u32 mode;
|
||||
int ret;
|
||||
|
||||
if (!started)
|
||||
return -EINVAL;
|
||||
|
||||
*started = false;
|
||||
ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_INFO_GET, sizeof(u32),
|
||||
0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(cpuid, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
out = t->rx.buf;
|
||||
mode = le32_to_cpu(out->runmode);
|
||||
if (mode == CPU_RUN_MODE_START || mode == CPU_RUN_MODE_SLEEP)
|
||||
*started = true;
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_imx_cpu_proto_ops scmi_imx_cpu_proto_ops = {
|
||||
.cpu_reset_vector_set = scmi_imx_cpu_reset_vector_set,
|
||||
.cpu_start = scmi_imx_cpu_start,
|
||||
.cpu_started = scmi_imx_cpu_started,
|
||||
};
|
||||
|
||||
static int scmi_imx_cpu_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_imx_cpu_info *info)
|
||||
{
|
||||
struct scmi_msg_imx_cpu_protocol_attributes *attr;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
|
||||
sizeof(*attr), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
attr = t->rx.buf;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
info->nr_cpu = le32_get_bits(attr->attributes, SCMI_IMX_CPU_NR_CPU_MASK);
|
||||
dev_info(ph->dev, "i.MX SM CPU: %d cpus\n",
|
||||
info->nr_cpu);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 cpuid)
|
||||
{
|
||||
struct scmi_msg_imx_cpu_attributes_out *out;
|
||||
char name[SCMI_SHORT_NAME_MAX_SIZE] = {'\0'};
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_ATTRIBUTES, sizeof(u32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(cpuid, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
out = t->rx.buf;
|
||||
strscpy(name, out->name, SCMI_SHORT_NAME_MAX_SIZE);
|
||||
dev_info(ph->dev, "i.MX CPU: name: %s\n", name);
|
||||
} else {
|
||||
dev_err(ph->dev, "i.MX cpu: Failed to get info of cpu(%u)\n", cpuid);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct scmi_imx_cpu_info *info;
|
||||
u32 version;
|
||||
int ret, i;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scmi_imx_cpu_protocol_attributes_get(ph, info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < info->nr_cpu; i++) {
|
||||
ret = scmi_imx_cpu_attributes_get(ph, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ph->set_priv(ph, info, version);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_cpu = {
|
||||
.id = SCMI_PROTOCOL_IMX_CPU,
|
||||
.owner = THIS_MODULE,
|
||||
.instance_init = &scmi_imx_cpu_protocol_init,
|
||||
.ops = &scmi_imx_cpu_proto_ops,
|
||||
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
|
||||
.vendor_id = SCMI_IMX_VENDOR,
|
||||
.sub_vendor_id = SCMI_IMX_SUBVENDOR,
|
||||
};
|
||||
module_scmi_protocol(scmi_imx_cpu);
|
||||
|
||||
MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_CPU) "-" SCMI_IMX_VENDOR);
|
||||
MODULE_DESCRIPTION("i.MX SCMI CPU driver");
|
||||
MODULE_LICENSE("GPL");
|
263
drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c
vendored
Normal file
263
drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System control and Management Interface (SCMI) NXP LMM Protocol
|
||||
*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
#include "../../protocols.h"
|
||||
#include "../../notify.h"
|
||||
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000
|
||||
|
||||
enum scmi_imx_lmm_protocol_cmd {
|
||||
SCMI_IMX_LMM_ATTRIBUTES = 0x3,
|
||||
SCMI_IMX_LMM_BOOT = 0x4,
|
||||
SCMI_IMX_LMM_RESET = 0x5,
|
||||
SCMI_IMX_LMM_SHUTDOWN = 0x6,
|
||||
SCMI_IMX_LMM_WAKE = 0x7,
|
||||
SCMI_IMX_LMM_SUSPEND = 0x8,
|
||||
SCMI_IMX_LMM_NOTIFY = 0x9,
|
||||
SCMI_IMX_LMM_RESET_REASON = 0xA,
|
||||
SCMI_IMX_LMM_POWER_ON = 0xB,
|
||||
SCMI_IMX_LMM_RESET_VECTOR_SET = 0xC,
|
||||
};
|
||||
|
||||
struct scmi_imx_lmm_priv {
|
||||
u32 nr_lmm;
|
||||
};
|
||||
|
||||
#define SCMI_IMX_LMM_NR_LM_MASK GENMASK(5, 0)
|
||||
#define SCMI_IMX_LMM_NR_MAX 16
|
||||
struct scmi_msg_imx_lmm_protocol_attributes {
|
||||
__le32 attributes;
|
||||
};
|
||||
|
||||
struct scmi_msg_imx_lmm_attributes_out {
|
||||
__le32 lmid;
|
||||
__le32 attributes;
|
||||
__le32 state;
|
||||
__le32 errstatus;
|
||||
u8 name[LMM_MAX_NAME];
|
||||
};
|
||||
|
||||
struct scmi_imx_lmm_reset_vector_set_in {
|
||||
__le32 lmid;
|
||||
__le32 cpuid;
|
||||
__le32 flags; /* reserved for future extension */
|
||||
__le32 resetvectorlow;
|
||||
__le32 resetvectorhigh;
|
||||
};
|
||||
|
||||
struct scmi_imx_lmm_shutdown_in {
|
||||
__le32 lmid;
|
||||
#define SCMI_IMX_LMM_SHUTDOWN_GRACEFUL BIT(0)
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
static int scmi_imx_lmm_validate_lmid(const struct scmi_protocol_handle *ph, u32 lmid)
|
||||
{
|
||||
struct scmi_imx_lmm_priv *priv = ph->get_priv(ph);
|
||||
|
||||
if (lmid >= priv->nr_lmm)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_lmm_attributes(const struct scmi_protocol_handle *ph,
|
||||
u32 lmid, struct scmi_imx_lmm_info *info)
|
||||
{
|
||||
struct scmi_msg_imx_lmm_attributes_out *out;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_LMM_ATTRIBUTES, sizeof(u32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(lmid, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
out = t->rx.buf;
|
||||
info->lmid = le32_to_cpu(out->lmid);
|
||||
info->state = le32_to_cpu(out->state);
|
||||
info->errstatus = le32_to_cpu(out->errstatus);
|
||||
strscpy(info->name, out->name);
|
||||
dev_dbg(ph->dev, "i.MX LMM: Logical Machine(%d), name: %s\n",
|
||||
info->lmid, info->name);
|
||||
} else {
|
||||
dev_err(ph->dev, "i.MX LMM: Failed to get info of Logical Machine(%u)\n", lmid);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_imx_lmm_power_boot(const struct scmi_protocol_handle *ph, u32 lmid, bool boot)
|
||||
{
|
||||
struct scmi_xfer *t;
|
||||
u8 msg_id;
|
||||
int ret;
|
||||
|
||||
ret = scmi_imx_lmm_validate_lmid(ph, lmid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (boot)
|
||||
msg_id = SCMI_IMX_LMM_BOOT;
|
||||
else
|
||||
msg_id = SCMI_IMX_LMM_POWER_ON;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, msg_id, sizeof(u32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(lmid, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_lmm_reset_vector_set(const struct scmi_protocol_handle *ph,
|
||||
u32 lmid, u32 cpuid, u32 flags, u64 vector)
|
||||
{
|
||||
struct scmi_imx_lmm_reset_vector_set_in *in;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_LMM_RESET_VECTOR_SET, sizeof(*in),
|
||||
0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in = t->tx.buf;
|
||||
in->lmid = cpu_to_le32(lmid);
|
||||
in->cpuid = cpu_to_le32(cpuid);
|
||||
in->flags = cpu_to_le32(0);
|
||||
in->resetvectorlow = cpu_to_le32(lower_32_bits(vector));
|
||||
in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector));
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_lmm_shutdown(const struct scmi_protocol_handle *ph, u32 lmid,
|
||||
u32 flags)
|
||||
{
|
||||
struct scmi_imx_lmm_shutdown_in *in;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = scmi_imx_lmm_validate_lmid(ph, lmid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_LMM_SHUTDOWN, sizeof(*in),
|
||||
0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in = t->tx.buf;
|
||||
in->lmid = cpu_to_le32(lmid);
|
||||
if (flags & SCMI_IMX_LMM_SHUTDOWN_GRACEFUL)
|
||||
in->flags = cpu_to_le32(SCMI_IMX_LMM_SHUTDOWN_GRACEFUL);
|
||||
else
|
||||
in->flags = cpu_to_le32(0);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_imx_lmm_proto_ops scmi_imx_lmm_proto_ops = {
|
||||
.lmm_power_boot = scmi_imx_lmm_power_boot,
|
||||
.lmm_info = scmi_imx_lmm_attributes,
|
||||
.lmm_reset_vector_set = scmi_imx_lmm_reset_vector_set,
|
||||
.lmm_shutdown = scmi_imx_lmm_shutdown,
|
||||
};
|
||||
|
||||
static int scmi_imx_lmm_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_imx_lmm_priv *priv)
|
||||
{
|
||||
struct scmi_msg_imx_lmm_protocol_attributes *attr;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
|
||||
sizeof(*attr), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
attr = t->rx.buf;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
priv->nr_lmm = le32_get_bits(attr->attributes, SCMI_IMX_LMM_NR_LM_MASK);
|
||||
if (priv->nr_lmm > SCMI_IMX_LMM_NR_MAX) {
|
||||
dev_err(ph->dev, "i.MX LMM: %d:Exceed max supported Logical Machines\n",
|
||||
priv->nr_lmm);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
dev_info(ph->dev, "i.MX LMM: %d Logical Machines\n", priv->nr_lmm);
|
||||
}
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct scmi_imx_lmm_priv *info;
|
||||
u32 version;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM LMM Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scmi_imx_lmm_protocol_attributes_get(ph, info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, info, version);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_lmm = {
|
||||
.id = SCMI_PROTOCOL_IMX_LMM,
|
||||
.owner = THIS_MODULE,
|
||||
.instance_init = &scmi_imx_lmm_protocol_init,
|
||||
.ops = &scmi_imx_lmm_proto_ops,
|
||||
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
|
||||
.vendor_id = SCMI_IMX_VENDOR,
|
||||
.sub_vendor_id = SCMI_IMX_SUBVENDOR,
|
||||
};
|
||||
module_scmi_protocol(scmi_imx_lmm);
|
||||
|
||||
MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_LMM) "-" SCMI_IMX_VENDOR);
|
||||
MODULE_DESCRIPTION("i.MX SCMI LMM driver");
|
||||
MODULE_LICENSE("GPL");
|
828
drivers/firmware/arm_scmi/vendors/imx/imx95.rst
vendored
828
drivers/firmware/arm_scmi/vendors/imx/imx95.rst
vendored
@ -32,6 +32,518 @@ port, and deploy the SM on supported processors.
|
||||
The SM implements an interface compliant with the Arm SCMI Specification
|
||||
with additional vendor specific extensions.
|
||||
|
||||
System Control and Management Logical Machine Management Vendor Protocol
|
||||
========================================================================
|
||||
|
||||
The SM adds the concept of logical machines (LMs). These are analogous to
|
||||
VMs and each has its own instance of SCMI. All normal SCMI calls only apply
|
||||
the LM running the calling agent. That includes boot, shutdown, reset,
|
||||
suspend, wake, etc. If a caller makes the SCMI base call to get a list
|
||||
of agents, it will only get those on that LM. Each LM is completely isolated
|
||||
from the others. This is mandatory for these to operate independently.
|
||||
|
||||
This protocol is intended to support boot, shutdown, and reset of other logical
|
||||
machines (LM). It is usually used to allow one LM(e.g. OSPM) to manage
|
||||
another LM which is usually an offload or accelerator engine. Notifications
|
||||
from this protocol can also be used to manage a communication link to another
|
||||
LM. The LMM protocol provides commands to:
|
||||
|
||||
- Describe the protocol version.
|
||||
- Discover implementation attributes.
|
||||
- Discover all the LMs defined in the system.
|
||||
- Boot a target LM.
|
||||
- Shutdown a target LM (gracefully or forcibly).
|
||||
- Reset a target LM (gracefully or forcibly).
|
||||
- Wake a target LM from suspend.
|
||||
- Suspend a target LM (gracefully).
|
||||
- Read boot/shutdown/reset information for a target LM.
|
||||
- Get notifications when a target LM boots or shuts down (e.g. LM 'X' requested
|
||||
notification of LM 'Y' boots or shuts down, when LM 'Y' boots or shuts down,
|
||||
SCMI firmware will send notification to LM 'X').
|
||||
|
||||
'Graceful' means asking LM itself to shutdown/reset/etc (e.g. sending
|
||||
notification to Linux, Then Linux reboots or powers down itself). It is async
|
||||
command that the SUCCESS of the command just means the command successfully
|
||||
return, not means reboot/reset successfully finished.
|
||||
|
||||
'Forceful' means the SM will force shutdown/reset/etc the LM. It is sync
|
||||
command that the SUCCESS of the command means the LM has been successfully
|
||||
shutdown/reset/etc.
|
||||
If the commands not have Graceful/Forceful flag settings, such as WAKE, SUSEND,
|
||||
it is a Graceful command.
|
||||
|
||||
Commands:
|
||||
_________
|
||||
|
||||
PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x0
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|uint32 version | For this revision of the specification, this value must be |
|
||||
| | 0x10000. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|
||||
PROTOCOL_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x1
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Protocol attributes: |
|
||||
| |Bits[31:5] Reserved, must be zero. |
|
||||
| |Bits[4:0] Number of Logical Machines |
|
||||
| |Note that due to both hardware limitations and reset reason|
|
||||
| |field limitations, the max number of LM is 16. The minimum |
|
||||
| |is 1. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
PROTOCOL_MESSAGE_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x2
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: in case the message is implemented and available |
|
||||
| |to use. |
|
||||
| |NOT_FOUND: if the message identified by message_id is |
|
||||
| |invalid or not implemented |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Flags that are associated with a specific command in the |
|
||||
| |protocol. For all commands in this protocol, this |
|
||||
| |parameter has a value of 0 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x3
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if valid attributes are returned. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |DENIED: if the agent does not have permission to get info |
|
||||
| |for the LM specified by lmid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |Identifier of the LM whose identification is requested. |
|
||||
| |This field is: Populated with the lmid of the calling |
|
||||
| |agent, when the lmid parameter passed via the command is |
|
||||
| |0xFFFFFFFF. Identical to the lmid field passed via the |
|
||||
| |calling parameters, in all other cases |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes | Bits[31:0] reserved. must be zero |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 state | Current state of the LM |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 errStatus | Last error status recorded |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|char name[16] | A NULL terminated ASCII string with the LM name, of up |
|
||||
| | to 16 bytes |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_BOOT
|
||||
~~~~~~~~
|
||||
|
||||
message_id: 0x4
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if LM boots successfully started. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |INVALID_PARAMETERS: if lmid is same as the caller. |
|
||||
| |DENIED: if the agent does not have permission to manage the|
|
||||
| |the LM specified by lmid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_RESET
|
||||
~~~~~~~~~
|
||||
|
||||
message_id: 0x5
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Reset flags: |
|
||||
| |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] Graceful request: |
|
||||
| |Set to 1 if the request is a graceful request. |
|
||||
| |Set to 0 if the request is a forceful request. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: The LMM RESET command finished successfully in |
|
||||
| |graceful reset or LM successfully resets in forceful reset.|
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |INVALID_PARAMETERS: if lmid is same as the caller. |
|
||||
| |DENIED: if the agent does not have permission to manage the|
|
||||
| |the LM specified by lmid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_SHUTDOWN
|
||||
~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x6
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Reset flags: |
|
||||
| |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] Graceful request: |
|
||||
| |Set to 1 if the request is a graceful request. |
|
||||
| |Set to 0 if the request is a forceful request. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: The LMM shutdown command finished successfully in |
|
||||
| |graceful request or LM successfully shutdown in forceful |
|
||||
| |request. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |INVALID_PARAMETERS: if lmid is same as the caller. |
|
||||
| |DENIED: if the agent does not have permission to manage the|
|
||||
| |the LM specified by lmid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_WAKE
|
||||
~~~~~~~~
|
||||
|
||||
message_id: 0x7
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if LM wake command successfully returns. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |INVALID_PARAMETERS: if lmid is same as the caller. |
|
||||
| |DENIED: if the agent does not have permission to manage the|
|
||||
| |the LM specified by lmid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_SUSPEND
|
||||
~~~~~~~~~~~
|
||||
|
||||
message_id: 0x8
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if LM suspend command successfully returns. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |INVALID_PARAMETERS: if lmid is same as the caller. |
|
||||
| |DENIED: if the agent does not have permission to manage the|
|
||||
| |the LM specified by lmid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_NOTIFY
|
||||
~~~~~~~~~~
|
||||
|
||||
message_id: 0x9
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Notification flags: |
|
||||
| |Bits[31:3] Reserved, must be zero. |
|
||||
| |Bit[3] Wake (resume) notification: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification. |
|
||||
| |Bit[2] Suspend (sleep) notification: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification. |
|
||||
| |Bit[1] Shutdown (off) notification: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification. |
|
||||
| |Bit[0] Boot (on) notification: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the notification state successfully updated. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |INVALID_PARAMETERS: if input attributes flag specifies |
|
||||
| |unsupported or invalid configurations. |
|
||||
| |DENIED: if the agent does not have permission to request |
|
||||
| |the notification. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_RESET_REASON
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xA
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
This command is to return the reset reason that caused the last reset, such as
|
||||
POR, WDOG, JTAG and etc.
|
||||
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|Parameters |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|Return values |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the reset reason of the LM successfully |
|
||||
| |updated. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine|
|
||||
| |DENIED: if the agent does not have permission to request|
|
||||
| |the reset reason. |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|uint32 bootflags |Boot reason flags. This parameter has the format: |
|
||||
| |Bits[31] Valid. |
|
||||
| |Set to 1 if the entire reason is valid. |
|
||||
| |Set to 0 if the entire reason is not valid. |
|
||||
| |Bits[30:29] Reserved, must be zero. |
|
||||
| |Bit[28] Valid origin: |
|
||||
| |Set to 1 if the origin field is valid. |
|
||||
| |Set to 0 if the origin field is not valid. |
|
||||
| |Bits[27:24] Origin. |
|
||||
| |Logical Machine(LM) ID that causes the BOOT of this LM |
|
||||
| |Bit[23] Valid err ID: |
|
||||
| |Set to 1 if the error ID field is valid. |
|
||||
| |Set to 0 if the error ID field is not valid. |
|
||||
| |Bits[22:8] Error ID(Agent ID of the system). |
|
||||
| |Bit[7:0] Reason(WDOG, POR, FCCU and etc): |
|
||||
| |See the SRESR register description in the System |
|
||||
| |Reset Controller (SRC) section in SoC reference mannual |
|
||||
| |One reason maps to BIT(reason) in SRESR |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|uint32 shutdownflags |Shutdown reason flags. This parameter has the format: |
|
||||
| |Bits[31] Valid. |
|
||||
| |Set to 1 if the entire reason is valid. |
|
||||
| |Set to 0 if the entire reason is not valid. |
|
||||
| |Bits[30:29] Number of valid extended info words. |
|
||||
| |Bit[28] Valid origin: |
|
||||
| |Set to 1 if the origin field is valid. |
|
||||
| |Set to 0 if the origin field is not valid. |
|
||||
| |Bits[27:24] Origin. |
|
||||
| |Logical Machine(LM) ID that causes the BOOT of this LM |
|
||||
| |Bit[23] Valid err ID: |
|
||||
| |Set to 1 if the error ID field is valid. |
|
||||
| |Set to 0 if the error ID field is not valid. |
|
||||
| |Bits[22:8] Error ID(Agent ID of the System). |
|
||||
| |Bit[7:0] Reason |
|
||||
| |See the SRESR register description in the System |
|
||||
| |Reset Controller (SRC) section in SoC reference mannual |
|
||||
| |One reason maps to BIT(reason) in SRESR |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|uint32 extinfo[3] |Array of extended info words(e.g. fault pc) |
|
||||
+---------------------+--------------------------------------------------------+
|
||||
|
||||
LMM_POWER_ON
|
||||
~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xB
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if LM successfully powers on. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical machine. |
|
||||
| |INVALID_PARAMETERS: if lmid is same as the caller. |
|
||||
| |DENIED: if the agent does not have permission to manage the|
|
||||
| |the LM specified by lmid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
LMM_RESET_VECTOR_SET
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xC
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|Parameters |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|uint32 lmid |ID of the Logical Machine |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|uint32 cpuid |ID of the CPU inside the LM |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|uint32 flags |Reset vector flags |
|
||||
| |Bits[31:0] Reserved, must be zero. |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|uint32 resetVectorLow |Lower vector |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|uint32 resetVectorHigh |Higher vector |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|Return values |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|int32 status |SUCCESS: If reset vector is set successfully. |
|
||||
| |NOT_FOUND: if lmid not points to a valid logical |
|
||||
| |machine, or cpuId is not valid. |
|
||||
| |INVALID_PARAMETERS: if reset vector is invalid. |
|
||||
| |DENIED: if the agent does not have permission to set |
|
||||
| |the reset vector for the CPU in the LM. |
|
||||
+-----------------------+------------------------------------------------------+
|
||||
|
||||
NEGOTIATE_PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x10
|
||||
protocol_id: 0x80
|
||||
This command is mandatory.
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Parameters |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 version |The negotiated protocol version the agent intends to use |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the negotiated protocol version is supported |
|
||||
| |by the platform. All commands, responses, and |
|
||||
| |notifications post successful return of this command must|
|
||||
| |comply with the negotiated version. |
|
||||
| |NOT_SUPPORTED: if the protocol version is not supported. |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
Notifications
|
||||
_____________
|
||||
|
||||
LMM_EVENT
|
||||
~~~~~~~~~
|
||||
|
||||
message_id: 0x0
|
||||
protocol_id: 0x80
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 lmid |Identifier for the LM that caused the transition. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 eventlm |Identifier of the LM this event refers to. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |LM events: |
|
||||
| |Bits[31:3] Reserved, must be zero. |
|
||||
| |Bit[3] Wake (resume) event: |
|
||||
| |1 LM has awakened. |
|
||||
| |0 not a wake event. |
|
||||
| |Bit[2] Suspend (sleep) event: |
|
||||
| |1 LM has suspended. |
|
||||
| |0 not a suspend event. |
|
||||
| |Bit[1] Shutdown (off) event: |
|
||||
| |1 LM has shutdown. |
|
||||
| |0 not a shutdown event. |
|
||||
| |Bit[0] Boot (on) event: |
|
||||
| |1 LM has booted. |
|
||||
| |0 not a boot event. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
SCMI_BBM: System Control and Management BBM Vendor Protocol
|
||||
==============================================================
|
||||
|
||||
@ -436,6 +948,322 @@ protocol_id: 0x81
|
||||
| |0 no button change detected. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
System Control and Management CPU Vendor Protocol
|
||||
=================================================
|
||||
|
||||
This protocol allows an agent to start or stop a CPU. It is used to manage
|
||||
auxiliary CPUs in a target LM (e.g. additional cores in an AP cluster or
|
||||
Cortex-M cores).
|
||||
Note:
|
||||
- For cores in AP cluster, PSCI should be used and PSCI firmware will use CPU
|
||||
protocol to handle them. For cores in non-AP cluster, Operating System(e.g.
|
||||
Linux OS) could use CPU protocols to control Cortex-M7 cores.
|
||||
- CPU indicates the core and its auxiliary peripherals(e.g. TCM) inside
|
||||
i.MX SoC
|
||||
|
||||
There are cases where giving an agent full control of a CPU via the CPU
|
||||
protocol is not desired. The LMM protocol is more restricted to just boot,
|
||||
shutdown, etc. So an agent might boot another logical machine but not be
|
||||
able to directly mess the state of its CPUs. Its also the reason there is an
|
||||
LMM power on command even though that could have been done through the
|
||||
power protocol.
|
||||
|
||||
The CPU protocol provides commands to:
|
||||
|
||||
- Describe the protocol version.
|
||||
- Discover implementation attributes.
|
||||
- Discover the CPUs defined in the system.
|
||||
- Start a CPU.
|
||||
- Stop a CPU.
|
||||
- Set the boot and resume addresses for a CPU.
|
||||
- Set the sleep mode of a CPU.
|
||||
- Configure wake-up sources for a CPU.
|
||||
- Configure power domain reactions (LPM mode and retention mask) for a CPU.
|
||||
- The CPU IDs can be found in the CPU section of the SoC DEVICE: SM Device
|
||||
Interface. They can also be found in the SoC RM. See the CPU Mode Control
|
||||
(CMC) list in General Power Controller (GPC) section.
|
||||
|
||||
CPU settings are not aggregated and setting their state is normally exclusive
|
||||
to one client.
|
||||
|
||||
Commands:
|
||||
_________
|
||||
|
||||
PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x0
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|uint32 version | For this revision of the specification, this value must be |
|
||||
| | 0x10000. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|
||||
PROTOCOL_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x1
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Protocol attributes: |
|
||||
| |Bits[31:16] Reserved, must be zero. |
|
||||
| |Bits[15:0] Number of CPUs |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
PROTOCOL_MESSAGE_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x2
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: in case the message is implemented and available |
|
||||
| |to use. |
|
||||
| |NOT_FOUND: if the message identified by message_id is |
|
||||
| |invalid or not implemented |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Flags that are associated with a specific command in the |
|
||||
| |protocol. For all commands in this protocol, this |
|
||||
| |parameter has a value of 0 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
CPU_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x4
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 cpuid |Identifier for the CPU |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if valid attributes are returned successfully. |
|
||||
| |NOT_FOUND: if the cpuid is not valid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Bits[31:0] Reserved, must be zero |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|char name[16] |NULL terminated ASCII string with CPU name up to 16 bytes |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
CPU_START
|
||||
~~~~~~~~~
|
||||
|
||||
message_id: 0x4
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 cpuid |Identifier for the CPU |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the cpu is started successfully. |
|
||||
| |NOT_FOUND: if cpuid is not valid. |
|
||||
| |DENIED: the calling agent is not allowed to start this CPU.|
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
CPU_STOP
|
||||
~~~~~~~~
|
||||
|
||||
message_id: 0x5
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 cpuid |Identifier for the CPU |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the cpu is started successfully. |
|
||||
| |NOT_FOUND: if cpuid is not valid. |
|
||||
| |DENIED: the calling agent is not allowed to stop this CPU. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
CPU_RESET_VECTOR_SET
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x6
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Parameters |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 cpuid |Identifier for the CPU |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 flags |Reset vector flags: |
|
||||
| |Bit[31] Resume flag. |
|
||||
| |Set to 1 to update the reset vector used on resume. |
|
||||
| |Bit[30] Boot flag. |
|
||||
| |Set to 1 to update the reset vector used for boot. |
|
||||
| |Bits[29:1] Reserved, must be zero. |
|
||||
| |Bit[0] Table flag. |
|
||||
| |Set to 1 if vector is the vector table base address. |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 resetVectorLow |Lower vector: |
|
||||
| |If bit[0] of flags is 0, the lower 32 bits of the |
|
||||
| |physical address where the CPU should execute from on |
|
||||
| |reset. If bit[0] of flags is 1, the lower 32 bits of |
|
||||
| |the vector table base address |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 resetVectorhigh|Upper vector: |
|
||||
| |If bit[0] of flags is 0, the upper 32 bits of the |
|
||||
| |physical address where the CPU should execute from on |
|
||||
| |reset. If bit[0] of flags is 1, the upper 32 bits of |
|
||||
| |the vector table base address |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Return values |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the CPU reset vector is set successfully. |
|
||||
| |NOT_FOUND: if cpuId does not point to a valid CPU. |
|
||||
| |INVALID_PARAMETERS: the requested vector type is not |
|
||||
| |supported by this CPU. |
|
||||
| |DENIED: the calling agent is not allowed to set the |
|
||||
| |reset vector of this CPU |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|
||||
CPU_SLEEP_MODE_SET
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x7
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Parameters |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 cpuid |Identifier for the CPU |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 flags |Sleep mode flags: |
|
||||
| |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] IRQ mux: |
|
||||
| |If set to 1 the wakeup mux source is the GIC, else if 0|
|
||||
| |then the GPC |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 sleepmode |target sleep mode. When CPU runs into WFI, the GPC mode|
|
||||
| |will be triggered to be in below modes: |
|
||||
| |RUN: (0) |
|
||||
| |WAIT: (1) |
|
||||
| |STOP: (2) |
|
||||
| |SUSPEND: (3) |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Return values |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the CPU sleep mode is set successfully. |
|
||||
| |NOT_FOUND: if cpuId does not point to a valid CPU. |
|
||||
| |INVALID_PARAMETERS: the sleepmode or flags is invalid. |
|
||||
| |DENIED: the calling agent is not allowed to configure |
|
||||
| |the CPU |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|
||||
CPU_INFO_GET
|
||||
~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xC
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Parameters |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 cpuid |Identifier for the CPU |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Return values |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if valid attributes are returned successfully.|
|
||||
| |NOT_FOUND: if the cpuid is not valid. |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 runmode |Run mode for the CPU |
|
||||
| |RUN(0):cpu started |
|
||||
| |HOLD(1):cpu powered up and reset asserted |
|
||||
| |STOP(2):cpu reseted and hold cpu |
|
||||
| |SUSPEND(3):in cpuidle state |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 sleepmode |Sleep mode for the CPU, see CPU_SLEEP_MODE_SET |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 resetvectorlow |Reset vector low 32 bits for the CPU |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|uint32 resetvecothigh |Reset vector high 32 bits for the CPU |
|
||||
+----------------------+-------------------------------------------------------+
|
||||
|
||||
NEGOTIATE_PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x10
|
||||
protocol_id: 0x82
|
||||
This command is mandatory.
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Parameters |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 version |The negotiated protocol version the agent intends to use |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the negotiated protocol version is supported |
|
||||
| |by the platform. All commands, responses, and |
|
||||
| |notifications post successful return of this command must|
|
||||
| |comply with the negotiated version. |
|
||||
| |NOT_SUPPORTED: if the protocol version is not supported. |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
SCMI_MISC: System Control and Management MISC Vendor Protocol
|
||||
================================================================
|
||||
|
||||
|
@ -23,6 +23,28 @@ config IMX_SCU
|
||||
This driver manages the IPC interface between host CPU and the
|
||||
SCU firmware running on M4.
|
||||
|
||||
config IMX_SCMI_CPU_DRV
|
||||
tristate "IMX SCMI CPU Protocol driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
The System Controller Management Interface firmware (SCMI FW) is
|
||||
a low-level system function which runs on a dedicated Cortex-M
|
||||
core that could provide cpu management features.
|
||||
|
||||
This driver can also be built as a module.
|
||||
|
||||
config IMX_SCMI_LMM_DRV
|
||||
tristate "IMX SCMI LMM Protocol driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
The System Controller Management Interface firmware (SCMI FW) is
|
||||
a low-level system function which runs on a dedicated Cortex-M
|
||||
core that could provide Logical Machine management features.
|
||||
|
||||
This driver can also be built as a module.
|
||||
|
||||
config IMX_SCMI_MISC_DRV
|
||||
tristate "IMX SCMI MISC Protocol driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
|
@ -1,4 +1,6 @@
|
||||
# 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 rm.o imx-scu-soc.o
|
||||
obj-${CONFIG_IMX_SCMI_CPU_DRV} += sm-cpu.o
|
||||
obj-${CONFIG_IMX_SCMI_MISC_DRV} += sm-misc.o
|
||||
obj-${CONFIG_IMX_SCMI_LMM_DRV} += sm-lmm.o
|
||||
|
85
drivers/firmware/imx/sm-cpu.c
Normal file
85
drivers/firmware/imx/sm-cpu.c
Normal file
@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
|
||||
#include <linux/firmware/imx/sm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
static const struct scmi_imx_cpu_proto_ops *imx_cpu_ops;
|
||||
static struct scmi_protocol_handle *ph;
|
||||
|
||||
int scmi_imx_cpu_reset_vector_set(u32 cpuid, u64 vector, bool start, bool boot,
|
||||
bool resume)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return imx_cpu_ops->cpu_reset_vector_set(ph, cpuid, vector, start,
|
||||
boot, resume);
|
||||
}
|
||||
EXPORT_SYMBOL(scmi_imx_cpu_reset_vector_set);
|
||||
|
||||
int scmi_imx_cpu_start(u32 cpuid, bool start)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (start)
|
||||
return imx_cpu_ops->cpu_start(ph, cpuid, true);
|
||||
|
||||
return imx_cpu_ops->cpu_start(ph, cpuid, false);
|
||||
};
|
||||
EXPORT_SYMBOL(scmi_imx_cpu_start);
|
||||
|
||||
int scmi_imx_cpu_started(u32 cpuid, bool *started)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!started)
|
||||
return -EINVAL;
|
||||
|
||||
return imx_cpu_ops->cpu_started(ph, cpuid, started);
|
||||
};
|
||||
EXPORT_SYMBOL(scmi_imx_cpu_started);
|
||||
|
||||
static int scmi_imx_cpu_probe(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
if (imx_cpu_ops) {
|
||||
dev_err(&sdev->dev, "sm cpu already initialized\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
imx_cpu_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_CPU, &ph);
|
||||
if (IS_ERR(imx_cpu_ops))
|
||||
return PTR_ERR(imx_cpu_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_IMX_CPU, "imx-cpu" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_imx_cpu_driver = {
|
||||
.name = "scmi-imx-cpu",
|
||||
.probe = scmi_imx_cpu_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_imx_cpu_driver);
|
||||
|
||||
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX SM CPU driver");
|
||||
MODULE_LICENSE("GPL");
|
91
drivers/firmware/imx/sm-lmm.c
Normal file
91
drivers/firmware/imx/sm-lmm.c
Normal file
@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
|
||||
#include <linux/firmware/imx/sm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
static const struct scmi_imx_lmm_proto_ops *imx_lmm_ops;
|
||||
static struct scmi_protocol_handle *ph;
|
||||
|
||||
int scmi_imx_lmm_info(u32 lmid, struct scmi_imx_lmm_info *info)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
|
||||
return imx_lmm_ops->lmm_info(ph, lmid, info);
|
||||
};
|
||||
EXPORT_SYMBOL(scmi_imx_lmm_info);
|
||||
|
||||
int scmi_imx_lmm_reset_vector_set(u32 lmid, u32 cpuid, u32 flags, u64 vector)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return imx_lmm_ops->lmm_reset_vector_set(ph, lmid, cpuid, flags, vector);
|
||||
}
|
||||
EXPORT_SYMBOL(scmi_imx_lmm_reset_vector_set);
|
||||
|
||||
int scmi_imx_lmm_operation(u32 lmid, enum scmi_imx_lmm_op op, u32 flags)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
switch (op) {
|
||||
case SCMI_IMX_LMM_BOOT:
|
||||
return imx_lmm_ops->lmm_power_boot(ph, lmid, true);
|
||||
case SCMI_IMX_LMM_POWER_ON:
|
||||
return imx_lmm_ops->lmm_power_boot(ph, lmid, false);
|
||||
case SCMI_IMX_LMM_SHUTDOWN:
|
||||
return imx_lmm_ops->lmm_shutdown(ph, lmid, flags);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(scmi_imx_lmm_operation);
|
||||
|
||||
static int scmi_imx_lmm_probe(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
if (imx_lmm_ops) {
|
||||
dev_err(&sdev->dev, "lmm already initialized\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
imx_lmm_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_LMM, &ph);
|
||||
if (IS_ERR(imx_lmm_ops))
|
||||
return PTR_ERR(imx_lmm_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_IMX_LMM, "imx-lmm" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_imx_lmm_driver = {
|
||||
.name = "scmi-imx-lmm",
|
||||
.probe = scmi_imx_lmm_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_imx_lmm_driver);
|
||||
|
||||
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX SM LMM driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1986,7 +1986,10 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send);
|
||||
*/
|
||||
static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
|
||||
{ .compatible = "asus,vivobook-s15" },
|
||||
{ .compatible = "asus,zenbook-a14-ux3407qa" },
|
||||
{ .compatible = "asus,zenbook-a14-ux3407ra" },
|
||||
{ .compatible = "dell,xps13-9345" },
|
||||
{ .compatible = "hp,elitebook-ultra-g1q" },
|
||||
{ .compatible = "hp,omnibook-x14" },
|
||||
{ .compatible = "huawei,gaokun3" },
|
||||
{ .compatible = "lenovo,flex-5g" },
|
||||
|
@ -44,8 +44,11 @@ enum qcom_scm_arg_types {
|
||||
|
||||
/**
|
||||
* struct qcom_scm_desc
|
||||
* @svc: Service identifier
|
||||
* @cmd: Command identifier
|
||||
* @arginfo: Metadata describing the arguments in args[]
|
||||
* @args: The array of arguments for the secure syscall
|
||||
* @owner: Owner identifier
|
||||
*/
|
||||
struct qcom_scm_desc {
|
||||
u32 svc;
|
||||
|
@ -79,6 +79,7 @@ static const char *const qcom_tzmem_blacklist[] = {
|
||||
"qcom,sc8180x",
|
||||
"qcom,sdm670", /* failure in GPU firmware loading */
|
||||
"qcom,sdm845", /* reset in rmtfs memory assignment */
|
||||
"qcom,sm7150", /* reset in rmtfs memory assignment */
|
||||
"qcom,sm8150", /* reset in rmtfs memory assignment */
|
||||
NULL
|
||||
};
|
||||
|
@ -43,13 +43,13 @@ static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i)
|
||||
return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK;
|
||||
}
|
||||
|
||||
static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd,
|
||||
static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
|
||||
unsigned int acpm_chan_id)
|
||||
{
|
||||
xfer->txd = cmd;
|
||||
xfer->rxd = cmd;
|
||||
xfer->txlen = sizeof(cmd);
|
||||
xfer->rxlen = sizeof(cmd);
|
||||
xfer->txlen = cmdlen;
|
||||
xfer->rxlen = cmdlen;
|
||||
xfer->acpm_chan_id = acpm_chan_id;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ int acpm_pmic_read_reg(const struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_read_cmd(cmd, type, reg, chan);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@ -104,7 +104,7 @@ int acpm_pmic_bulk_read(const struct acpm_handle *handle,
|
||||
return -EINVAL;
|
||||
|
||||
acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@ -144,7 +144,7 @@ int acpm_pmic_write_reg(const struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_write_cmd(cmd, type, reg, chan, value);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@ -184,7 +184,7 @@ int acpm_pmic_bulk_write(const struct acpm_handle *handle,
|
||||
return -EINVAL;
|
||||
|
||||
acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@ -214,7 +214,7 @@ int acpm_pmic_update_reg(const struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/firmware/samsung/exynos-acpm-protocol.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/mailbox/exynos-message.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
@ -32,8 +33,7 @@
|
||||
|
||||
#define ACPM_PROTOCOL_SEQNUM GENMASK(21, 16)
|
||||
|
||||
/* The unit of counter is 20 us. 5000 * 20 = 100 ms */
|
||||
#define ACPM_POLL_TIMEOUT 5000
|
||||
#define ACPM_POLL_TIMEOUT_US (100 * USEC_PER_MSEC)
|
||||
#define ACPM_TX_TIMEOUT_US 500000
|
||||
|
||||
#define ACPM_GS101_INITDATA_BASE 0xa000
|
||||
@ -300,12 +300,13 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan,
|
||||
const struct acpm_xfer *xfer)
|
||||
{
|
||||
struct device *dev = achan->acpm->dev;
|
||||
unsigned int cnt_20us = 0;
|
||||
ktime_t timeout;
|
||||
u32 seqnum;
|
||||
int ret;
|
||||
|
||||
seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
|
||||
|
||||
timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US);
|
||||
do {
|
||||
ret = acpm_get_rx(achan, xfer);
|
||||
if (ret)
|
||||
@ -315,12 +316,11 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan,
|
||||
return 0;
|
||||
|
||||
/* Determined experimentally. */
|
||||
usleep_range(20, 30);
|
||||
cnt_20us++;
|
||||
} while (cnt_20us < ACPM_POLL_TIMEOUT);
|
||||
udelay(20);
|
||||
} while (ktime_before(ktime_get(), timeout));
|
||||
|
||||
dev_err(dev, "Timeout! ch:%u s:%u bitmap:%lx, cnt_20us = %d.\n",
|
||||
achan->id, seqnum, achan->bitmap_seqnum[0], cnt_20us);
|
||||
dev_err(dev, "Timeout! ch:%u s:%u bitmap:%lx.\n",
|
||||
achan->id, seqnum, achan->bitmap_seqnum[0]);
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
@ -649,7 +649,7 @@ static int acpm_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, acpm);
|
||||
|
||||
return 0;
|
||||
return devm_of_platform_populate(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -677,43 +677,30 @@ static void devm_acpm_release(struct device *dev, void *res)
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_get_by_phandle() - get the ACPM handle using DT phandle.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @property: property name containing phandle on ACPM node.
|
||||
* acpm_get_by_node() - get the ACPM handle using node pointer.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @np: ACPM device tree node.
|
||||
*
|
||||
* Return: pointer to handle on success, ERR_PTR(-errno) otherwise.
|
||||
*/
|
||||
static const struct acpm_handle *acpm_get_by_phandle(struct device *dev,
|
||||
const char *property)
|
||||
static const struct acpm_handle *acpm_get_by_node(struct device *dev,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *acpm_np;
|
||||
struct device_link *link;
|
||||
struct acpm_info *acpm;
|
||||
|
||||
acpm_np = of_parse_phandle(dev->of_node, property, 0);
|
||||
if (!acpm_np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pdev = of_find_device_by_node(acpm_np);
|
||||
if (!pdev) {
|
||||
dev_err(dev, "Cannot find device node %s\n", acpm_np->name);
|
||||
of_node_put(acpm_np);
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
of_node_put(acpm_np);
|
||||
|
||||
acpm = platform_get_drvdata(pdev);
|
||||
if (!acpm) {
|
||||
dev_err(dev, "Cannot get drvdata from %s\n",
|
||||
dev_name(&pdev->dev));
|
||||
platform_device_put(pdev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
if (!try_module_get(pdev->dev.driver->owner)) {
|
||||
dev_err(dev, "Cannot get module reference.\n");
|
||||
platform_device_put(pdev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
@ -732,14 +719,14 @@ static const struct acpm_handle *acpm_get_by_phandle(struct device *dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_acpm_get_by_phandle() - managed get handle using phandle.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @property: property name containing phandle on ACPM node.
|
||||
* devm_acpm_get_by_node() - managed get handle using node pointer.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @np: ACPM device tree node.
|
||||
*
|
||||
* Return: pointer to handle on success, ERR_PTR(-errno) otherwise.
|
||||
*/
|
||||
const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev,
|
||||
const char *property)
|
||||
const struct acpm_handle *devm_acpm_get_by_node(struct device *dev,
|
||||
struct device_node *np)
|
||||
{
|
||||
const struct acpm_handle **ptr, *handle;
|
||||
|
||||
@ -747,7 +734,7 @@ const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev,
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
handle = acpm_get_by_phandle(dev, property);
|
||||
handle = acpm_get_by_node(dev, np);
|
||||
if (!IS_ERR(handle)) {
|
||||
*ptr = handle;
|
||||
devres_add(dev, ptr);
|
||||
@ -757,6 +744,7 @@ const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev,
|
||||
|
||||
return handle;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_acpm_get_by_node);
|
||||
|
||||
static const struct acpm_match_data acpm_gs101 = {
|
||||
.initdata_base = ACPM_GS101_INITDATA_BASE,
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Texas Instruments System Control Interface Protocol Driver
|
||||
*
|
||||
* Copyright (C) 2015-2024 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Copyright (C) 2015-2025 Texas Instruments Incorporated - https://www.ti.com/
|
||||
* Nishanth Menon
|
||||
*/
|
||||
|
||||
@ -3670,6 +3670,7 @@ static int __maybe_unused ti_sci_suspend(struct device *dev)
|
||||
struct ti_sci_info *info = dev_get_drvdata(dev);
|
||||
struct device *cpu_dev, *cpu_dev_max = NULL;
|
||||
s32 val, cpu_lat = 0;
|
||||
u16 cpu_lat_ms;
|
||||
int i, ret;
|
||||
|
||||
if (info->fw_caps & MSG_FLAG_CAPS_LPM_DM_MANAGED) {
|
||||
@ -3682,9 +3683,16 @@ static int __maybe_unused ti_sci_suspend(struct device *dev)
|
||||
}
|
||||
}
|
||||
if (cpu_dev_max) {
|
||||
dev_dbg(cpu_dev_max, "%s: sending max CPU latency=%u\n", __func__, cpu_lat);
|
||||
/*
|
||||
* PM QoS latency unit is usecs, device manager uses msecs.
|
||||
* Convert to msecs and round down for device manager.
|
||||
*/
|
||||
cpu_lat_ms = cpu_lat / USEC_PER_MSEC;
|
||||
dev_dbg(cpu_dev_max, "%s: sending max CPU latency=%u ms\n", __func__,
|
||||
cpu_lat_ms);
|
||||
ret = ti_sci_cmd_set_latency_constraint(&info->handle,
|
||||
cpu_lat, TISCI_MSG_CONSTRAINT_SET);
|
||||
cpu_lat_ms,
|
||||
TISCI_MSG_CONSTRAINT_SET);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -2,29 +2,31 @@
|
||||
/*
|
||||
* Turris Mox rWTM firmware driver
|
||||
*
|
||||
* Copyright (C) 2019, 2024 Marek Behún <kabel@kernel.org>
|
||||
* Copyright (C) 2019, 2024, 2025 Marek Behún <kabel@kernel.org>
|
||||
*/
|
||||
|
||||
#include <crypto/sha2.h>
|
||||
#include <linux/align.h>
|
||||
#include <linux/armada-37xx-rwtm-mailbox.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/turris-signing-key.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DRIVER_NAME "turris-mox-rwtm"
|
||||
@ -37,10 +39,13 @@
|
||||
* https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi.
|
||||
*/
|
||||
|
||||
#define MOX_ECC_NUMBER_WORDS 17
|
||||
#define MOX_ECC_NUMBER_LEN (MOX_ECC_NUMBER_WORDS * sizeof(u32))
|
||||
|
||||
#define MOX_ECC_SIGNATURE_WORDS (2 * MOX_ECC_NUMBER_WORDS)
|
||||
enum {
|
||||
MOX_ECC_NUM_BITS = 521,
|
||||
MOX_ECC_NUM_LEN = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 8),
|
||||
MOX_ECC_NUM_WORDS = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 32),
|
||||
MOX_ECC_SIG_LEN = 2 * MOX_ECC_NUM_LEN,
|
||||
MOX_ECC_PUBKEY_LEN = 1 + MOX_ECC_NUM_LEN,
|
||||
};
|
||||
|
||||
#define MBOX_STS_SUCCESS (0 << 30)
|
||||
#define MBOX_STS_FAIL (1 << 30)
|
||||
@ -77,10 +82,7 @@ enum mbox_cmd {
|
||||
* @ram_size: RAM size of the device
|
||||
* @mac_address1: first MAC address of the device
|
||||
* @mac_address2: second MAC address of the device
|
||||
* @has_pubkey: whether board ECDSA public key is present
|
||||
* @pubkey: board ECDSA public key
|
||||
* @last_sig: last ECDSA signature generated with board ECDSA private key
|
||||
* @last_sig_done: whether the last ECDSA signing is complete
|
||||
*/
|
||||
struct mox_rwtm {
|
||||
struct mbox_client mbox_client;
|
||||
@ -100,18 +102,8 @@ struct mox_rwtm {
|
||||
int board_version, ram_size;
|
||||
u8 mac_address1[ETH_ALEN], mac_address2[ETH_ALEN];
|
||||
|
||||
bool 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.
|
||||
*/
|
||||
u32 last_sig[MOX_ECC_SIGNATURE_WORDS];
|
||||
bool last_sig_done;
|
||||
#ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL
|
||||
u8 pubkey[MOX_ECC_PUBKEY_LEN];
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -120,24 +112,23 @@ static inline struct device *rwtm_dev(struct mox_rwtm *rwtm)
|
||||
return rwtm->mbox_client.dev;
|
||||
}
|
||||
|
||||
#define MOX_ATTR_RO(name, format, cat) \
|
||||
#define MOX_ATTR_RO(name, format) \
|
||||
static ssize_t \
|
||||
name##_show(struct device *dev, struct device_attribute *a, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct mox_rwtm *rwtm = dev_get_drvdata(dev); \
|
||||
if (!rwtm->has_##cat) \
|
||||
if (!rwtm->has_board_info) \
|
||||
return -ENODATA; \
|
||||
return sysfs_emit(buf, format, rwtm->name); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
MOX_ATTR_RO(serial_number, "%016llX\n", board_info);
|
||||
MOX_ATTR_RO(board_version, "%i\n", board_info);
|
||||
MOX_ATTR_RO(ram_size, "%i\n", board_info);
|
||||
MOX_ATTR_RO(mac_address1, "%pM\n", board_info);
|
||||
MOX_ATTR_RO(mac_address2, "%pM\n", board_info);
|
||||
MOX_ATTR_RO(pubkey, "%s\n", pubkey);
|
||||
MOX_ATTR_RO(serial_number, "%016llX\n");
|
||||
MOX_ATTR_RO(board_version, "%i\n");
|
||||
MOX_ATTR_RO(ram_size, "%i\n");
|
||||
MOX_ATTR_RO(mac_address1, "%pM\n");
|
||||
MOX_ATTR_RO(mac_address2, "%pM\n");
|
||||
|
||||
static struct attribute *turris_mox_rwtm_attrs[] = {
|
||||
&dev_attr_serial_number.attr,
|
||||
@ -145,7 +136,6 @@ static struct attribute *turris_mox_rwtm_attrs[] = {
|
||||
&dev_attr_ram_size.attr,
|
||||
&dev_attr_mac_address1.attr,
|
||||
&dev_attr_mac_address2.attr,
|
||||
&dev_attr_pubkey.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(turris_mox_rwtm);
|
||||
@ -247,24 +237,6 @@ static int mox_get_board_info(struct mox_rwtm *rwtm)
|
||||
pr_info(" burned RAM size %i MiB\n", rwtm->ram_size);
|
||||
}
|
||||
|
||||
ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false);
|
||||
if (ret == -ENODATA) {
|
||||
dev_warn(dev, "Board has no public key burned!\n");
|
||||
} else if (ret == -EOPNOTSUPP) {
|
||||
dev_notice(dev,
|
||||
"Firmware does not support the ECDSA_PUB_KEY command\n");
|
||||
} else if (ret < 0) {
|
||||
return ret;
|
||||
} else {
|
||||
u32 *s = reply->status;
|
||||
|
||||
rwtm->has_pubkey = true;
|
||||
sprintf(rwtm->pubkey,
|
||||
"%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],
|
||||
s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -306,127 +278,139 @@ 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;
|
||||
#ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
static void mox_ecc_number_to_bin(void *dst, const u32 *src)
|
||||
{
|
||||
__be32 tmp[MOX_ECC_NUM_WORDS];
|
||||
|
||||
cpu_to_be32_array(tmp, src, MOX_ECC_NUM_WORDS);
|
||||
|
||||
memcpy(dst, (void *)tmp + 2, MOX_ECC_NUM_LEN);
|
||||
}
|
||||
|
||||
static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len,
|
||||
loff_t *ppos)
|
||||
static void mox_ecc_public_key_to_bin(void *dst, u32 src_first,
|
||||
const u32 *src_rest)
|
||||
{
|
||||
struct mox_rwtm *rwtm = file->private_data;
|
||||
ssize_t ret;
|
||||
__be32 tmp[MOX_ECC_NUM_WORDS - 1];
|
||||
u8 *p = dst;
|
||||
|
||||
/* only allow one read, of whole signature, from position 0 */
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
/* take 3 bytes from the first word */
|
||||
*p++ = src_first >> 16;
|
||||
*p++ = src_first >> 8;
|
||||
*p++ = src_first;
|
||||
|
||||
if (len < sizeof(rwtm->last_sig))
|
||||
return -EINVAL;
|
||||
|
||||
if (!rwtm->last_sig_done)
|
||||
return -ENODATA;
|
||||
|
||||
ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig,
|
||||
sizeof(rwtm->last_sig));
|
||||
rwtm->last_sig_done = false;
|
||||
|
||||
return ret;
|
||||
/* take the rest of the words */
|
||||
cpu_to_be32_array(tmp, src_rest, MOX_ECC_NUM_WORDS - 1);
|
||||
memcpy(p, tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
static ssize_t do_sign_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
static int mox_rwtm_sign(const struct key *key, const void *data, void *signature)
|
||||
{
|
||||
struct mox_rwtm *rwtm = file->private_data;
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
loff_t dummy = 0;
|
||||
ssize_t ret;
|
||||
struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key));
|
||||
struct armada_37xx_rwtm_tx_msg msg = {};
|
||||
u32 offset_r, offset_s;
|
||||
int ret;
|
||||
|
||||
if (len != SHA512_DIGEST_SIZE)
|
||||
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;
|
||||
guard(mutex)(&rwtm->busy);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* For MBOX_CMD_SIGN command:
|
||||
* args[0] - must be 1
|
||||
* args[1] - address of message M to sign; message is a 521-bit number
|
||||
* args[2] - address where the R part of the signature will be stored
|
||||
* args[3] - address where the S part of the signature will be stored
|
||||
*
|
||||
* M, R and S are 521-bit numbers encoded as seventeen 32-bit words,
|
||||
* most significat word first.
|
||||
* Since the message in @data is a sha512 digest, the most significat
|
||||
* word is always zero.
|
||||
*/
|
||||
|
||||
offset_r = MOX_ECC_NUM_WORDS * sizeof(u32);
|
||||
offset_s = 2 * MOX_ECC_NUM_WORDS * sizeof(u32);
|
||||
|
||||
memset(rwtm->buf, 0, sizeof(u32));
|
||||
ret = simple_write_to_buffer(rwtm->buf + sizeof(u32),
|
||||
SHA512_DIGEST_SIZE, &dummy, buf, len);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUMBER_WORDS);
|
||||
memcpy(rwtm->buf + sizeof(u32), data, SHA512_DIGEST_SIZE);
|
||||
be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUM_WORDS);
|
||||
|
||||
msg.args[0] = 1;
|
||||
msg.args[1] = rwtm->buf_phys;
|
||||
msg.args[2] = rwtm->buf_phys + MOX_ECC_NUMBER_LEN;
|
||||
msg.args[3] = rwtm->buf_phys + 2 * MOX_ECC_NUMBER_LEN;
|
||||
msg.args[2] = rwtm->buf_phys + offset_r;
|
||||
msg.args[3] = rwtm->buf_phys + offset_s;
|
||||
|
||||
ret = mox_rwtm_exec(rwtm, MBOX_CMD_SIGN, &msg, true);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* 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 + MOX_ECC_NUMBER_LEN,
|
||||
sizeof(rwtm->last_sig));
|
||||
cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig,
|
||||
MOX_ECC_SIGNATURE_WORDS);
|
||||
rwtm->last_sig_done = true;
|
||||
/* convert R and S parts of the signature */
|
||||
mox_ecc_number_to_bin(signature, rwtm->buf + offset_r);
|
||||
mox_ecc_number_to_bin(signature + MOX_ECC_NUM_LEN, rwtm->buf + offset_s);
|
||||
|
||||
mutex_unlock(&rwtm->busy);
|
||||
return len;
|
||||
unlock_mutex:
|
||||
mutex_unlock(&rwtm->busy);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations do_sign_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rwtm_debug_open,
|
||||
.read = do_sign_read,
|
||||
.write = do_sign_write,
|
||||
static const void *mox_rwtm_get_public_key(const struct key *key)
|
||||
{
|
||||
struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key));
|
||||
|
||||
return rwtm->pubkey;
|
||||
}
|
||||
|
||||
static const struct turris_signing_key_subtype mox_signing_key_subtype = {
|
||||
.key_size = MOX_ECC_NUM_BITS,
|
||||
.data_size = SHA512_DIGEST_SIZE,
|
||||
.sig_size = MOX_ECC_SIG_LEN,
|
||||
.public_key_size = MOX_ECC_PUBKEY_LEN,
|
||||
.hash_algo = "sha512",
|
||||
.get_public_key = mox_rwtm_get_public_key,
|
||||
.sign = mox_rwtm_sign,
|
||||
};
|
||||
|
||||
static void rwtm_debugfs_release(void *root)
|
||||
static int mox_register_signing_key(struct mox_rwtm *rwtm)
|
||||
{
|
||||
debugfs_remove_recursive(root);
|
||||
struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
|
||||
struct device *dev = rwtm_dev(rwtm);
|
||||
int ret;
|
||||
|
||||
ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false);
|
||||
if (ret == -ENODATA) {
|
||||
dev_warn(dev, "Board has no public key burned!\n");
|
||||
} else if (ret == -EOPNOTSUPP) {
|
||||
dev_notice(dev,
|
||||
"Firmware does not support the ECDSA_PUB_KEY command\n");
|
||||
} else if (ret < 0) {
|
||||
return ret;
|
||||
} else {
|
||||
char sn[17] = "unknown";
|
||||
char desc[46];
|
||||
|
||||
if (rwtm->has_board_info)
|
||||
sprintf(sn, "%016llX", rwtm->serial_number);
|
||||
|
||||
sprintf(desc, "Turris MOX SN %s rWTM ECDSA key", sn);
|
||||
|
||||
mox_ecc_public_key_to_bin(rwtm->pubkey, ret, reply->status);
|
||||
|
||||
ret = devm_turris_signing_key_create(dev,
|
||||
&mox_signing_key_subtype,
|
||||
desc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Cannot create signing key\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
#else /* CONFIG_TURRIS_MOX_RWTM_KEYCTL */
|
||||
|
||||
static inline int mox_register_signing_key(struct mox_rwtm *rwtm)
|
||||
{
|
||||
struct dentry *root;
|
||||
|
||||
root = debugfs_create_dir("turris-mox-rwtm", NULL);
|
||||
|
||||
debugfs_create_file_unsafe("do_sign", 0600, root, rwtm, &do_sign_fops);
|
||||
|
||||
devm_add_action_or_reset(rwtm_dev(rwtm), rwtm_debugfs_release, root);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline void rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !CONFIG_TURRIS_MOX_RWTM_KEYCTL */
|
||||
|
||||
static void rwtm_devm_mbox_release(void *mbox)
|
||||
{
|
||||
@ -477,6 +461,10 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
dev_warn(dev, "Cannot read board information: %i\n", ret);
|
||||
|
||||
ret = mox_register_signing_key(rwtm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = check_get_random_support(rwtm);
|
||||
if (ret < 0) {
|
||||
dev_notice(dev,
|
||||
@ -491,8 +479,6 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Cannot register HWRNG!\n");
|
||||
|
||||
rwtm_register_debugfs(rwtm);
|
||||
|
||||
dev_info(dev, "HWRNG successfully registered\n");
|
||||
|
||||
/*
|
||||
|
@ -32,7 +32,7 @@ config ARM_PL172_MPMC
|
||||
|
||||
config ATMEL_EBI
|
||||
bool "Atmel EBI driver"
|
||||
default y if ARCH_AT91
|
||||
default ARCH_AT91
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on OF
|
||||
select MFD_SYSCON
|
||||
@ -147,7 +147,7 @@ config FPGA_DFL_EMIF
|
||||
|
||||
config MVEBU_DEVBUS
|
||||
bool "Marvell EBU Device Bus Controller"
|
||||
default y if PLAT_ORION
|
||||
default PLAT_ORION
|
||||
depends on PLAT_ORION || COMPILE_TEST
|
||||
depends on OF
|
||||
help
|
||||
@ -198,7 +198,7 @@ config DA8XX_DDRCTL
|
||||
|
||||
config PL353_SMC
|
||||
tristate "ARM PL35X Static Memory Controller(SMC) driver"
|
||||
default y if ARM
|
||||
default ARM
|
||||
depends on ARM || COMPILE_TEST
|
||||
depends on ARM_AMBA
|
||||
help
|
||||
@ -225,6 +225,23 @@ config STM32_FMC2_EBI
|
||||
devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
|
||||
SOCs containing the FMC2 External Bus Interface.
|
||||
|
||||
config STM32_OMM
|
||||
tristate "STM32 Octo Memory Manager"
|
||||
depends on SPI_STM32_OSPI || COMPILE_TEST
|
||||
help
|
||||
This driver manages the muxing between the 2 OSPI busses and
|
||||
the 2 output ports. There are 4 possible muxing configurations:
|
||||
- direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
|
||||
output is on port 2
|
||||
- OSPI1 and OSPI2 are multiplexed over the same output port 1
|
||||
- swapped mode (no multiplexing), OSPI1 output is on port 2,
|
||||
OSPI2 output is on port 1
|
||||
- OSPI1 and OSPI2 are multiplexed over the same output port 2
|
||||
It also manages :
|
||||
- the split of the memory area shared between the 2 OSPI instances.
|
||||
- chip select selection override.
|
||||
- the time between 2 transactions in multiplexed mode.
|
||||
|
||||
source "drivers/memory/samsung/Kconfig"
|
||||
source "drivers/memory/tegra/Kconfig"
|
||||
|
||||
|
@ -24,6 +24,7 @@ obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o
|
||||
obj-$(CONFIG_PL353_SMC) += pl353-smc.o
|
||||
obj-$(CONFIG_RENESAS_RPCIF) += renesas-rpc-if.o
|
||||
obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
|
||||
obj-$(CONFIG_STM32_OMM) += stm32_omm.o
|
||||
|
||||
obj-$(CONFIG_SAMSUNG_MC) += samsung/
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra/
|
||||
|
@ -222,7 +222,7 @@ static ssize_t l2_ctl_latency_show(struct device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", data);
|
||||
return sysfs_emit(buf, "%u\n", data);
|
||||
}
|
||||
|
||||
static ssize_t l2_ctl_latency_store(struct device *dev,
|
||||
|
@ -283,6 +283,43 @@ static int mtk_smi_larb_config_port_gen2_general(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u8 mtk_smi_larb_mt6893_ostd[][SMI_LARB_PORT_NR_MAX] = {
|
||||
[0] = {0x2, 0x6, 0x2, 0x2, 0x2, 0x28, 0x18, 0x18, 0x1, 0x1, 0x1, 0x8,
|
||||
0x8, 0x1, 0x3f},
|
||||
[1] = {0x2, 0x6, 0x2, 0x2, 0x2, 0x28, 0x18, 0x18, 0x1, 0x1, 0x1, 0x8,
|
||||
0x8, 0x1, 0x3f},
|
||||
[2] = {0x5, 0x5, 0x5, 0x5, 0x1, 0x3f},
|
||||
[3] = {0x5, 0x5, 0x5, 0x5, 0x1, 0x3f},
|
||||
[4] = {0x28, 0x19, 0xb, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1},
|
||||
[5] = {0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x16},
|
||||
[6] = {},
|
||||
[7] = {0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x4, 0x1,
|
||||
0x4, 0x1, 0xa, 0x6, 0x1, 0xa, 0x6, 0x1, 0x1, 0x1, 0x1, 0x5,
|
||||
0x3, 0x3, 0x4},
|
||||
[8] = {0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x4, 0x1,
|
||||
0x4, 0x1, 0xa, 0x6, 0x1, 0xa, 0x6, 0x1, 0x1, 0x1, 0x1, 0x5,
|
||||
0x3, 0x3, 0x4},
|
||||
[9] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0x6, 0x7, 0x4,
|
||||
0x9, 0x3, 0x4, 0xe, 0x1, 0x7, 0x8, 0x7, 0x7, 0x1, 0x6, 0x2,
|
||||
0xf, 0x8, 0x1, 0x1, 0x1},
|
||||
[10] = {},
|
||||
[11] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0x6, 0x7, 0x4,
|
||||
0x9, 0x3, 0x4, 0xe, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
0x1, 0x1, 0x1, 0x1, 0x1},
|
||||
[12] = {},
|
||||
[13] = {0x2, 0xc, 0xc, 0xe, 0x6, 0x6, 0x6, 0x6, 0x6, 0x12, 0x6, 0x1},
|
||||
[14] = {0x2, 0xc, 0xc, 0x28, 0x12, 0x6},
|
||||
[15] = {0x28, 0x1, 0x2, 0x28, 0x1},
|
||||
[16] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x2, 0x14, 0x14, 0x4, 0x4, 0x4, 0x2,
|
||||
0x4, 0x2, 0x8, 0x4, 0x4},
|
||||
[17] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x2, 0x14, 0x14, 0x4, 0x4, 0x4, 0x2,
|
||||
0x4, 0x2, 0x8, 0x4, 0x4},
|
||||
[18] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x2, 0x14, 0x14, 0x4, 0x4, 0x4, 0x2,
|
||||
0x4, 0x2, 0x8, 0x4, 0x4},
|
||||
[19] = {0x2, 0x2, 0x4, 0x2},
|
||||
[20] = {0x9, 0x9, 0x5, 0x5, 0x1, 0x1},
|
||||
};
|
||||
|
||||
static const u8 mtk_smi_larb_mt8188_ostd[][SMI_LARB_PORT_NR_MAX] = {
|
||||
[0] = {0x02, 0x18, 0x22, 0x22, 0x01, 0x02, 0x0a,},
|
||||
[1] = {0x12, 0x02, 0x14, 0x14, 0x01, 0x18, 0x0a,},
|
||||
@ -429,6 +466,12 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt6779 = {
|
||||
/* DUMMY | IPU0 | IPU1 | CCU | MDLA */
|
||||
};
|
||||
|
||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt6893 = {
|
||||
.config_port = mtk_smi_larb_config_port_gen2_general,
|
||||
.flags_general = MTK_SMI_FLAG_THRT_UPDATE | MTK_SMI_FLAG_SW_FLAG,
|
||||
.ostd = mtk_smi_larb_mt6893_ostd,
|
||||
};
|
||||
|
||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8167 = {
|
||||
/* mt8167 do not need the port in larb */
|
||||
.config_port = mtk_smi_larb_config_port_mt8167,
|
||||
@ -474,6 +517,7 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
||||
{.compatible = "mediatek,mt2712-smi-larb", .data = &mtk_smi_larb_mt2712},
|
||||
{.compatible = "mediatek,mt6779-smi-larb", .data = &mtk_smi_larb_mt6779},
|
||||
{.compatible = "mediatek,mt6795-smi-larb", .data = &mtk_smi_larb_mt8173},
|
||||
{.compatible = "mediatek,mt6893-smi-larb", .data = &mtk_smi_larb_mt6893},
|
||||
{.compatible = "mediatek,mt8167-smi-larb", .data = &mtk_smi_larb_mt8167},
|
||||
{.compatible = "mediatek,mt8173-smi-larb", .data = &mtk_smi_larb_mt8173},
|
||||
{.compatible = "mediatek,mt8183-smi-larb", .data = &mtk_smi_larb_mt8183},
|
||||
@ -694,6 +738,13 @@ static const struct mtk_smi_common_plat mtk_smi_common_mt6795 = {
|
||||
.init = mtk_smi_common_mt6795_init,
|
||||
};
|
||||
|
||||
static const struct mtk_smi_common_plat mtk_smi_common_mt6893 = {
|
||||
.type = MTK_SMI_GEN2,
|
||||
.has_gals = true,
|
||||
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(4) |
|
||||
F_MMU1_LARB(5) | F_MMU1_LARB(7),
|
||||
};
|
||||
|
||||
static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
|
||||
.type = MTK_SMI_GEN2,
|
||||
.has_gals = true,
|
||||
@ -756,6 +807,7 @@ static const struct of_device_id mtk_smi_common_of_ids[] = {
|
||||
{.compatible = "mediatek,mt2712-smi-common", .data = &mtk_smi_common_gen2},
|
||||
{.compatible = "mediatek,mt6779-smi-common", .data = &mtk_smi_common_mt6779},
|
||||
{.compatible = "mediatek,mt6795-smi-common", .data = &mtk_smi_common_mt6795},
|
||||
{.compatible = "mediatek,mt6893-smi-common", .data = &mtk_smi_common_mt6893},
|
||||
{.compatible = "mediatek,mt8167-smi-common", .data = &mtk_smi_common_gen2},
|
||||
{.compatible = "mediatek,mt8173-smi-common", .data = &mtk_smi_common_gen2},
|
||||
{.compatible = "mediatek,mt8183-smi-common", .data = &mtk_smi_common_mt8183},
|
||||
|
@ -2374,7 +2374,7 @@ static void gpmc_probe_dt_children(struct platform_device *pdev)
|
||||
|
||||
static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return 1; /* we're input only */
|
||||
return GPIO_LINE_DIRECTION_IN; /* we're input only */
|
||||
}
|
||||
|
||||
static int gpmc_gpio_direction_input(struct gpio_chip *chip,
|
||||
@ -2383,17 +2383,6 @@ static int gpmc_gpio_direction_input(struct gpio_chip *chip,
|
||||
return 0; /* we're input only */
|
||||
}
|
||||
|
||||
static int gpmc_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
return -EINVAL; /* we're input only */
|
||||
}
|
||||
|
||||
static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
}
|
||||
|
||||
static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
u32 reg;
|
||||
@ -2415,8 +2404,6 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
|
||||
gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
|
||||
gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
|
||||
gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
|
||||
gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
|
||||
gpmc->gpio_chip.set = gpmc_gpio_set;
|
||||
gpmc->gpio_chip.get = gpmc_gpio_get;
|
||||
gpmc->gpio_chip.base = -1;
|
||||
|
||||
|
479
drivers/memory/stm32_omm.c
Normal file
479
drivers/memory/stm32_omm.c
Normal file
@ -0,0 +1,479 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2025 - All Rights Reserved
|
||||
* Author(s): Patrice Chotard <patrice.chotard@foss.st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bus/stm32_firewall_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define OMM_CR 0
|
||||
#define CR_MUXEN BIT(0)
|
||||
#define CR_MUXENMODE_MASK GENMASK(1, 0)
|
||||
#define CR_CSSEL_OVR_EN BIT(4)
|
||||
#define CR_CSSEL_OVR_MASK GENMASK(6, 5)
|
||||
#define CR_REQ2ACK_MASK GENMASK(23, 16)
|
||||
|
||||
#define OMM_CHILD_NB 2
|
||||
#define OMM_CLK_NB 3
|
||||
|
||||
struct stm32_omm {
|
||||
struct resource *mm_res;
|
||||
struct clk_bulk_data clk_bulk[OMM_CLK_NB];
|
||||
struct reset_control *child_reset[OMM_CHILD_NB];
|
||||
void __iomem *io_base;
|
||||
u32 cr;
|
||||
u8 nb_child;
|
||||
bool restore_omm;
|
||||
};
|
||||
|
||||
static int stm32_omm_set_amcr(struct device *dev, bool set)
|
||||
{
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
resource_size_t mm_ospi2_size = 0;
|
||||
static const char * const mm_name[] = { "ospi1", "ospi2" };
|
||||
struct regmap *syscfg_regmap;
|
||||
struct device_node *node;
|
||||
struct resource res, res1;
|
||||
u32 amcr_base, amcr_mask;
|
||||
int ret, idx;
|
||||
unsigned int i, amcr, read_amcr;
|
||||
|
||||
for (i = 0; i < omm->nb_child; i++) {
|
||||
idx = of_property_match_string(dev->of_node,
|
||||
"memory-region-names",
|
||||
mm_name[i]);
|
||||
if (idx < 0)
|
||||
continue;
|
||||
|
||||
/* res1 only used on second loop iteration */
|
||||
res1.start = res.start;
|
||||
res1.end = res.end;
|
||||
|
||||
node = of_parse_phandle(dev->of_node, "memory-region", idx);
|
||||
if (!node)
|
||||
continue;
|
||||
|
||||
ret = of_address_to_resource(node, 0, &res);
|
||||
if (ret) {
|
||||
of_node_put(node);
|
||||
dev_err(dev, "unable to resolve memory region\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* check that memory region fits inside OMM memory map area */
|
||||
if (!resource_contains(omm->mm_res, &res)) {
|
||||
dev_err(dev, "%s doesn't fit inside OMM memory map area\n",
|
||||
mm_name[i]);
|
||||
dev_err(dev, "%pR doesn't fit inside %pR\n", &res, omm->mm_res);
|
||||
of_node_put(node);
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (i == 1) {
|
||||
mm_ospi2_size = resource_size(&res);
|
||||
|
||||
/* check that OMM memory region 1 doesn't overlap memory region 2 */
|
||||
if (resource_overlaps(&res, &res1)) {
|
||||
dev_err(dev, "OMM memory-region %s overlaps memory region %s\n",
|
||||
mm_name[0], mm_name[1]);
|
||||
dev_err(dev, "%pR overlaps %pR\n", &res1, &res);
|
||||
of_node_put(node);
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
of_node_put(node);
|
||||
}
|
||||
|
||||
syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr");
|
||||
if (IS_ERR(syscfg_regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(syscfg_regmap),
|
||||
"Failed to get st,syscfg-amcr property\n");
|
||||
|
||||
ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1,
|
||||
&amcr_base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2,
|
||||
&amcr_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
amcr = mm_ospi2_size / SZ_64M;
|
||||
|
||||
if (set)
|
||||
regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr);
|
||||
|
||||
/* read AMCR and check coherency with memory-map areas defined in DT */
|
||||
regmap_read(syscfg_regmap, amcr_base, &read_amcr);
|
||||
read_amcr = read_amcr >> (ffs(amcr_mask) - 1);
|
||||
|
||||
if (amcr != read_amcr) {
|
||||
dev_err(dev, "AMCR value not coherent with DT memory-map areas\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_omm_toggle_child_clock(struct device *dev, bool enable)
|
||||
{
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < omm->nb_child; i++) {
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(omm->clk_bulk[i + 1].clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can not enable clock\n");
|
||||
goto clk_error;
|
||||
}
|
||||
} else {
|
||||
clk_disable_unprepare(omm->clk_bulk[i + 1].clk);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clk_error:
|
||||
while (i--)
|
||||
clk_disable_unprepare(omm->clk_bulk[i + 1].clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_omm_disable_child(struct device *dev)
|
||||
{
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
struct reset_control *reset;
|
||||
int ret;
|
||||
u8 i;
|
||||
|
||||
ret = stm32_omm_toggle_child_clock(dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < omm->nb_child; i++) {
|
||||
/* reset OSPI to ensure CR_EN bit is set to 0 */
|
||||
reset = omm->child_reset[i];
|
||||
ret = reset_control_acquire(reset);
|
||||
if (ret) {
|
||||
stm32_omm_toggle_child_clock(dev, false);
|
||||
dev_err(dev, "Can not acquire reset %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reset_control_assert(reset);
|
||||
udelay(2);
|
||||
reset_control_deassert(reset);
|
||||
|
||||
reset_control_release(reset);
|
||||
}
|
||||
|
||||
return stm32_omm_toggle_child_clock(dev, false);
|
||||
}
|
||||
|
||||
static int stm32_omm_configure(struct device *dev)
|
||||
{
|
||||
static const char * const clocks_name[] = {"omm", "ospi1", "ospi2"};
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
unsigned long clk_rate_max = 0;
|
||||
u32 mux = 0;
|
||||
u32 cssel_ovr = 0;
|
||||
u32 req2ack = 0;
|
||||
struct reset_control *rstc;
|
||||
unsigned long clk_rate;
|
||||
int ret;
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < OMM_CLK_NB; i++)
|
||||
omm->clk_bulk[i].id = clocks_name[i];
|
||||
|
||||
/* retrieve OMM, OSPI1 and OSPI2 clocks */
|
||||
ret = devm_clk_bulk_get(dev, OMM_CLK_NB, omm->clk_bulk);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get OMM/OSPI's clocks\n");
|
||||
|
||||
/* Ensure both OSPI instance are disabled before configuring OMM */
|
||||
ret = stm32_omm_disable_child(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* parse children's clock */
|
||||
for (i = 1; i <= omm->nb_child; i++) {
|
||||
clk_rate = clk_get_rate(omm->clk_bulk[i].clk);
|
||||
if (!clk_rate) {
|
||||
dev_err(dev, "Invalid clock rate\n");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (clk_rate > clk_rate_max)
|
||||
clk_rate_max = clk_rate;
|
||||
}
|
||||
|
||||
rstc = devm_reset_control_get_exclusive(dev, "omm");
|
||||
if (IS_ERR(rstc)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
reset_control_assert(rstc);
|
||||
udelay(2);
|
||||
reset_control_deassert(rstc);
|
||||
|
||||
omm->cr = readl_relaxed(omm->io_base + OMM_CR);
|
||||
/* optional */
|
||||
ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux);
|
||||
if (!ret) {
|
||||
if (mux & CR_MUXEN) {
|
||||
ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns",
|
||||
&req2ack);
|
||||
if (!ret && !req2ack) {
|
||||
req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1;
|
||||
|
||||
if (req2ack > 256)
|
||||
req2ack = 256;
|
||||
}
|
||||
|
||||
req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
|
||||
|
||||
omm->cr &= ~CR_REQ2ACK_MASK;
|
||||
omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
|
||||
|
||||
/*
|
||||
* If the mux is enabled, the 2 OSPI clocks have to be
|
||||
* always enabled
|
||||
*/
|
||||
ret = stm32_omm_toggle_child_clock(dev, true);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
|
||||
omm->cr &= ~CR_MUXENMODE_MASK;
|
||||
omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux);
|
||||
}
|
||||
|
||||
/* optional */
|
||||
ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr);
|
||||
if (!ret) {
|
||||
omm->cr &= ~CR_CSSEL_OVR_MASK;
|
||||
omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr);
|
||||
omm->cr |= CR_CSSEL_OVR_EN;
|
||||
}
|
||||
|
||||
omm->restore_omm = true;
|
||||
writel_relaxed(omm->cr, omm->io_base + OMM_CR);
|
||||
|
||||
ret = stm32_omm_set_amcr(dev, true);
|
||||
|
||||
error:
|
||||
pm_runtime_put_sync_suspend(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_omm_check_access(struct device_node *np)
|
||||
{
|
||||
struct stm32_firewall firewall;
|
||||
int ret;
|
||||
|
||||
ret = stm32_firewall_get_firewall(np, &firewall, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return stm32_firewall_grant_access(&firewall);
|
||||
}
|
||||
|
||||
static int stm32_omm_probe(struct platform_device *pdev)
|
||||
{
|
||||
static const char * const resets_name[] = {"ospi1", "ospi2"};
|
||||
struct device *dev = &pdev->dev;
|
||||
u8 child_access_granted = 0;
|
||||
struct stm32_omm *omm;
|
||||
int i, ret;
|
||||
|
||||
omm = devm_kzalloc(dev, sizeof(*omm), GFP_KERNEL);
|
||||
if (!omm)
|
||||
return -ENOMEM;
|
||||
|
||||
omm->io_base = devm_platform_ioremap_resource_byname(pdev, "regs");
|
||||
if (IS_ERR(omm->io_base))
|
||||
return PTR_ERR(omm->io_base);
|
||||
|
||||
omm->mm_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory_map");
|
||||
if (!omm->mm_res)
|
||||
return -ENODEV;
|
||||
|
||||
/* check child's access */
|
||||
for_each_child_of_node_scoped(dev->of_node, child) {
|
||||
if (omm->nb_child >= OMM_CHILD_NB) {
|
||||
dev_err(dev, "Bad DT, found too much children\n");
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
ret = stm32_omm_check_access(child);
|
||||
if (ret < 0 && ret != -EACCES)
|
||||
return ret;
|
||||
|
||||
if (!ret)
|
||||
child_access_granted++;
|
||||
|
||||
omm->nb_child++;
|
||||
}
|
||||
|
||||
if (omm->nb_child != OMM_CHILD_NB)
|
||||
return -EINVAL;
|
||||
|
||||
platform_set_drvdata(pdev, omm);
|
||||
|
||||
devm_pm_runtime_enable(dev);
|
||||
|
||||
/* check if OMM's resource access is granted */
|
||||
ret = stm32_omm_check_access(dev->of_node);
|
||||
if (ret < 0 && ret != -EACCES)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < omm->nb_child; i++) {
|
||||
omm->child_reset[i] = devm_reset_control_get_exclusive_released(dev,
|
||||
resets_name[i]);
|
||||
|
||||
if (IS_ERR(omm->child_reset[i]))
|
||||
return dev_err_probe(dev, PTR_ERR(omm->child_reset[i]),
|
||||
"Can't get %s reset\n", resets_name[i]);
|
||||
}
|
||||
|
||||
if (!ret && child_access_granted == OMM_CHILD_NB) {
|
||||
ret = stm32_omm_configure(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
dev_dbg(dev, "Octo Memory Manager resource's access not granted\n");
|
||||
/*
|
||||
* AMCR can't be set, so check if current value is coherent
|
||||
* with memory-map areas defined in DT
|
||||
*/
|
||||
ret = stm32_omm_set_amcr(dev, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_of_platform_populate(dev);
|
||||
if (ret) {
|
||||
if (omm->cr & CR_MUXEN)
|
||||
stm32_omm_toggle_child_clock(&pdev->dev, false);
|
||||
|
||||
return dev_err_probe(dev, ret, "Failed to create Octo Memory Manager child\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_omm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_omm *omm = platform_get_drvdata(pdev);
|
||||
|
||||
if (omm->cr & CR_MUXEN)
|
||||
stm32_omm_toggle_child_clock(&pdev->dev, false);
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_omm_of_match[] = {
|
||||
{ .compatible = "st,stm32mp25-omm", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_omm_of_match);
|
||||
|
||||
static int __maybe_unused stm32_omm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(omm->clk_bulk[0].clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_omm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
|
||||
return clk_prepare_enable(omm->clk_bulk[0].clk);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_omm_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
|
||||
if (omm->restore_omm && omm->cr & CR_MUXEN)
|
||||
stm32_omm_toggle_child_clock(dev, false);
|
||||
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_omm_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_omm *omm = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
if (!omm->restore_omm)
|
||||
return 0;
|
||||
|
||||
/* Ensure both OSPI instance are disabled before configuring OMM */
|
||||
ret = stm32_omm_disable_child(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
writel_relaxed(omm->cr, omm->io_base + OMM_CR);
|
||||
ret = stm32_omm_set_amcr(dev, true);
|
||||
pm_runtime_put_sync_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (omm->cr & CR_MUXEN)
|
||||
ret = stm32_omm_toggle_child_clock(dev, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops stm32_omm_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(stm32_omm_runtime_suspend,
|
||||
stm32_omm_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(stm32_omm_suspend, stm32_omm_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver stm32_omm_driver = {
|
||||
.probe = stm32_omm_probe,
|
||||
.remove = stm32_omm_remove,
|
||||
.driver = {
|
||||
.name = "stm32-omm",
|
||||
.of_match_table = stm32_omm_of_match,
|
||||
.pm = &stm32_omm_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_omm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("STMicroelectronics Octo Memory Manager driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config TEGRA_MC
|
||||
bool "NVIDIA Tegra Memory Controller support"
|
||||
default y
|
||||
default ARCH_TEGRA
|
||||
depends on ARCH_TEGRA || (COMPILE_TEST && COMMON_CLK)
|
||||
select INTERCONNECT
|
||||
help
|
||||
@ -12,7 +12,7 @@ if TEGRA_MC
|
||||
|
||||
config TEGRA20_EMC
|
||||
tristate "NVIDIA Tegra20 External Memory Controller driver"
|
||||
default y
|
||||
default ARCH_TEGRA_2x_SOC
|
||||
depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select PM_DEVFREQ
|
||||
@ -25,7 +25,7 @@ config TEGRA20_EMC
|
||||
|
||||
config TEGRA30_EMC
|
||||
tristate "NVIDIA Tegra30 External Memory Controller driver"
|
||||
default y
|
||||
default ARCH_TEGRA_3x_SOC
|
||||
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
|
||||
select PM_OPP
|
||||
select DDR
|
||||
@ -37,7 +37,7 @@ config TEGRA30_EMC
|
||||
|
||||
config TEGRA124_EMC
|
||||
tristate "NVIDIA Tegra124 External Memory Controller driver"
|
||||
default y
|
||||
default ARCH_TEGRA_124_SOC
|
||||
depends on ARCH_TEGRA_124_SOC || COMPILE_TEST
|
||||
select TEGRA124_CLK_EMC if ARCH_TEGRA
|
||||
select PM_OPP
|
||||
|
@ -76,6 +76,23 @@ config TURRIS_OMNIA_MCU_TRNG
|
||||
Say Y here to add support for the true random number generator
|
||||
provided by CZ.NIC's Turris Omnia MCU.
|
||||
|
||||
config TURRIS_OMNIA_MCU_KEYCTL
|
||||
bool "Turris Omnia MCU ECDSA message signing"
|
||||
default y
|
||||
depends on KEYS
|
||||
depends on ASYMMETRIC_KEY_TYPE
|
||||
depends on TURRIS_OMNIA_MCU_GPIO
|
||||
select TURRIS_SIGNING_KEY
|
||||
help
|
||||
Say Y here to add support for ECDSA message signing with board private
|
||||
key (if available on the MCU). This is exposed via the keyctl()
|
||||
syscall.
|
||||
|
||||
endif # TURRIS_OMNIA_MCU
|
||||
|
||||
config TURRIS_SIGNING_KEY
|
||||
tristate
|
||||
depends on KEYS
|
||||
depends on ASYMMETRIC_KEY_TYPE
|
||||
|
||||
endif # CZNIC_PLATFORMS
|
||||
|
@ -3,6 +3,9 @@
|
||||
obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
|
||||
turris-omnia-mcu-y := turris-omnia-mcu-base.o
|
||||
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_GPIO) += turris-omnia-mcu-gpio.o
|
||||
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_KEYCTL) += turris-omnia-mcu-keyctl.o
|
||||
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP) += turris-omnia-mcu-sys-off-wakeup.o
|
||||
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_TRNG) += turris-omnia-mcu-trng.o
|
||||
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_WATCHDOG) += turris-omnia-mcu-watchdog.o
|
||||
|
||||
obj-$(CONFIG_TURRIS_SIGNING_KEY) += turris-signing-key.o
|
||||
|
@ -392,6 +392,10 @@ static int omnia_mcu_probe(struct i2c_client *client)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = omnia_mcu_register_keyctl(mcu);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return omnia_mcu_register_trng(mcu);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -195,7 +196,7 @@ static const struct omnia_gpio omnia_gpios[64] = {
|
||||
};
|
||||
|
||||
/* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */
|
||||
const u8 omnia_int_to_gpio_idx[32] = {
|
||||
static const u8 omnia_int_to_gpio_idx[32] = {
|
||||
[__bf_shf(OMNIA_INT_CARD_DET)] = 4,
|
||||
[__bf_shf(OMNIA_INT_MSATA_IND)] = 5,
|
||||
[__bf_shf(OMNIA_INT_USB30_OVC)] = 6,
|
||||
@ -1093,3 +1094,21 @@ int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omnia_mcu_request_irq(struct omnia_mcu *mcu, u32 spec,
|
||||
irq_handler_t thread_fn, const char *devname)
|
||||
{
|
||||
u8 irq_idx;
|
||||
int irq;
|
||||
|
||||
if (!spec)
|
||||
return -EINVAL;
|
||||
|
||||
irq_idx = omnia_int_to_gpio_idx[ffs(spec) - 1];
|
||||
irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx));
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
return devm_request_threaded_irq(&mcu->client->dev, irq, NULL,
|
||||
thread_fn, IRQF_ONESHOT, devname, mcu);
|
||||
}
|
||||
|
162
drivers/platform/cznic/turris-omnia-mcu-keyctl.c
Normal file
162
drivers/platform/cznic/turris-omnia-mcu-keyctl.c
Normal file
@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* CZ.NIC's Turris Omnia MCU ECDSA message signing via keyctl
|
||||
*
|
||||
* 2025 by Marek Behún <kabel@kernel.org>
|
||||
*/
|
||||
|
||||
#include <crypto/sha2.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/turris-omnia-mcu-interface.h>
|
||||
#include <linux/turris-signing-key.h>
|
||||
#include "turris-omnia-mcu.h"
|
||||
|
||||
static irqreturn_t omnia_msg_signed_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
u8 reply[1 + OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
|
||||
struct omnia_mcu *mcu = dev_id;
|
||||
int err;
|
||||
|
||||
err = omnia_cmd_read(mcu->client, OMNIA_CMD_CRYPTO_COLLECT_SIGNATURE,
|
||||
reply, sizeof(reply));
|
||||
if (!err && reply[0] != OMNIA_MCU_CRYPTO_SIGNATURE_LEN)
|
||||
err = -EIO;
|
||||
|
||||
guard(mutex)(&mcu->sign_lock);
|
||||
|
||||
if (mcu->sign_requested) {
|
||||
mcu->sign_err = err;
|
||||
if (!err)
|
||||
memcpy(mcu->signature, &reply[1],
|
||||
OMNIA_MCU_CRYPTO_SIGNATURE_LEN);
|
||||
mcu->sign_requested = false;
|
||||
complete(&mcu->msg_signed);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int omnia_mcu_sign(const struct key *key, const void *msg,
|
||||
void *signature)
|
||||
{
|
||||
struct omnia_mcu *mcu = dev_get_drvdata(turris_signing_key_get_dev(key));
|
||||
u8 cmd[1 + SHA256_DIGEST_SIZE], reply;
|
||||
int err;
|
||||
|
||||
scoped_guard(mutex, &mcu->sign_lock) {
|
||||
if (mcu->sign_requested)
|
||||
return -EBUSY;
|
||||
|
||||
cmd[0] = OMNIA_CMD_CRYPTO_SIGN_MESSAGE;
|
||||
memcpy(&cmd[1], msg, SHA256_DIGEST_SIZE);
|
||||
|
||||
err = omnia_cmd_write_read(mcu->client, cmd, sizeof(cmd),
|
||||
&reply, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!reply)
|
||||
return -EBUSY;
|
||||
|
||||
mcu->sign_requested = true;
|
||||
}
|
||||
|
||||
if (wait_for_completion_interruptible(&mcu->msg_signed))
|
||||
return -EINTR;
|
||||
|
||||
guard(mutex)(&mcu->sign_lock);
|
||||
|
||||
if (mcu->sign_err)
|
||||
return mcu->sign_err;
|
||||
|
||||
memcpy(signature, mcu->signature, OMNIA_MCU_CRYPTO_SIGNATURE_LEN);
|
||||
|
||||
/* forget the signature, for security */
|
||||
memzero_explicit(mcu->signature, sizeof(mcu->signature));
|
||||
|
||||
return OMNIA_MCU_CRYPTO_SIGNATURE_LEN;
|
||||
}
|
||||
|
||||
static const void *omnia_mcu_get_public_key(const struct key *key)
|
||||
{
|
||||
struct omnia_mcu *mcu = dev_get_drvdata(turris_signing_key_get_dev(key));
|
||||
|
||||
return mcu->board_public_key;
|
||||
}
|
||||
|
||||
static const struct turris_signing_key_subtype omnia_signing_key_subtype = {
|
||||
.key_size = 256,
|
||||
.data_size = SHA256_DIGEST_SIZE,
|
||||
.sig_size = OMNIA_MCU_CRYPTO_SIGNATURE_LEN,
|
||||
.public_key_size = OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN,
|
||||
.hash_algo = "sha256",
|
||||
.get_public_key = omnia_mcu_get_public_key,
|
||||
.sign = omnia_mcu_sign,
|
||||
};
|
||||
|
||||
static int omnia_mcu_read_public_key(struct omnia_mcu *mcu)
|
||||
{
|
||||
u8 reply[1 + OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];
|
||||
int err;
|
||||
|
||||
err = omnia_cmd_read(mcu->client, OMNIA_CMD_CRYPTO_GET_PUBLIC_KEY,
|
||||
reply, sizeof(reply));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (reply[0] != OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN)
|
||||
return -EIO;
|
||||
|
||||
memcpy(mcu->board_public_key, &reply[1],
|
||||
OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omnia_mcu_register_keyctl(struct omnia_mcu *mcu)
|
||||
{
|
||||
struct device *dev = &mcu->client->dev;
|
||||
char desc[48];
|
||||
int err;
|
||||
|
||||
if (!(mcu->features & OMNIA_FEAT_CRYPTO))
|
||||
return 0;
|
||||
|
||||
err = omnia_mcu_read_public_key(mcu);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"Cannot read board public key\n");
|
||||
|
||||
err = devm_mutex_init(dev, &mcu->sign_lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
init_completion(&mcu->msg_signed);
|
||||
|
||||
err = omnia_mcu_request_irq(mcu, OMNIA_INT_MESSAGE_SIGNED,
|
||||
omnia_msg_signed_irq_handler,
|
||||
"turris-omnia-mcu-keyctl");
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"Cannot request MESSAGE_SIGNED IRQ\n");
|
||||
|
||||
sprintf(desc, "Turris Omnia SN %016llX MCU ECDSA key",
|
||||
mcu->board_serial_number);
|
||||
|
||||
err = devm_turris_signing_key_create(dev, &omnia_signing_key_subtype,
|
||||
desc);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "Cannot create signing key\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -5,12 +5,9 @@
|
||||
* 2024 by Marek Behún <kabel@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -62,17 +59,12 @@ static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
||||
int omnia_mcu_register_trng(struct omnia_mcu *mcu)
|
||||
{
|
||||
struct device *dev = &mcu->client->dev;
|
||||
u8 irq_idx, dummy;
|
||||
int irq, err;
|
||||
u8 dummy;
|
||||
int err;
|
||||
|
||||
if (!(mcu->features & OMNIA_FEAT_TRNG))
|
||||
return 0;
|
||||
|
||||
irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)];
|
||||
irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx));
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, irq, "Cannot get TRNG IRQ\n");
|
||||
|
||||
/*
|
||||
* If someone else cleared the TRNG interrupt but did not read the
|
||||
* entropy, a new interrupt won't be generated, and entropy collection
|
||||
@ -86,9 +78,8 @@ int omnia_mcu_register_trng(struct omnia_mcu *mcu)
|
||||
|
||||
init_completion(&mcu->trng_entropy_ready);
|
||||
|
||||
err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler,
|
||||
IRQF_ONESHOT, "turris-omnia-mcu-trng",
|
||||
mcu);
|
||||
err = omnia_mcu_request_irq(mcu, OMNIA_INT_TRNG, omnia_trng_irq_handler,
|
||||
"turris-omnia-mcu-trng");
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n");
|
||||
|
||||
|
@ -12,11 +12,17 @@
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
enum {
|
||||
OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN = 1 + 32,
|
||||
OMNIA_MCU_CRYPTO_SIGNATURE_LEN = 64,
|
||||
};
|
||||
|
||||
struct i2c_client;
|
||||
struct rtc_device;
|
||||
|
||||
@ -55,6 +61,12 @@ struct rtc_device;
|
||||
* @wdt: watchdog driver structure
|
||||
* @trng: RNG driver structure
|
||||
* @trng_entropy_ready: RNG entropy ready completion
|
||||
* @msg_signed: message signed completion
|
||||
* @sign_lock: mutex to protect message signing state
|
||||
* @sign_requested: flag indicating that message signing was requested but not completed
|
||||
* @sign_err: message signing error number, filled in interrupt handler
|
||||
* @signature: message signing signature, filled in interrupt handler
|
||||
* @board_public_key: board public key, if stored in MCU
|
||||
*/
|
||||
struct omnia_mcu {
|
||||
struct i2c_client *client;
|
||||
@ -88,12 +100,22 @@ struct omnia_mcu {
|
||||
struct hwrng trng;
|
||||
struct completion trng_entropy_ready;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TURRIS_OMNIA_MCU_KEYCTL
|
||||
struct completion msg_signed;
|
||||
struct mutex sign_lock;
|
||||
bool sign_requested;
|
||||
int sign_err;
|
||||
u8 signature[OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
|
||||
u8 board_public_key[OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
|
||||
extern const u8 omnia_int_to_gpio_idx[32];
|
||||
extern const struct attribute_group omnia_mcu_gpio_group;
|
||||
int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
|
||||
int omnia_mcu_request_irq(struct omnia_mcu *mcu, u32 spec,
|
||||
irq_handler_t thread_fn, const char *devname);
|
||||
#else
|
||||
static inline int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu)
|
||||
{
|
||||
@ -101,6 +123,15 @@ static inline int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TURRIS_OMNIA_MCU_KEYCTL
|
||||
int omnia_mcu_register_keyctl(struct omnia_mcu *mcu);
|
||||
#else
|
||||
static inline int omnia_mcu_register_keyctl(struct omnia_mcu *mcu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
|
||||
extern const struct attribute_group omnia_mcu_poweroff_group;
|
||||
int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
|
||||
|
193
drivers/platform/cznic/turris-signing-key.c
Normal file
193
drivers/platform/cznic/turris-signing-key.c
Normal file
@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Some of CZ.NIC's Turris devices support signing messages with a per-device unique asymmetric
|
||||
* cryptographic key that was burned into the device at manufacture.
|
||||
*
|
||||
* This helper module exposes this message signing ability via the keyctl() syscall. Upon load, it
|
||||
* creates the `.turris-signing-keys` keyring. A device-specific driver then has to create a signing
|
||||
* key by calling devm_turris_signing_key_create().
|
||||
*
|
||||
* 2025 by Marek Behún <kabel@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/turris-signing-key.h>
|
||||
|
||||
static int turris_signing_key_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *payload)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void turris_signing_key_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
|
||||
|
||||
if (!subtype)
|
||||
return;
|
||||
|
||||
seq_printf(m, "%s: %*phN", key->description, subtype->public_key_size,
|
||||
subtype->get_public_key(key));
|
||||
}
|
||||
|
||||
static long turris_signing_key_read(const struct key *key, char *buffer, size_t buflen)
|
||||
{
|
||||
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
|
||||
|
||||
if (!subtype)
|
||||
return -EIO;
|
||||
|
||||
if (buffer) {
|
||||
if (buflen > subtype->public_key_size)
|
||||
buflen = subtype->public_key_size;
|
||||
|
||||
memcpy(buffer, subtype->get_public_key(key), subtype->public_key_size);
|
||||
}
|
||||
|
||||
return subtype->public_key_size;
|
||||
}
|
||||
|
||||
static bool turris_signing_key_asym_valid_params(const struct turris_signing_key_subtype *subtype,
|
||||
const struct kernel_pkey_params *params)
|
||||
{
|
||||
if (params->encoding && strcmp(params->encoding, "raw"))
|
||||
return false;
|
||||
|
||||
if (params->hash_algo && strcmp(params->hash_algo, subtype->hash_algo))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int turris_signing_key_asym_query(const struct kernel_pkey_params *params,
|
||||
struct kernel_pkey_query *info)
|
||||
{
|
||||
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
|
||||
|
||||
if (!subtype)
|
||||
return -EIO;
|
||||
|
||||
if (!turris_signing_key_asym_valid_params(subtype, params))
|
||||
return -EINVAL;
|
||||
|
||||
info->supported_ops = KEYCTL_SUPPORTS_SIGN;
|
||||
info->key_size = subtype->key_size;
|
||||
info->max_data_size = subtype->data_size;
|
||||
info->max_sig_size = subtype->sig_size;
|
||||
info->max_enc_size = 0;
|
||||
info->max_dec_size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int turris_signing_key_asym_eds_op(struct kernel_pkey_params *params,
|
||||
const void *in, void *out)
|
||||
{
|
||||
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
|
||||
int err;
|
||||
|
||||
if (!subtype)
|
||||
return -EIO;
|
||||
|
||||
if (!turris_signing_key_asym_valid_params(subtype, params))
|
||||
return -EINVAL;
|
||||
|
||||
if (params->op != kernel_pkey_sign)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (params->in_len != subtype->data_size || params->out_len != subtype->sig_size)
|
||||
return -EINVAL;
|
||||
|
||||
err = subtype->sign(params->key, in, out);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return subtype->sig_size;
|
||||
}
|
||||
|
||||
static struct key_type turris_signing_key_type = {
|
||||
.name = "turris-signing-key",
|
||||
.instantiate = turris_signing_key_instantiate,
|
||||
.describe = turris_signing_key_describe,
|
||||
.read = turris_signing_key_read,
|
||||
.asym_query = turris_signing_key_asym_query,
|
||||
.asym_eds_op = turris_signing_key_asym_eds_op,
|
||||
};
|
||||
|
||||
static struct key *turris_signing_keyring;
|
||||
|
||||
static void turris_signing_key_release(void *key)
|
||||
{
|
||||
key_unlink(turris_signing_keyring, key);
|
||||
key_put(key);
|
||||
}
|
||||
|
||||
int
|
||||
devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype,
|
||||
const char *desc)
|
||||
{
|
||||
struct key *key;
|
||||
key_ref_t kref;
|
||||
|
||||
kref = key_create(make_key_ref(turris_signing_keyring, true),
|
||||
turris_signing_key_type.name, desc, NULL, 0,
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_SEARCH,
|
||||
KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | KEY_ALLOC_NOT_IN_QUOTA);
|
||||
if (IS_ERR(kref))
|
||||
return PTR_ERR(kref);
|
||||
|
||||
key = key_ref_to_ptr(kref);
|
||||
key->payload.data[1] = dev;
|
||||
rcu_assign_keypointer(key, subtype);
|
||||
|
||||
return devm_add_action_or_reset(dev, turris_signing_key_release, key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_turris_signing_key_create);
|
||||
|
||||
static int turris_signing_key_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = register_key_type(&turris_signing_key_type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
turris_signing_keyring = keyring_alloc(".turris-signing-keys",
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW |
|
||||
KEY_USR_READ | KEY_USR_SEARCH,
|
||||
KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP |
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(turris_signing_keyring)) {
|
||||
pr_err("Cannot allocate Turris keyring\n");
|
||||
|
||||
unregister_key_type(&turris_signing_key_type);
|
||||
|
||||
return PTR_ERR(turris_signing_keyring);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(turris_signing_key_init);
|
||||
|
||||
static void turris_signing_key_exit(void)
|
||||
{
|
||||
key_put(turris_signing_keyring);
|
||||
unregister_key_type(&turris_signing_key_type);
|
||||
}
|
||||
module_exit(turris_signing_key_exit);
|
||||
|
||||
MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
|
||||
MODULE_DESCRIPTION("CZ.NIC's Turris signing key helper");
|
||||
MODULE_LICENSE("GPL");
|
@ -225,6 +225,13 @@ config RESET_RZG2L_USBPHY_CTRL
|
||||
Support for USBPHY Control found on RZ/G2L family. It mainly
|
||||
controls reset and power down of the USB/PHY.
|
||||
|
||||
config RESET_RZV2H_USB2PHY
|
||||
tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
|
||||
(and similar SoCs).
|
||||
|
||||
config RESET_SCMI
|
||||
tristate "Reset driver controlled via ARM SCMI interface"
|
||||
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
|
||||
@ -279,6 +286,16 @@ config RESET_SUNXI
|
||||
help
|
||||
This enables the reset driver for Allwinner SoCs.
|
||||
|
||||
config RESET_TH1520
|
||||
tristate "T-HEAD 1520 reset controller"
|
||||
depends on ARCH_THEAD || COMPILE_TEST
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This driver provides support for the T-HEAD TH1520 SoC reset controller,
|
||||
which manages hardware reset lines for SoC components such as the GPU.
|
||||
Enable this option if you need to control hardware resets on TH1520-based
|
||||
systems.
|
||||
|
||||
config RESET_TI_SCI
|
||||
tristate "TI System Control Interface (TI-SCI) reset driver"
|
||||
depends on TI_SCI_PROTOCOL || (COMPILE_TEST && TI_SCI_PROTOCOL=n)
|
||||
|
@ -31,11 +31,13 @@ obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
|
||||
obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
|
||||
obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
|
||||
obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
|
||||
obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
|
||||
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
|
||||
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
|
||||
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
|
||||
obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
|
||||
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
|
||||
obj-$(CONFIG_RESET_TH1520) += reset-th1520.o
|
||||
obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
|
||||
obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
|
||||
obj-$(CONFIG_RESET_TI_TPS380X) += reset-tps380x.o
|
||||
|
236
drivers/reset/reset-rzv2h-usb2phy.c
Normal file
236
drivers/reset/reset-rzv2h-usb2phy.c
Normal file
@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas RZ/V2H(P) USB2PHY Port reset control driver
|
||||
*
|
||||
* Copyright (C) 2025 Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
struct rzv2h_usb2phy_regval {
|
||||
u16 reg;
|
||||
u16 val;
|
||||
};
|
||||
|
||||
struct rzv2h_usb2phy_reset_of_data {
|
||||
const struct rzv2h_usb2phy_regval *init_vals;
|
||||
unsigned int init_val_count;
|
||||
|
||||
u16 reset_reg;
|
||||
u16 reset_assert_val;
|
||||
u16 reset_deassert_val;
|
||||
u16 reset_status_bits;
|
||||
u16 reset_release_val;
|
||||
|
||||
u16 reset2_reg;
|
||||
u16 reset2_acquire_val;
|
||||
u16 reset2_release_val;
|
||||
};
|
||||
|
||||
struct rzv2h_usb2phy_reset_priv {
|
||||
const struct rzv2h_usb2phy_reset_of_data *data;
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct reset_controller_dev rcdev;
|
||||
spinlock_t lock; /* protects register accesses */
|
||||
};
|
||||
|
||||
static inline struct rzv2h_usb2phy_reset_priv
|
||||
*rzv2h_usbphy_rcdev_to_priv(struct reset_controller_dev *rcdev)
|
||||
{
|
||||
return container_of(rcdev, struct rzv2h_usb2phy_reset_priv, rcdev);
|
||||
}
|
||||
|
||||
/* This function must be called only after pm_runtime_resume_and_get() has been called */
|
||||
static void rzv2h_usbphy_assert_helper(struct rzv2h_usb2phy_reset_priv *priv)
|
||||
{
|
||||
const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
|
||||
|
||||
scoped_guard(spinlock, &priv->lock) {
|
||||
writel(data->reset2_acquire_val, priv->base + data->reset2_reg);
|
||||
writel(data->reset_assert_val, priv->base + data->reset_reg);
|
||||
}
|
||||
|
||||
usleep_range(11, 20);
|
||||
}
|
||||
|
||||
static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "pm_runtime_resume_and_get failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rzv2h_usbphy_assert_helper(priv);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
|
||||
const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "pm_runtime_resume_and_get failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
scoped_guard(spinlock, &priv->lock) {
|
||||
writel(data->reset_deassert_val, priv->base + data->reset_reg);
|
||||
writel(data->reset2_release_val, priv->base + data->reset2_reg);
|
||||
writel(data->reset_release_val, priv->base + data->reset_reg);
|
||||
}
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "pm_runtime_resume_and_get failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = readl(priv->base + priv->data->reset_reg);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits;
|
||||
}
|
||||
|
||||
static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
|
||||
.assert = rzv2h_usbphy_reset_assert,
|
||||
.deassert = rzv2h_usbphy_reset_deassert,
|
||||
.status = rzv2h_usbphy_reset_status,
|
||||
};
|
||||
|
||||
static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
|
||||
const struct of_phandle_args *reset_spec)
|
||||
{
|
||||
/* No special handling needed, we have only one reset line per device */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct rzv2h_usb2phy_reset_of_data *data;
|
||||
struct rzv2h_usb2phy_reset_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct reset_control *rstc;
|
||||
int error;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
data = of_device_get_match_data(dev);
|
||||
priv->data = data;
|
||||
priv->dev = dev;
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
|
||||
if (IS_ERR(rstc))
|
||||
return dev_err_probe(dev, PTR_ERR(rstc),
|
||||
"failed to get deasserted reset\n");
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
error = devm_pm_runtime_enable(dev);
|
||||
if (error)
|
||||
return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
|
||||
|
||||
error = pm_runtime_resume_and_get(dev);
|
||||
if (error)
|
||||
return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n");
|
||||
|
||||
for (unsigned int i = 0; i < data->init_val_count; i++)
|
||||
writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
|
||||
|
||||
/* keep usb2phy in asserted state */
|
||||
rzv2h_usbphy_assert_helper(priv);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
|
||||
priv->rcdev.of_reset_n_cells = 0;
|
||||
priv->rcdev.nr_resets = 1;
|
||||
priv->rcdev.of_xlate = rzv2h_usb2phy_reset_of_xlate;
|
||||
priv->rcdev.of_node = dev->of_node;
|
||||
priv->rcdev.dev = dev;
|
||||
|
||||
return devm_reset_controller_register(dev, &priv->rcdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialization values required to prepare the PHY to receive
|
||||
* assert and deassert requests.
|
||||
*/
|
||||
static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = {
|
||||
{ .reg = 0xc10, .val = 0x67c },
|
||||
{ .reg = 0xc14, .val = 0x1f },
|
||||
{ .reg = 0x600, .val = 0x909 },
|
||||
};
|
||||
|
||||
static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = {
|
||||
.init_vals = rzv2h_init_vals,
|
||||
.init_val_count = ARRAY_SIZE(rzv2h_init_vals),
|
||||
.reset_reg = 0,
|
||||
.reset_assert_val = 0x206,
|
||||
.reset_status_bits = BIT(2),
|
||||
.reset_deassert_val = 0x200,
|
||||
.reset_release_val = 0x0,
|
||||
.reset2_reg = 0xb04,
|
||||
.reset2_acquire_val = 0x303,
|
||||
.reset2_release_val = 0x3,
|
||||
};
|
||||
|
||||
static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = {
|
||||
{ .compatible = "renesas,r9a09g057-usb2phy-reset", .data = &rzv2h_reset_of_data },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rzv2h_usb2phy_reset_of_match);
|
||||
|
||||
static struct platform_driver rzv2h_usb2phy_reset_driver = {
|
||||
.driver = {
|
||||
.name = "rzv2h_usb2phy_reset",
|
||||
.of_match_table = rzv2h_usb2phy_reset_of_match,
|
||||
},
|
||||
.probe = rzv2h_usb2phy_reset_probe,
|
||||
};
|
||||
module_platform_driver(rzv2h_usb2phy_reset_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
|
||||
MODULE_DESCRIPTION("Renesas RZ/V2H(P) USB2PHY Control");
|
135
drivers/reset/reset-th1520.c
Normal file
135
drivers/reset/reset-th1520.c
Normal file
@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2024 Samsung Electronics Co., Ltd.
|
||||
* Author: Michal Wilczynski <m.wilczynski@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/reset/thead,th1520-reset.h>
|
||||
|
||||
/* register offset in VOSYS_REGMAP */
|
||||
#define TH1520_GPU_RST_CFG 0x0
|
||||
#define TH1520_GPU_RST_CFG_MASK GENMASK(1, 0)
|
||||
|
||||
/* register values */
|
||||
#define TH1520_GPU_SW_GPU_RST BIT(0)
|
||||
#define TH1520_GPU_SW_CLKGEN_RST BIT(1)
|
||||
|
||||
struct th1520_reset_priv {
|
||||
struct reset_controller_dev rcdev;
|
||||
struct regmap *map;
|
||||
};
|
||||
|
||||
struct th1520_reset_map {
|
||||
u32 bit;
|
||||
u32 reg;
|
||||
};
|
||||
|
||||
static const struct th1520_reset_map th1520_resets[] = {
|
||||
[TH1520_RESET_ID_GPU] = {
|
||||
.bit = TH1520_GPU_SW_GPU_RST,
|
||||
.reg = TH1520_GPU_RST_CFG,
|
||||
},
|
||||
[TH1520_RESET_ID_GPU_CLKGEN] = {
|
||||
.bit = TH1520_GPU_SW_CLKGEN_RST,
|
||||
.reg = TH1520_GPU_RST_CFG,
|
||||
}
|
||||
};
|
||||
|
||||
static inline struct th1520_reset_priv *
|
||||
to_th1520_reset(struct reset_controller_dev *rcdev)
|
||||
{
|
||||
return container_of(rcdev, struct th1520_reset_priv, rcdev);
|
||||
}
|
||||
|
||||
static int th1520_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct th1520_reset_priv *priv = to_th1520_reset(rcdev);
|
||||
const struct th1520_reset_map *reset;
|
||||
|
||||
reset = &th1520_resets[id];
|
||||
|
||||
return regmap_update_bits(priv->map, reset->reg, reset->bit, 0);
|
||||
}
|
||||
|
||||
static int th1520_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct th1520_reset_priv *priv = to_th1520_reset(rcdev);
|
||||
const struct th1520_reset_map *reset;
|
||||
|
||||
reset = &th1520_resets[id];
|
||||
|
||||
return regmap_update_bits(priv->map, reset->reg, reset->bit,
|
||||
reset->bit);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops th1520_reset_ops = {
|
||||
.assert = th1520_reset_assert,
|
||||
.deassert = th1520_reset_deassert,
|
||||
};
|
||||
|
||||
static const struct regmap_config th1520_reset_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static int th1520_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct th1520_reset_priv *priv;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->map = devm_regmap_init_mmio(dev, base,
|
||||
&th1520_reset_regmap_config);
|
||||
if (IS_ERR(priv->map))
|
||||
return PTR_ERR(priv->map);
|
||||
|
||||
/* Initialize GPU resets to asserted state */
|
||||
ret = regmap_update_bits(priv->map, TH1520_GPU_RST_CFG,
|
||||
TH1520_GPU_RST_CFG_MASK, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->rcdev.owner = THIS_MODULE;
|
||||
priv->rcdev.nr_resets = ARRAY_SIZE(th1520_resets);
|
||||
priv->rcdev.ops = &th1520_reset_ops;
|
||||
priv->rcdev.of_node = dev->of_node;
|
||||
|
||||
return devm_reset_controller_register(dev, &priv->rcdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id th1520_reset_match[] = {
|
||||
{ .compatible = "thead,th1520-reset" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, th1520_reset_match);
|
||||
|
||||
static struct platform_driver th1520_reset_driver = {
|
||||
.driver = {
|
||||
.name = "th1520-reset",
|
||||
.of_match_table = th1520_reset_match,
|
||||
},
|
||||
.probe = th1520_reset_probe,
|
||||
};
|
||||
module_platform_driver(th1520_reset_driver);
|
||||
|
||||
MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@samsung.com>");
|
||||
MODULE_DESCRIPTION("T-HEAD TH1520 SoC reset controller");
|
||||
MODULE_LICENSE("GPL");
|
@ -23,11 +23,13 @@ source "drivers/soc/qcom/Kconfig"
|
||||
source "drivers/soc/renesas/Kconfig"
|
||||
source "drivers/soc/rockchip/Kconfig"
|
||||
source "drivers/soc/samsung/Kconfig"
|
||||
source "drivers/soc/sophgo/Kconfig"
|
||||
source "drivers/soc/sunxi/Kconfig"
|
||||
source "drivers/soc/tegra/Kconfig"
|
||||
source "drivers/soc/ti/Kconfig"
|
||||
source "drivers/soc/ux500/Kconfig"
|
||||
source "drivers/soc/versatile/Kconfig"
|
||||
source "drivers/soc/vt8500/Kconfig"
|
||||
source "drivers/soc/xilinx/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -29,9 +29,11 @@ obj-y += qcom/
|
||||
obj-y += renesas/
|
||||
obj-y += rockchip/
|
||||
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
|
||||
obj-y += sophgo/
|
||||
obj-y += sunxi/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-y += ti/
|
||||
obj-$(CONFIG_ARCH_U8500) += ux500/
|
||||
obj-y += versatile/
|
||||
obj-y += vt8500/
|
||||
obj-y += xilinx/
|
||||
|
@ -14,11 +14,6 @@
|
||||
|
||||
static DEFINE_MUTEX(measure_lock);
|
||||
|
||||
#define MSR_CLK_DUTY 0x0
|
||||
#define MSR_CLK_REG0 0x4
|
||||
#define MSR_CLK_REG1 0x8
|
||||
#define MSR_CLK_REG2 0xc
|
||||
|
||||
#define MSR_DURATION GENMASK(15, 0)
|
||||
#define MSR_ENABLE BIT(16)
|
||||
#define MSR_CONT BIT(17) /* continuous measurement */
|
||||
@ -33,23 +28,34 @@ static DEFINE_MUTEX(measure_lock);
|
||||
#define DIV_STEP 32
|
||||
#define DIV_MAX 640
|
||||
|
||||
#define CLK_MSR_MAX 128
|
||||
|
||||
struct meson_msr_id {
|
||||
struct meson_msr *priv;
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct msr_reg_offset {
|
||||
unsigned int duty_val;
|
||||
unsigned int freq_ctrl;
|
||||
unsigned int duty_ctrl;
|
||||
unsigned int freq_val;
|
||||
};
|
||||
|
||||
struct meson_msr_data {
|
||||
struct meson_msr_id *msr_table;
|
||||
unsigned int msr_count;
|
||||
const struct msr_reg_offset *reg;
|
||||
};
|
||||
|
||||
struct meson_msr {
|
||||
struct regmap *regmap;
|
||||
struct meson_msr_id msr_table[CLK_MSR_MAX];
|
||||
struct meson_msr_data data;
|
||||
};
|
||||
|
||||
#define CLK_MSR_ID(__id, __name) \
|
||||
[__id] = {.id = __id, .name = __name,}
|
||||
|
||||
static struct meson_msr_id clk_msr_m8[CLK_MSR_MAX] = {
|
||||
static const struct meson_msr_id clk_msr_m8[] = {
|
||||
CLK_MSR_ID(0, "ring_osc_out_ee0"),
|
||||
CLK_MSR_ID(1, "ring_osc_out_ee1"),
|
||||
CLK_MSR_ID(2, "ring_osc_out_ee2"),
|
||||
@ -98,7 +104,7 @@ static struct meson_msr_id clk_msr_m8[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(63, "mipi_csi_cfg"),
|
||||
};
|
||||
|
||||
static struct meson_msr_id clk_msr_gx[CLK_MSR_MAX] = {
|
||||
static const struct meson_msr_id clk_msr_gx[] = {
|
||||
CLK_MSR_ID(0, "ring_osc_out_ee_0"),
|
||||
CLK_MSR_ID(1, "ring_osc_out_ee_1"),
|
||||
CLK_MSR_ID(2, "ring_osc_out_ee_2"),
|
||||
@ -168,7 +174,7 @@ static struct meson_msr_id clk_msr_gx[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(82, "ge2d"),
|
||||
};
|
||||
|
||||
static struct meson_msr_id clk_msr_axg[CLK_MSR_MAX] = {
|
||||
static const struct meson_msr_id clk_msr_axg[] = {
|
||||
CLK_MSR_ID(0, "ring_osc_out_ee_0"),
|
||||
CLK_MSR_ID(1, "ring_osc_out_ee_1"),
|
||||
CLK_MSR_ID(2, "ring_osc_out_ee_2"),
|
||||
@ -242,7 +248,7 @@ static struct meson_msr_id clk_msr_axg[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(109, "audio_locker_in"),
|
||||
};
|
||||
|
||||
static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = {
|
||||
static const struct meson_msr_id clk_msr_g12a[] = {
|
||||
CLK_MSR_ID(0, "ring_osc_out_ee_0"),
|
||||
CLK_MSR_ID(1, "ring_osc_out_ee_1"),
|
||||
CLK_MSR_ID(2, "ring_osc_out_ee_2"),
|
||||
@ -358,7 +364,7 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(122, "audio_pdm_dclk"),
|
||||
};
|
||||
|
||||
static struct meson_msr_id clk_msr_sm1[CLK_MSR_MAX] = {
|
||||
static const struct meson_msr_id clk_msr_sm1[] = {
|
||||
CLK_MSR_ID(0, "ring_osc_out_ee_0"),
|
||||
CLK_MSR_ID(1, "ring_osc_out_ee_1"),
|
||||
CLK_MSR_ID(2, "ring_osc_out_ee_2"),
|
||||
@ -488,10 +494,304 @@ static struct meson_msr_id clk_msr_sm1[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(127, "csi2_data"),
|
||||
};
|
||||
|
||||
static const struct meson_msr_id clk_msr_c3[] = {
|
||||
CLK_MSR_ID(0, "sys_clk"),
|
||||
CLK_MSR_ID(1, "axi_clk"),
|
||||
CLK_MSR_ID(2, "rtc_clk"),
|
||||
CLK_MSR_ID(3, "p20_usb2_ckout"),
|
||||
CLK_MSR_ID(4, "eth_mpll_test"),
|
||||
CLK_MSR_ID(5, "sys_pll"),
|
||||
CLK_MSR_ID(6, "cpu_clk_div16"),
|
||||
CLK_MSR_ID(7, "ts_pll"),
|
||||
CLK_MSR_ID(8, "fclk_div2"),
|
||||
CLK_MSR_ID(9, "fclk_div2p5"),
|
||||
CLK_MSR_ID(10, "fclk_div3"),
|
||||
CLK_MSR_ID(11, "fclk_div4"),
|
||||
CLK_MSR_ID(12, "fclk_div5"),
|
||||
CLK_MSR_ID(13, "fclk_div7"),
|
||||
CLK_MSR_ID(15, "fclk_50m"),
|
||||
CLK_MSR_ID(16, "sys_oscin32k_i"),
|
||||
CLK_MSR_ID(17, "mclk_pll"),
|
||||
CLK_MSR_ID(19, "hifi_pll"),
|
||||
CLK_MSR_ID(20, "gp0_pll"),
|
||||
CLK_MSR_ID(21, "gp1_pll"),
|
||||
CLK_MSR_ID(22, "eth_mppll_50m_ckout"),
|
||||
CLK_MSR_ID(23, "sys_pll_div16"),
|
||||
CLK_MSR_ID(24, "ddr_dpll_pt_clk"),
|
||||
CLK_MSR_ID(26, "nna_core"),
|
||||
CLK_MSR_ID(27, "rtc_sec_pulse_out"),
|
||||
CLK_MSR_ID(28, "rtc_osc_clk_out"),
|
||||
CLK_MSR_ID(29, "debug_in_clk"),
|
||||
CLK_MSR_ID(30, "mod_eth_phy_ref_clk"),
|
||||
CLK_MSR_ID(31, "mod_eth_tx_clk"),
|
||||
CLK_MSR_ID(32, "eth_125m"),
|
||||
CLK_MSR_ID(33, "eth_rmii"),
|
||||
CLK_MSR_ID(34, "co_clkin_to_mac"),
|
||||
CLK_MSR_ID(36, "co_rx_clk"),
|
||||
CLK_MSR_ID(37, "co_tx_clk"),
|
||||
CLK_MSR_ID(38, "eth_phy_rxclk"),
|
||||
CLK_MSR_ID(39, "eth_phy_plltxclk"),
|
||||
CLK_MSR_ID(40, "ephy_test_clk"),
|
||||
CLK_MSR_ID(66, "vapb"),
|
||||
CLK_MSR_ID(67, "ge2d"),
|
||||
CLK_MSR_ID(68, "dewarpa"),
|
||||
CLK_MSR_ID(70, "mipi_dsi_meas"),
|
||||
CLK_MSR_ID(71, "dsi_phy"),
|
||||
CLK_MSR_ID(79, "rama"),
|
||||
CLK_MSR_ID(94, "vc9000e_core"),
|
||||
CLK_MSR_ID(95, "vc9000e_sys"),
|
||||
CLK_MSR_ID(96, "vc9000e_aclk"),
|
||||
CLK_MSR_ID(97, "hcodec"),
|
||||
CLK_MSR_ID(106, "deskew_pll_clk_div32_out"),
|
||||
CLK_MSR_ID(107, "mipi_csi_phy_clk_out[0]"),
|
||||
CLK_MSR_ID(108, "mipi_csi_phy_clk_out[1]"),
|
||||
CLK_MSR_ID(110, "spifc"),
|
||||
CLK_MSR_ID(111, "saradc"),
|
||||
CLK_MSR_ID(112, "ts"),
|
||||
CLK_MSR_ID(113, "sd_emmc_c"),
|
||||
CLK_MSR_ID(114, "sd_emmc_b"),
|
||||
CLK_MSR_ID(115, "sd_emmc_a"),
|
||||
CLK_MSR_ID(116, "gpio_msr_clk"),
|
||||
CLK_MSR_ID(117, "spicc_b"),
|
||||
CLK_MSR_ID(118, "spicc_a"),
|
||||
CLK_MSR_ID(122, "mod_audio_pdm_dclk_o"),
|
||||
CLK_MSR_ID(124, "o_earcrx_dmac_clk"),
|
||||
CLK_MSR_ID(125, "o_earcrx_cmdc_clk"),
|
||||
CLK_MSR_ID(126, "o_earctx_dmac_clk"),
|
||||
CLK_MSR_ID(127, "o_earctx_cmdc_clk"),
|
||||
CLK_MSR_ID(128, "o_tohdmitx_bclk"),
|
||||
CLK_MSR_ID(129, "o_tohdmitx_mclk"),
|
||||
CLK_MSR_ID(130, "o_tohdmitx_spdif_clk"),
|
||||
CLK_MSR_ID(131, "o_toacodec_bclk"),
|
||||
CLK_MSR_ID(132, "o_toacodec_mclk"),
|
||||
CLK_MSR_ID(133, "o_spdifout_b_mst_clk"),
|
||||
CLK_MSR_ID(134, "o_spdifout_mst_clk"),
|
||||
CLK_MSR_ID(135, "o_spdifin_mst_clk"),
|
||||
CLK_MSR_ID(136, "o_audio_mclk"),
|
||||
CLK_MSR_ID(137, "o_vad_clk"),
|
||||
CLK_MSR_ID(138, "o_tdmout_d_sclk"),
|
||||
CLK_MSR_ID(139, "o_tdmout_c_sclk"),
|
||||
CLK_MSR_ID(140, "o_tdmout_b_sclk"),
|
||||
CLK_MSR_ID(141, "o_tdmout_a_sclk"),
|
||||
CLK_MSR_ID(142, "o_tdminb_1b_sclk"),
|
||||
CLK_MSR_ID(143, "o_tdmin_1b_sclk"),
|
||||
CLK_MSR_ID(144, "o_tdmin_d_sclk"),
|
||||
CLK_MSR_ID(145, "o_tdmin_c_sclk"),
|
||||
CLK_MSR_ID(146, "o_tdmin_b_sclk"),
|
||||
CLK_MSR_ID(147, "o_tdmin_a_sclk"),
|
||||
CLK_MSR_ID(148, "o_resampleb_clk"),
|
||||
CLK_MSR_ID(149, "o_resamplea_clk"),
|
||||
CLK_MSR_ID(150, "o_pdmb_sysclk"),
|
||||
CLK_MSR_ID(151, "o_pdmb_dclk"),
|
||||
CLK_MSR_ID(152, "o_pdm_sysclk"),
|
||||
CLK_MSR_ID(153, "o_pdm_dclk"),
|
||||
CLK_MSR_ID(154, "c_alockerb_out_clk"),
|
||||
CLK_MSR_ID(155, "c_alockerb_in_clk"),
|
||||
CLK_MSR_ID(156, "c_alocker_out_clk"),
|
||||
CLK_MSR_ID(157, "c_alocker_in_clk"),
|
||||
CLK_MSR_ID(158, "audio_mst_clk[34]"),
|
||||
CLK_MSR_ID(159, "audio_mst_clk[35]"),
|
||||
CLK_MSR_ID(160, "pwm_n"),
|
||||
CLK_MSR_ID(161, "pwm_m"),
|
||||
CLK_MSR_ID(162, "pwm_l"),
|
||||
CLK_MSR_ID(163, "pwm_k"),
|
||||
CLK_MSR_ID(164, "pwm_j"),
|
||||
CLK_MSR_ID(165, "pwm_i"),
|
||||
CLK_MSR_ID(166, "pwm_h"),
|
||||
CLK_MSR_ID(167, "pwm_g"),
|
||||
CLK_MSR_ID(168, "pwm_f"),
|
||||
CLK_MSR_ID(169, "pwm_e"),
|
||||
CLK_MSR_ID(170, "pwm_d"),
|
||||
CLK_MSR_ID(171, "pwm_c"),
|
||||
CLK_MSR_ID(172, "pwm_b"),
|
||||
CLK_MSR_ID(173, "pwm_a"),
|
||||
CLK_MSR_ID(174, "AU_DAC1_CLK_TO_GPIO"),
|
||||
CLK_MSR_ID(175, "AU_ADC_CLK_TO_GPIO"),
|
||||
CLK_MSR_ID(176, "rng_ring_osc_clk[0]"),
|
||||
CLK_MSR_ID(177, "rng_ring_osc_clk[1]"),
|
||||
CLK_MSR_ID(178, "rng_ring_osc_clk[2]"),
|
||||
CLK_MSR_ID(179, "rng_ring_osc_clk[3]"),
|
||||
CLK_MSR_ID(180, "sys_cpu_ring_osc_clk[0]"),
|
||||
CLK_MSR_ID(181, "sys_cpu_ring_osc_clk[1]"),
|
||||
CLK_MSR_ID(182, "sys_cpu_ring_osc_clk[2]"),
|
||||
CLK_MSR_ID(183, "sys_cpu_ring_osc_clk[3]"),
|
||||
CLK_MSR_ID(184, "sys_cpu_ring_osc_clk[4]"),
|
||||
CLK_MSR_ID(185, "sys_cpu_ring_osc_clk[5]"),
|
||||
CLK_MSR_ID(186, "sys_cpu_ring_osc_clk[6]"),
|
||||
CLK_MSR_ID(187, "sys_cpu_ring_osc_clk[7]"),
|
||||
CLK_MSR_ID(188, "sys_cpu_ring_osc_clk[8]"),
|
||||
CLK_MSR_ID(189, "sys_cpu_ring_osc_clk[9]"),
|
||||
CLK_MSR_ID(190, "sys_cpu_ring_osc_clk[10]"),
|
||||
CLK_MSR_ID(191, "sys_cpu_ring_osc_clk[11]"),
|
||||
CLK_MSR_ID(192, "am_ring_osc_clk_out[12](dmc)"),
|
||||
CLK_MSR_ID(193, "am_ring_osc_clk_out[13](rama)"),
|
||||
CLK_MSR_ID(194, "am_ring_osc_clk_out[14](nna)"),
|
||||
CLK_MSR_ID(195, "am_ring_osc_clk_out[15](nna)"),
|
||||
CLK_MSR_ID(200, "rng_ring_osc_clk_1[0]"),
|
||||
CLK_MSR_ID(201, "rng_ring_osc_clk_1[1]"),
|
||||
CLK_MSR_ID(202, "rng_ring_osc_clk_1[2]"),
|
||||
CLK_MSR_ID(203, "rng_ring_osc_clk_1[3]"),
|
||||
|
||||
};
|
||||
|
||||
static const struct meson_msr_id clk_msr_s4[] = {
|
||||
CLK_MSR_ID(0, "sys_clk"),
|
||||
CLK_MSR_ID(1, "axi_clk"),
|
||||
CLK_MSR_ID(2, "rtc_clk"),
|
||||
CLK_MSR_ID(5, "mali"),
|
||||
CLK_MSR_ID(6, "cpu_clk_div16"),
|
||||
CLK_MSR_ID(7, "ceca_clk"),
|
||||
CLK_MSR_ID(8, "cecb_clk"),
|
||||
CLK_MSR_ID(10, "fclk_div5"),
|
||||
CLK_MSR_ID(11, "mpll0"),
|
||||
CLK_MSR_ID(12, "mpll1"),
|
||||
CLK_MSR_ID(13, "mpll2"),
|
||||
CLK_MSR_ID(14, "mpll3"),
|
||||
CLK_MSR_ID(15, "fclk_50m"),
|
||||
CLK_MSR_ID(16, "pcie_clk_inp"),
|
||||
CLK_MSR_ID(17, "pcie_clk_inn"),
|
||||
CLK_MSR_ID(18, "mpll_clk_test_out"),
|
||||
CLK_MSR_ID(19, "hifi_pll"),
|
||||
CLK_MSR_ID(20, "gp0_pll"),
|
||||
CLK_MSR_ID(21, "gp1_pll"),
|
||||
CLK_MSR_ID(22, "eth_mppll_50m_ckout"),
|
||||
CLK_MSR_ID(23, "sys_pll_div16"),
|
||||
CLK_MSR_ID(24, "ddr_dpll_pt_clk"),
|
||||
CLK_MSR_ID(30, "mod_eth_phy_ref_clk"),
|
||||
CLK_MSR_ID(31, "mod_eth_tx_clk"),
|
||||
CLK_MSR_ID(32, "eth_125m"),
|
||||
CLK_MSR_ID(33, "eth_rmii"),
|
||||
CLK_MSR_ID(34, "co_clkin_to_mac"),
|
||||
CLK_MSR_ID(35, "mod_eth_rx_clk_rmii"),
|
||||
CLK_MSR_ID(36, "co_rx_clk"),
|
||||
CLK_MSR_ID(37, "co_tx_clk"),
|
||||
CLK_MSR_ID(38, "eth_phy_rxclk"),
|
||||
CLK_MSR_ID(39, "eth_phy_plltxclk"),
|
||||
CLK_MSR_ID(40, "ephy_test_clk"),
|
||||
CLK_MSR_ID(50, "vid_pll_div_clk_out"),
|
||||
CLK_MSR_ID(51, "enci"),
|
||||
CLK_MSR_ID(52, "encp"),
|
||||
CLK_MSR_ID(53, "encl"),
|
||||
CLK_MSR_ID(54, "vdac"),
|
||||
CLK_MSR_ID(55, "cdac_clk_c"),
|
||||
CLK_MSR_ID(56, "mod_tcon_clko"),
|
||||
CLK_MSR_ID(57, "lcd_an_clk_ph2"),
|
||||
CLK_MSR_ID(58, "lcd_an_clk_ph3"),
|
||||
CLK_MSR_ID(59, "hdmitx_pixel"),
|
||||
CLK_MSR_ID(60, "vdin_meas"),
|
||||
CLK_MSR_ID(61, "vpu"),
|
||||
CLK_MSR_ID(62, "vpu_clkb"),
|
||||
CLK_MSR_ID(63, "vpu_clkb_tmp"),
|
||||
CLK_MSR_ID(64, "vpu_clkc"),
|
||||
CLK_MSR_ID(65, "vid_lock"),
|
||||
CLK_MSR_ID(66, "vapb"),
|
||||
CLK_MSR_ID(67, "ge2d"),
|
||||
CLK_MSR_ID(68, "cts_hdcp22_esmclk"),
|
||||
CLK_MSR_ID(69, "cts_hdcp22_skpclk"),
|
||||
CLK_MSR_ID(76, "hdmitx_tmds"),
|
||||
CLK_MSR_ID(77, "hdmitx_sys_clk"),
|
||||
CLK_MSR_ID(78, "hdmitx_fe_clk"),
|
||||
CLK_MSR_ID(79, "rama"),
|
||||
CLK_MSR_ID(93, "vdec"),
|
||||
CLK_MSR_ID(99, "hevcf"),
|
||||
CLK_MSR_ID(100, "demod_core"),
|
||||
CLK_MSR_ID(101, "adc_extclk_in"),
|
||||
CLK_MSR_ID(102, "cts_demod_core_t2_clk"),
|
||||
CLK_MSR_ID(103, "adc_dpll_intclk"),
|
||||
CLK_MSR_ID(104, "adc_dpll_clk_b3"),
|
||||
CLK_MSR_ID(105, "s2_adc_clk"),
|
||||
CLK_MSR_ID(106, "deskew_pll_clk_div32_out"),
|
||||
CLK_MSR_ID(110, "sc"),
|
||||
CLK_MSR_ID(111, "sar_adc"),
|
||||
CLK_MSR_ID(113, "sd_emmc_c"),
|
||||
CLK_MSR_ID(114, "sd_emmc_b"),
|
||||
CLK_MSR_ID(115, "sd_emmc_a"),
|
||||
CLK_MSR_ID(116, "gpio_msr_clk"),
|
||||
CLK_MSR_ID(118, "spicc0"),
|
||||
CLK_MSR_ID(121, "ts"),
|
||||
CLK_MSR_ID(130, "audio_vad_clk"),
|
||||
CLK_MSR_ID(131, "acodec_dac_clk_x128"),
|
||||
CLK_MSR_ID(132, "audio_locker_in_clk"),
|
||||
CLK_MSR_ID(133, "audio_locker_out_clk"),
|
||||
CLK_MSR_ID(134, "audio_tdmout_c_sclk"),
|
||||
CLK_MSR_ID(135, "audio_tdmout_b_sclk"),
|
||||
CLK_MSR_ID(136, "audio_tdmout_a_sclk"),
|
||||
CLK_MSR_ID(137, "audio_tdmin_lb_sclk"),
|
||||
CLK_MSR_ID(138, "audio_tdmin_c_sclk"),
|
||||
CLK_MSR_ID(139, "audio_tdmin_b_sclk"),
|
||||
CLK_MSR_ID(140, "audio_tdmin_a_sclk"),
|
||||
CLK_MSR_ID(141, "audio_resamplea_clk"),
|
||||
CLK_MSR_ID(142, "audio_pdm_sysclk"),
|
||||
CLK_MSR_ID(143, "audio_spdifout_b_mst_clk"),
|
||||
CLK_MSR_ID(144, "audio_spdifout_mst_clk"),
|
||||
CLK_MSR_ID(145, "audio_spdifin_mst_clk"),
|
||||
CLK_MSR_ID(146, "audio_pdm_dclk"),
|
||||
CLK_MSR_ID(147, "audio_resampleb_clk"),
|
||||
CLK_MSR_ID(160, "pwm_j"),
|
||||
CLK_MSR_ID(161, "pwm_i"),
|
||||
CLK_MSR_ID(162, "pwm_h"),
|
||||
CLK_MSR_ID(163, "pwm_g"),
|
||||
CLK_MSR_ID(164, "pwm_f"),
|
||||
CLK_MSR_ID(165, "pwm_e"),
|
||||
CLK_MSR_ID(166, "pwm_d"),
|
||||
CLK_MSR_ID(167, "pwm_c"),
|
||||
CLK_MSR_ID(168, "pwm_b"),
|
||||
CLK_MSR_ID(169, "pwm_a"),
|
||||
CLK_MSR_ID(176, "rng_ring_0"),
|
||||
CLK_MSR_ID(177, "rng_ring_1"),
|
||||
CLK_MSR_ID(178, "rng_ring_2"),
|
||||
CLK_MSR_ID(179, "rng_ring_3"),
|
||||
CLK_MSR_ID(180, "dmc_osc_ring(LVT16)"),
|
||||
CLK_MSR_ID(181, "gpu_osc_ring0(LVT16)"),
|
||||
CLK_MSR_ID(182, "gpu_osc_ring1(ULVT16)"),
|
||||
CLK_MSR_ID(183, "gpu_osc_ring2(SLVT16)"),
|
||||
CLK_MSR_ID(184, "vpu_osc_ring0(SVT24)"),
|
||||
CLK_MSR_ID(185, "vpu_osc_ring1(LVT20)"),
|
||||
CLK_MSR_ID(186, "vpu_osc_ring2(LVT16)"),
|
||||
CLK_MSR_ID(187, "dos_osc_ring0(SVT24)"),
|
||||
CLK_MSR_ID(188, "dos_osc_ring1(SVT16)"),
|
||||
CLK_MSR_ID(189, "dos_osc_ring2(LVT16)"),
|
||||
CLK_MSR_ID(190, "dos_osc_ring3(ULVT20)"),
|
||||
CLK_MSR_ID(192, "axi_sram_osc_ring(SVT16)"),
|
||||
CLK_MSR_ID(193, "demod_osc_ring0"),
|
||||
CLK_MSR_ID(194, "demod_osc_ring1"),
|
||||
CLK_MSR_ID(195, "sar_osc_ring"),
|
||||
CLK_MSR_ID(196, "sys_cpu_osc_ring0"),
|
||||
CLK_MSR_ID(197, "sys_cpu_osc_ring1"),
|
||||
CLK_MSR_ID(198, "sys_cpu_osc_ring2"),
|
||||
CLK_MSR_ID(199, "sys_cpu_osc_ring3"),
|
||||
CLK_MSR_ID(200, "sys_cpu_osc_ring4"),
|
||||
CLK_MSR_ID(201, "sys_cpu_osc_ring5"),
|
||||
CLK_MSR_ID(202, "sys_cpu_osc_ring6"),
|
||||
CLK_MSR_ID(203, "sys_cpu_osc_ring7"),
|
||||
CLK_MSR_ID(204, "sys_cpu_osc_ring8"),
|
||||
CLK_MSR_ID(205, "sys_cpu_osc_ring9"),
|
||||
CLK_MSR_ID(206, "sys_cpu_osc_ring10"),
|
||||
CLK_MSR_ID(207, "sys_cpu_osc_ring11"),
|
||||
CLK_MSR_ID(208, "sys_cpu_osc_ring12"),
|
||||
CLK_MSR_ID(209, "sys_cpu_osc_ring13"),
|
||||
CLK_MSR_ID(210, "sys_cpu_osc_ring14"),
|
||||
CLK_MSR_ID(211, "sys_cpu_osc_ring15"),
|
||||
CLK_MSR_ID(212, "sys_cpu_osc_ring16"),
|
||||
CLK_MSR_ID(213, "sys_cpu_osc_ring17"),
|
||||
CLK_MSR_ID(214, "sys_cpu_osc_ring18"),
|
||||
CLK_MSR_ID(215, "sys_cpu_osc_ring19"),
|
||||
CLK_MSR_ID(216, "sys_cpu_osc_ring20"),
|
||||
CLK_MSR_ID(217, "sys_cpu_osc_ring21"),
|
||||
CLK_MSR_ID(218, "sys_cpu_osc_ring22"),
|
||||
CLK_MSR_ID(219, "sys_cpu_osc_ring23"),
|
||||
CLK_MSR_ID(220, "sys_cpu_osc_ring24"),
|
||||
CLK_MSR_ID(221, "sys_cpu_osc_ring25"),
|
||||
CLK_MSR_ID(222, "sys_cpu_osc_ring26"),
|
||||
CLK_MSR_ID(223, "sys_cpu_osc_ring27"),
|
||||
|
||||
};
|
||||
|
||||
static int meson_measure_id(struct meson_msr_id *clk_msr_id,
|
||||
unsigned int duration)
|
||||
unsigned int duration)
|
||||
{
|
||||
struct meson_msr *priv = clk_msr_id->priv;
|
||||
const struct msr_reg_offset *reg = priv->data.reg;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
@ -499,22 +799,22 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, MSR_CLK_REG0, 0);
|
||||
regmap_write(priv->regmap, reg->freq_ctrl, 0);
|
||||
|
||||
/* Set measurement duration */
|
||||
regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_DURATION,
|
||||
regmap_update_bits(priv->regmap, reg->freq_ctrl, MSR_DURATION,
|
||||
FIELD_PREP(MSR_DURATION, duration - 1));
|
||||
|
||||
/* Set ID */
|
||||
regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_CLK_SRC,
|
||||
regmap_update_bits(priv->regmap, reg->freq_ctrl, MSR_CLK_SRC,
|
||||
FIELD_PREP(MSR_CLK_SRC, clk_msr_id->id));
|
||||
|
||||
/* Enable & Start */
|
||||
regmap_update_bits(priv->regmap, MSR_CLK_REG0,
|
||||
regmap_update_bits(priv->regmap, reg->freq_ctrl,
|
||||
MSR_RUN | MSR_ENABLE,
|
||||
MSR_RUN | MSR_ENABLE);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MSR_CLK_REG0,
|
||||
ret = regmap_read_poll_timeout(priv->regmap, reg->freq_ctrl,
|
||||
val, !(val & MSR_BUSY), 10, 10000);
|
||||
if (ret) {
|
||||
mutex_unlock(&measure_lock);
|
||||
@ -522,10 +822,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
|
||||
}
|
||||
|
||||
/* Disable */
|
||||
regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_ENABLE, 0);
|
||||
regmap_update_bits(priv->regmap, reg->freq_ctrl, MSR_ENABLE, 0);
|
||||
|
||||
/* Get the value in multiple of gate time counts */
|
||||
regmap_read(priv->regmap, MSR_CLK_REG2, &val);
|
||||
regmap_read(priv->regmap, reg->freq_val, &val);
|
||||
|
||||
mutex_unlock(&measure_lock);
|
||||
|
||||
@ -573,13 +873,14 @@ DEFINE_SHOW_ATTRIBUTE(clk_msr);
|
||||
static int clk_msr_summary_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct meson_msr_id *msr_table = s->private;
|
||||
unsigned int msr_count = msr_table->priv->data.msr_count;
|
||||
unsigned int precision = 0;
|
||||
int val, i;
|
||||
|
||||
seq_puts(s, " clock rate precision\n");
|
||||
seq_puts(s, "---------------------------------------------\n");
|
||||
|
||||
for (i = 0 ; i < CLK_MSR_MAX ; ++i) {
|
||||
for (i = 0 ; i < msr_count ; ++i) {
|
||||
if (!msr_table[i].name)
|
||||
continue;
|
||||
|
||||
@ -595,18 +896,18 @@ static int clk_msr_summary_show(struct seq_file *s, void *data)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(clk_msr_summary);
|
||||
|
||||
static const struct regmap_config meson_clk_msr_regmap_config = {
|
||||
static struct regmap_config meson_clk_msr_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = MSR_CLK_REG2,
|
||||
};
|
||||
|
||||
static int meson_msr_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct meson_msr_id *match_data;
|
||||
const struct meson_msr_data *match_data;
|
||||
struct meson_msr *priv;
|
||||
struct dentry *root, *clks;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int i;
|
||||
|
||||
@ -621,60 +922,142 @@ static int meson_msr_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
memcpy(priv->msr_table, match_data, sizeof(priv->msr_table));
|
||||
priv->data.msr_table = devm_kcalloc(&pdev->dev,
|
||||
match_data->msr_count,
|
||||
sizeof(struct meson_msr_id),
|
||||
GFP_KERNEL);
|
||||
if (!priv->data.msr_table)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
memcpy(priv->data.msr_table, match_data->msr_table,
|
||||
match_data->msr_count * sizeof(struct meson_msr_id));
|
||||
priv->data.msr_count = match_data->msr_count;
|
||||
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
meson_clk_msr_regmap_config.max_register = resource_size(res) - 4;
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&meson_clk_msr_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->data.reg = devm_kzalloc(&pdev->dev, sizeof(struct msr_reg_offset),
|
||||
GFP_KERNEL);
|
||||
if (!priv->data.reg)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy((void *)priv->data.reg, match_data->reg,
|
||||
sizeof(struct msr_reg_offset));
|
||||
|
||||
root = debugfs_create_dir("meson-clk-msr", NULL);
|
||||
clks = debugfs_create_dir("clks", root);
|
||||
|
||||
debugfs_create_file("measure_summary", 0444, root,
|
||||
priv->msr_table, &clk_msr_summary_fops);
|
||||
priv->data.msr_table, &clk_msr_summary_fops);
|
||||
|
||||
for (i = 0 ; i < CLK_MSR_MAX ; ++i) {
|
||||
if (!priv->msr_table[i].name)
|
||||
for (i = 0 ; i < priv->data.msr_count ; ++i) {
|
||||
if (!priv->data.msr_table[i].name)
|
||||
continue;
|
||||
|
||||
priv->msr_table[i].priv = priv;
|
||||
priv->data.msr_table[i].priv = priv;
|
||||
|
||||
debugfs_create_file(priv->msr_table[i].name, 0444, clks,
|
||||
&priv->msr_table[i], &clk_msr_fops);
|
||||
debugfs_create_file(priv->data.msr_table[i].name, 0444, clks,
|
||||
&priv->data.msr_table[i], &clk_msr_fops);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct msr_reg_offset msr_reg_offset = {
|
||||
.duty_val = 0x0,
|
||||
.freq_ctrl = 0x4,
|
||||
.duty_ctrl = 0x8,
|
||||
.freq_val = 0xc,
|
||||
};
|
||||
|
||||
static const struct meson_msr_data clk_msr_gx_data = {
|
||||
.msr_table = (void *)clk_msr_gx,
|
||||
.msr_count = ARRAY_SIZE(clk_msr_gx),
|
||||
.reg = &msr_reg_offset,
|
||||
};
|
||||
|
||||
static const struct meson_msr_data clk_msr_m8_data = {
|
||||
.msr_table = (void *)clk_msr_m8,
|
||||
.msr_count = ARRAY_SIZE(clk_msr_m8),
|
||||
.reg = &msr_reg_offset,
|
||||
};
|
||||
|
||||
static const struct meson_msr_data clk_msr_axg_data = {
|
||||
.msr_table = (void *)clk_msr_axg,
|
||||
.msr_count = ARRAY_SIZE(clk_msr_axg),
|
||||
.reg = &msr_reg_offset,
|
||||
};
|
||||
|
||||
static const struct meson_msr_data clk_msr_g12a_data = {
|
||||
.msr_table = (void *)clk_msr_g12a,
|
||||
.msr_count = ARRAY_SIZE(clk_msr_g12a),
|
||||
.reg = &msr_reg_offset,
|
||||
};
|
||||
|
||||
static const struct meson_msr_data clk_msr_sm1_data = {
|
||||
.msr_table = (void *)clk_msr_sm1,
|
||||
.msr_count = ARRAY_SIZE(clk_msr_sm1),
|
||||
.reg = &msr_reg_offset,
|
||||
};
|
||||
|
||||
static const struct msr_reg_offset msr_reg_offset_v2 = {
|
||||
.freq_ctrl = 0x0,
|
||||
.duty_ctrl = 0x4,
|
||||
.freq_val = 0x8,
|
||||
.duty_val = 0x18,
|
||||
};
|
||||
|
||||
static const struct meson_msr_data clk_msr_c3_data = {
|
||||
.msr_table = (void *)clk_msr_c3,
|
||||
.msr_count = ARRAY_SIZE(clk_msr_c3),
|
||||
.reg = &msr_reg_offset_v2,
|
||||
};
|
||||
|
||||
static const struct meson_msr_data clk_msr_s4_data = {
|
||||
.msr_table = (void *)clk_msr_s4,
|
||||
.msr_count = ARRAY_SIZE(clk_msr_s4),
|
||||
.reg = &msr_reg_offset_v2,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_msr_match_table[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson-gx-clk-measure",
|
||||
.data = (void *)clk_msr_gx,
|
||||
.data = &clk_msr_gx_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson8-clk-measure",
|
||||
.data = (void *)clk_msr_m8,
|
||||
.data = &clk_msr_m8_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson8b-clk-measure",
|
||||
.data = (void *)clk_msr_m8,
|
||||
.data = &clk_msr_m8_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-axg-clk-measure",
|
||||
.data = (void *)clk_msr_axg,
|
||||
.data = &clk_msr_axg_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-g12a-clk-measure",
|
||||
.data = (void *)clk_msr_g12a,
|
||||
.data = &clk_msr_g12a_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-sm1-clk-measure",
|
||||
.data = (void *)clk_msr_sm1,
|
||||
.data = &clk_msr_sm1_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,c3-clk-measure",
|
||||
.data = &clk_msr_c3_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,s4-clk-measure",
|
||||
.data = &clk_msr_s4_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
@ -166,7 +166,7 @@ static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop,
|
||||
int rc;
|
||||
|
||||
lpc_snoop->irq = platform_get_irq(pdev, 0);
|
||||
if (!lpc_snoop->irq)
|
||||
if (lpc_snoop->irq < 0)
|
||||
return -ENODEV;
|
||||
|
||||
rc = devm_request_irq(dev, lpc_snoop->irq,
|
||||
@ -200,11 +200,15 @@ static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
|
||||
lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||
lpc_snoop->chan[channel].miscdev.name =
|
||||
devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
|
||||
if (!lpc_snoop->chan[channel].miscdev.name) {
|
||||
rc = -ENOMEM;
|
||||
goto err_free_fifo;
|
||||
}
|
||||
lpc_snoop->chan[channel].miscdev.fops = &snoop_fops;
|
||||
lpc_snoop->chan[channel].miscdev.parent = dev;
|
||||
rc = misc_register(&lpc_snoop->chan[channel].miscdev);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto err_free_fifo;
|
||||
|
||||
/* Enable LPC snoop channel at requested port */
|
||||
switch (channel) {
|
||||
@ -221,7 +225,8 @@ static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
|
||||
hicrb_en = HICRB_ENSNP1D;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
rc = -EINVAL;
|
||||
goto err_misc_deregister;
|
||||
}
|
||||
|
||||
regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en);
|
||||
@ -231,6 +236,12 @@ static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
|
||||
regmap_update_bits(lpc_snoop->regmap, HICRB,
|
||||
hicrb_en, hicrb_en);
|
||||
|
||||
return 0;
|
||||
|
||||
err_misc_deregister:
|
||||
misc_deregister(&lpc_snoop->chan[channel].miscdev);
|
||||
err_free_fifo:
|
||||
kfifo_free(&lpc_snoop->chan[channel].fifo);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ config FSL_MC_DPIO
|
||||
config DPAA2_CONSOLE
|
||||
tristate "QorIQ DPAA2 console driver"
|
||||
depends on OF && (ARCH_LAYERSCAPE || COMPILE_TEST)
|
||||
default y
|
||||
default ARCH_LAYERSCAPE
|
||||
help
|
||||
Console driver for DPAA2 platforms. Exports 2 char devices,
|
||||
/dev/dpaa2_mc_console and /dev/dpaa2_aiop_console,
|
||||
|
@ -1270,7 +1270,7 @@ static int qman_create_portal(struct qman_portal *portal,
|
||||
qm_dqrr_set_ithresh(p, QMAN_PIRQ_DQRR_ITHRESH);
|
||||
qm_mr_set_ithresh(p, QMAN_PIRQ_MR_ITHRESH);
|
||||
qm_out(p, QM_REG_ITPR, QMAN_PIRQ_IPERIOD);
|
||||
portal->cgrs = kmalloc_array(2, sizeof(*cgrs), GFP_KERNEL);
|
||||
portal->cgrs = kmalloc_array(2, sizeof(*portal->cgrs), GFP_KERNEL);
|
||||
if (!portal->cgrs)
|
||||
goto fail_cgrs;
|
||||
/* initial snapshot is no-depletion */
|
||||
|
@ -232,11 +232,6 @@ static inline void qe_ic_write(__be32 __iomem *base, unsigned int reg,
|
||||
iowrite32be(value, base + (reg >> 2));
|
||||
}
|
||||
|
||||
static inline struct qe_ic *qe_ic_from_irq(unsigned int virq)
|
||||
{
|
||||
return irq_get_chip_data(virq);
|
||||
}
|
||||
|
||||
static inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d)
|
||||
{
|
||||
return irq_data_get_irq_chip_data(d);
|
||||
@ -455,13 +450,11 @@ static int qe_ic_init(struct platform_device *pdev)
|
||||
|
||||
qe_ic_write(qe_ic->regs, QEIC_CICR, 0);
|
||||
|
||||
irq_set_handler_data(qe_ic->virq_low, qe_ic);
|
||||
irq_set_chained_handler(qe_ic->virq_low, low_handler);
|
||||
irq_set_chained_handler_and_data(qe_ic->virq_low, low_handler, qe_ic);
|
||||
|
||||
if (high_handler) {
|
||||
irq_set_handler_data(qe_ic->virq_high, qe_ic);
|
||||
irq_set_chained_handler(qe_ic->virq_high, high_handler);
|
||||
}
|
||||
if (high_handler)
|
||||
irq_set_chained_handler_and_data(qe_ic->virq_high,
|
||||
high_handler, qe_ic);
|
||||
return 0;
|
||||
}
|
||||
static const struct of_device_id qe_ic_ids[] = {
|
||||
|
@ -167,10 +167,6 @@ static void hccs_pcc_rx_callback(struct mbox_client *cl, void *mssg)
|
||||
|
||||
static void hccs_unregister_pcc_channel(struct hccs_dev *hdev)
|
||||
{
|
||||
struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
|
||||
|
||||
if (cl_info->pcc_comm_addr)
|
||||
iounmap(cl_info->pcc_comm_addr);
|
||||
pcc_mbox_free_channel(hdev->cl_info.pcc_chan);
|
||||
}
|
||||
|
||||
@ -179,6 +175,7 @@ static int hccs_register_pcc_channel(struct hccs_dev *hdev)
|
||||
struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
|
||||
struct mbox_client *cl = &cl_info->client;
|
||||
struct pcc_mbox_chan *pcc_chan;
|
||||
struct mbox_chan *mbox_chan;
|
||||
struct device *dev = hdev->dev;
|
||||
int rc;
|
||||
|
||||
@ -196,7 +193,7 @@ static int hccs_register_pcc_channel(struct hccs_dev *hdev)
|
||||
goto out;
|
||||
}
|
||||
cl_info->pcc_chan = pcc_chan;
|
||||
cl_info->mbox_chan = pcc_chan->mchan;
|
||||
mbox_chan = pcc_chan->mchan;
|
||||
|
||||
/*
|
||||
* pcc_chan->latency is just a nominal value. In reality the remote
|
||||
@ -206,34 +203,24 @@ static int hccs_register_pcc_channel(struct hccs_dev *hdev)
|
||||
cl_info->deadline_us =
|
||||
HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
|
||||
if (!hdev->verspec_data->has_txdone_irq &&
|
||||
cl_info->mbox_chan->mbox->txdone_irq) {
|
||||
mbox_chan->mbox->txdone_irq) {
|
||||
dev_err(dev, "PCC IRQ in PCCT is enabled.\n");
|
||||
rc = -EINVAL;
|
||||
goto err_mbx_channel_free;
|
||||
} else if (hdev->verspec_data->has_txdone_irq &&
|
||||
!cl_info->mbox_chan->mbox->txdone_irq) {
|
||||
!mbox_chan->mbox->txdone_irq) {
|
||||
dev_err(dev, "PCC IRQ in PCCT isn't supported.\n");
|
||||
rc = -EINVAL;
|
||||
goto err_mbx_channel_free;
|
||||
}
|
||||
|
||||
if (!pcc_chan->shmem_base_addr ||
|
||||
pcc_chan->shmem_size != HCCS_PCC_SHARE_MEM_BYTES) {
|
||||
dev_err(dev, "The base address or size (%llu) of PCC communication region is invalid.\n",
|
||||
pcc_chan->shmem_size);
|
||||
if (pcc_chan->shmem_size != HCCS_PCC_SHARE_MEM_BYTES) {
|
||||
dev_err(dev, "Base size (%llu) of PCC communication region must be %d bytes.\n",
|
||||
pcc_chan->shmem_size, HCCS_PCC_SHARE_MEM_BYTES);
|
||||
rc = -EINVAL;
|
||||
goto err_mbx_channel_free;
|
||||
}
|
||||
|
||||
cl_info->pcc_comm_addr = ioremap(pcc_chan->shmem_base_addr,
|
||||
pcc_chan->shmem_size);
|
||||
if (!cl_info->pcc_comm_addr) {
|
||||
dev_err(dev, "Failed to ioremap PCC communication region for channel-%u.\n",
|
||||
hdev->chan_id);
|
||||
rc = -ENOMEM;
|
||||
goto err_mbx_channel_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_mbx_channel_free:
|
||||
@ -246,7 +233,7 @@ static int hccs_wait_cmd_complete_by_poll(struct hccs_dev *hdev)
|
||||
{
|
||||
struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
|
||||
struct acpi_pcct_shared_memory __iomem *comm_base =
|
||||
cl_info->pcc_comm_addr;
|
||||
cl_info->pcc_chan->shmem;
|
||||
u16 status;
|
||||
int ret;
|
||||
|
||||
@ -289,7 +276,7 @@ static inline void hccs_fill_pcc_shared_mem_region(struct hccs_dev *hdev,
|
||||
.status = 0,
|
||||
};
|
||||
|
||||
memcpy_toio(hdev->cl_info.pcc_comm_addr, (void *)&tmp,
|
||||
memcpy_toio(hdev->cl_info.pcc_chan->shmem, (void *)&tmp,
|
||||
sizeof(struct acpi_pcct_shared_memory));
|
||||
|
||||
/* Copy the message to the PCC comm space */
|
||||
@ -309,7 +296,7 @@ static inline void hccs_fill_ext_pcc_shared_mem_region(struct hccs_dev *hdev,
|
||||
.command = cmd,
|
||||
};
|
||||
|
||||
memcpy_toio(hdev->cl_info.pcc_comm_addr, (void *)&tmp,
|
||||
memcpy_toio(hdev->cl_info.pcc_chan->shmem, (void *)&tmp,
|
||||
sizeof(struct acpi_pcct_ext_pcc_shared_memory));
|
||||
|
||||
/* Copy the message to the PCC comm space */
|
||||
@ -321,12 +308,13 @@ static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
|
||||
{
|
||||
const struct hccs_verspecific_data *verspec_data = hdev->verspec_data;
|
||||
struct hccs_mbox_client_info *cl_info = &hdev->cl_info;
|
||||
struct mbox_chan *mbox_chan = cl_info->pcc_chan->mchan;
|
||||
struct hccs_fw_inner_head *fw_inner_head;
|
||||
void __iomem *comm_space;
|
||||
u16 space_size;
|
||||
int ret;
|
||||
|
||||
comm_space = cl_info->pcc_comm_addr + verspec_data->shared_mem_size;
|
||||
comm_space = cl_info->pcc_chan->shmem + verspec_data->shared_mem_size;
|
||||
space_size = HCCS_PCC_SHARE_MEM_BYTES - verspec_data->shared_mem_size;
|
||||
verspec_data->fill_pcc_shared_mem(hdev, cmd, desc,
|
||||
comm_space, space_size);
|
||||
@ -334,7 +322,7 @@ static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
|
||||
reinit_completion(&cl_info->done);
|
||||
|
||||
/* Ring doorbell */
|
||||
ret = mbox_send_message(cl_info->mbox_chan, &cmd);
|
||||
ret = mbox_send_message(mbox_chan, &cmd);
|
||||
if (ret < 0) {
|
||||
dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n",
|
||||
ret);
|
||||
@ -356,9 +344,9 @@ static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd,
|
||||
|
||||
end:
|
||||
if (verspec_data->has_txdone_irq)
|
||||
mbox_chan_txdone(cl_info->mbox_chan, ret);
|
||||
mbox_chan_txdone(mbox_chan, ret);
|
||||
else
|
||||
mbox_client_txdone(cl_info->mbox_chan, ret);
|
||||
mbox_client_txdone(mbox_chan, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -60,10 +60,8 @@ struct hccs_chip_info {
|
||||
|
||||
struct hccs_mbox_client_info {
|
||||
struct mbox_client client;
|
||||
struct mbox_chan *mbox_chan;
|
||||
struct pcc_mbox_chan *pcc_chan;
|
||||
u64 deadline_us;
|
||||
void __iomem *pcc_comm_addr;
|
||||
struct completion done;
|
||||
};
|
||||
|
||||
|
@ -24,13 +24,21 @@
|
||||
#define OCOTP_UID_HIGH 0x420
|
||||
|
||||
#define IMX8MP_OCOTP_UID_OFFSET 0x10
|
||||
#define IMX8MP_OCOTP_UID_HIGH 0xE00
|
||||
|
||||
/* Same as ANADIG_DIGPROG_IMX7D */
|
||||
#define ANADIG_DIGPROG_IMX8MM 0x800
|
||||
|
||||
struct imx8_soc_data {
|
||||
char *name;
|
||||
int (*soc_revision)(u32 *socrev, u64 *socuid);
|
||||
const char *ocotp_compatible;
|
||||
int (*soc_revision)(struct platform_device *pdev, u32 *socrev);
|
||||
int (*soc_uid)(struct platform_device *pdev, u64 *socuid);
|
||||
};
|
||||
|
||||
struct imx8_soc_drvdata {
|
||||
void __iomem *ocotp_base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HAVE_ARM_SMCCC
|
||||
@ -49,30 +57,24 @@ static u32 imx8mq_soc_revision_from_atf(void)
|
||||
static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; };
|
||||
#endif
|
||||
|
||||
static int imx8mq_soc_revision(u32 *socrev, u64 *socuid)
|
||||
static int imx8m_soc_uid(struct platform_device *pdev, u64 *socuid)
|
||||
{
|
||||
struct device_node *np __free(device_node) =
|
||||
of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp");
|
||||
void __iomem *ocotp_base;
|
||||
struct imx8_soc_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ocotp_base = drvdata->ocotp_base;
|
||||
|
||||
*socuid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
|
||||
*socuid <<= 32;
|
||||
*socuid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mq_soc_revision(struct platform_device *pdev, u32 *socrev)
|
||||
{
|
||||
struct imx8_soc_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ocotp_base = drvdata->ocotp_base;
|
||||
u32 magic;
|
||||
u32 rev;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
ocotp_base = of_iomap(np, 0);
|
||||
if (!ocotp_base)
|
||||
return -EINVAL;
|
||||
|
||||
clk = of_clk_get_by_name(np, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
/*
|
||||
* SOC revision on older imx8mq is not available in fuses so query
|
||||
@ -85,61 +87,28 @@ static int imx8mq_soc_revision(u32 *socrev, u64 *socuid)
|
||||
rev = REV_B1;
|
||||
}
|
||||
|
||||
*socuid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
|
||||
*socuid <<= 32;
|
||||
*socuid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
|
||||
|
||||
*socrev = rev;
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
iounmap(ocotp_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mp_soc_uid(struct platform_device *pdev, u64 *socuid)
|
||||
{
|
||||
struct imx8_soc_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ocotp_base = drvdata->ocotp_base;
|
||||
|
||||
socuid[0] = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + IMX8MP_OCOTP_UID_OFFSET);
|
||||
socuid[0] <<= 32;
|
||||
socuid[0] |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + IMX8MP_OCOTP_UID_OFFSET);
|
||||
|
||||
socuid[1] = readl_relaxed(ocotp_base + IMX8MP_OCOTP_UID_HIGH + 0x10);
|
||||
socuid[1] <<= 32;
|
||||
socuid[1] |= readl_relaxed(ocotp_base + IMX8MP_OCOTP_UID_HIGH);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
iounmap(ocotp_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx8mm_soc_uid(u64 *socuid)
|
||||
{
|
||||
struct device_node *np __free(device_node) =
|
||||
of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
|
||||
void __iomem *ocotp_base;
|
||||
struct clk *clk;
|
||||
int ret = 0;
|
||||
u32 offset = of_machine_is_compatible("fsl,imx8mp") ?
|
||||
IMX8MP_OCOTP_UID_OFFSET : 0;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
ocotp_base = of_iomap(np, 0);
|
||||
if (!ocotp_base)
|
||||
return -EINVAL;
|
||||
|
||||
clk = of_clk_get_by_name(np, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
*socuid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + offset);
|
||||
*socuid <<= 32;
|
||||
*socuid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + offset);
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
|
||||
err_clk:
|
||||
iounmap(ocotp_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx8mm_soc_revision(u32 *socrev, u64 *socuid)
|
||||
static int imx8mm_soc_revision(struct platform_device *pdev, u32 *socrev)
|
||||
{
|
||||
struct device_node *np __free(device_node) =
|
||||
of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
|
||||
@ -156,27 +125,71 @@ static int imx8mm_soc_revision(u32 *socrev, u64 *socuid)
|
||||
|
||||
iounmap(anatop_base);
|
||||
|
||||
return imx8mm_soc_uid(socuid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8m_soc_prepare(struct platform_device *pdev, const char *ocotp_compatible)
|
||||
{
|
||||
struct device_node *np __free(device_node) =
|
||||
of_find_compatible_node(NULL, NULL, ocotp_compatible);
|
||||
struct imx8_soc_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
int ret = 0;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
drvdata->ocotp_base = of_iomap(np, 0);
|
||||
if (!drvdata->ocotp_base)
|
||||
return -EINVAL;
|
||||
|
||||
drvdata->clk = of_clk_get_by_name(np, NULL);
|
||||
if (IS_ERR(drvdata->clk)) {
|
||||
ret = PTR_ERR(drvdata->clk);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
return clk_prepare_enable(drvdata->clk);
|
||||
|
||||
err_clk:
|
||||
iounmap(drvdata->ocotp_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void imx8m_soc_unprepare(struct platform_device *pdev)
|
||||
{
|
||||
struct imx8_soc_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
clk_put(drvdata->clk);
|
||||
iounmap(drvdata->ocotp_base);
|
||||
}
|
||||
|
||||
static const struct imx8_soc_data imx8mq_soc_data = {
|
||||
.name = "i.MX8MQ",
|
||||
.ocotp_compatible = "fsl,imx8mq-ocotp",
|
||||
.soc_revision = imx8mq_soc_revision,
|
||||
.soc_uid = imx8m_soc_uid,
|
||||
};
|
||||
|
||||
static const struct imx8_soc_data imx8mm_soc_data = {
|
||||
.name = "i.MX8MM",
|
||||
.ocotp_compatible = "fsl,imx8mm-ocotp",
|
||||
.soc_revision = imx8mm_soc_revision,
|
||||
.soc_uid = imx8m_soc_uid,
|
||||
};
|
||||
|
||||
static const struct imx8_soc_data imx8mn_soc_data = {
|
||||
.name = "i.MX8MN",
|
||||
.ocotp_compatible = "fsl,imx8mm-ocotp",
|
||||
.soc_revision = imx8mm_soc_revision,
|
||||
.soc_uid = imx8m_soc_uid,
|
||||
};
|
||||
|
||||
static const struct imx8_soc_data imx8mp_soc_data = {
|
||||
.name = "i.MX8MP",
|
||||
.ocotp_compatible = "fsl,imx8mm-ocotp",
|
||||
.soc_revision = imx8mm_soc_revision,
|
||||
.soc_uid = imx8mp_soc_uid,
|
||||
};
|
||||
|
||||
static __maybe_unused const struct of_device_id imx8_soc_match[] = {
|
||||
@ -207,17 +220,24 @@ static int imx8m_soc_probe(struct platform_device *pdev)
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
struct platform_device *cpufreq_dev;
|
||||
const struct imx8_soc_data *data;
|
||||
struct imx8_soc_drvdata *drvdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *id;
|
||||
struct soc_device *soc_dev;
|
||||
u32 soc_rev = 0;
|
||||
u64 soc_uid = 0;
|
||||
u64 soc_uid[2] = {0, 0};
|
||||
int ret;
|
||||
|
||||
soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
soc_dev_attr->family = "Freescale i.MX";
|
||||
|
||||
ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
|
||||
@ -231,18 +251,37 @@ static int imx8m_soc_probe(struct platform_device *pdev)
|
||||
data = id->data;
|
||||
if (data) {
|
||||
soc_dev_attr->soc_id = data->name;
|
||||
ret = imx8m_soc_prepare(pdev, data->ocotp_compatible);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data->soc_revision) {
|
||||
ret = data->soc_revision(&soc_rev, &soc_uid);
|
||||
if (ret)
|
||||
ret = data->soc_revision(pdev, &soc_rev);
|
||||
if (ret) {
|
||||
imx8m_soc_unprepare(pdev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (data->soc_uid) {
|
||||
ret = data->soc_uid(pdev, soc_uid);
|
||||
if (ret) {
|
||||
imx8m_soc_unprepare(pdev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
imx8m_soc_unprepare(pdev);
|
||||
}
|
||||
|
||||
soc_dev_attr->revision = imx8_revision(dev, soc_rev);
|
||||
if (!soc_dev_attr->revision)
|
||||
return -ENOMEM;
|
||||
|
||||
soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llX", soc_uid);
|
||||
if (soc_uid[1])
|
||||
soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llX%016llX",
|
||||
soc_uid[1], soc_uid[0]);
|
||||
else
|
||||
soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%016llX",
|
||||
soc_uid[0]);
|
||||
if (!soc_dev_attr->serial_number)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -446,6 +446,46 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v1 = { 0, 0, 0 };
|
||||
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v2 = {
|
||||
.max_dram_nom_bw = 255,
|
||||
.max_dram_peak_bw = 255,
|
||||
.max_dram_hrt_bw = 1023,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp dvfsrc_opp_mt6893_lp4[] = {
|
||||
{ 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },
|
||||
{ 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 },
|
||||
{ 0, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 },
|
||||
{ 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 },
|
||||
{ 1, 4 }, { 2, 4 }, { 3, 4 }, { 2, 5 },
|
||||
{ 3, 5 }, { 3, 6 }, { 4, 6 }, { 4, 7 },
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp_desc dvfsrc_opp_mt6893_desc[] = {
|
||||
[0] = {
|
||||
.opps = dvfsrc_opp_mt6893_lp4,
|
||||
.num_opp = ARRAY_SIZE(dvfsrc_opp_mt6893_lp4),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct dvfsrc_soc_data mt6893_data = {
|
||||
.opps_desc = dvfsrc_opp_mt6893_desc,
|
||||
.regs = dvfsrc_mt8195_regs,
|
||||
.get_target_level = dvfsrc_get_target_level_v2,
|
||||
.get_current_level = dvfsrc_get_current_level_v2,
|
||||
.get_vcore_level = dvfsrc_get_vcore_level_v2,
|
||||
.get_vscp_level = dvfsrc_get_vscp_level_v2,
|
||||
.set_dram_bw = dvfsrc_set_dram_bw_v1,
|
||||
.set_dram_peak_bw = dvfsrc_set_dram_peak_bw_v1,
|
||||
.set_dram_hrt_bw = dvfsrc_set_dram_hrt_bw_v1,
|
||||
.set_vcore_level = dvfsrc_set_vcore_level_v2,
|
||||
.set_vscp_level = dvfsrc_set_vscp_level_v2,
|
||||
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
|
||||
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
|
||||
.bw_constraints = &dvfsrc_bw_constr_v2,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp dvfsrc_opp_mt8183_lp4[] = {
|
||||
{ 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 2 },
|
||||
};
|
||||
@ -469,8 +509,6 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8183_desc[] = {
|
||||
}
|
||||
};
|
||||
|
||||
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_mt8183 = { 0, 0, 0 };
|
||||
|
||||
static const struct dvfsrc_soc_data mt8183_data = {
|
||||
.opps_desc = dvfsrc_opp_mt8183_desc,
|
||||
.regs = dvfsrc_mt8183_regs,
|
||||
@ -482,7 +520,7 @@ static const struct dvfsrc_soc_data mt8183_data = {
|
||||
.set_vcore_level = dvfsrc_set_vcore_level_v1,
|
||||
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v1,
|
||||
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
|
||||
.bw_constraints = &dvfsrc_bw_constr_mt8183,
|
||||
.bw_constraints = &dvfsrc_bw_constr_v1,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_opp dvfsrc_opp_mt8195_lp4[] = {
|
||||
@ -501,12 +539,6 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8195_desc[] = {
|
||||
}
|
||||
};
|
||||
|
||||
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_mt8195 = {
|
||||
.max_dram_nom_bw = 255,
|
||||
.max_dram_peak_bw = 255,
|
||||
.max_dram_hrt_bw = 1023,
|
||||
};
|
||||
|
||||
static const struct dvfsrc_soc_data mt8195_data = {
|
||||
.opps_desc = dvfsrc_opp_mt8195_desc,
|
||||
.regs = dvfsrc_mt8195_regs,
|
||||
@ -521,10 +553,11 @@ static const struct dvfsrc_soc_data mt8195_data = {
|
||||
.set_vscp_level = dvfsrc_set_vscp_level_v2,
|
||||
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
|
||||
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
|
||||
.bw_constraints = &dvfsrc_bw_constr_mt8195,
|
||||
.bw_constraints = &dvfsrc_bw_constr_v2,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_dvfsrc_of_match[] = {
|
||||
{ .compatible = "mediatek,mt6893-dvfsrc", .data = &mt6893_data },
|
||||
{ .compatible = "mediatek,mt8183-dvfsrc", .data = &mt8183_data },
|
||||
{ .compatible = "mediatek,mt8195-dvfsrc", .data = &mt8195_data },
|
||||
{ /* sentinel */ }
|
||||
|
@ -35,6 +35,11 @@
|
||||
#define ATTR0_RES_WAYS_MASK GENMASK(15, 0)
|
||||
#define ATTR0_BONUS_WAYS_MASK GENMASK(31, 16)
|
||||
#define ATTR0_BONUS_WAYS_SHIFT 16
|
||||
#define ATTR2_PROBE_TARGET_WAYS_MASK BIT(4)
|
||||
#define ATTR2_FIXED_SIZE_MASK BIT(8)
|
||||
#define ATTR2_PRIORITY_MASK GENMASK(14, 12)
|
||||
#define ATTR2_PARENT_SCID_MASK GENMASK(21, 16)
|
||||
#define ATTR2_IN_A_GROUP_MASK BIT(24)
|
||||
#define LLCC_STATUS_READ_DELAY 100
|
||||
|
||||
#define CACHE_LINE_SIZE_SHIFT 6
|
||||
@ -49,6 +54,10 @@
|
||||
#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + SZ_8 * n)
|
||||
#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + SZ_8 * n)
|
||||
#define LLCC_TRP_ATTR2_CFGn(n) (0x21100 + SZ_4 * n)
|
||||
#define LLCC_V6_TRP_ATTR0_CFGn(n) (cfg->reg_offset[LLCC_TRP_ATTR0_CFG] + SZ_64 * (n))
|
||||
#define LLCC_V6_TRP_ATTR1_CFGn(n) (cfg->reg_offset[LLCC_TRP_ATTR1_CFG] + SZ_64 * (n))
|
||||
#define LLCC_V6_TRP_ATTR2_CFGn(n) (cfg->reg_offset[LLCC_TRP_ATTR2_CFG] + SZ_64 * (n))
|
||||
#define LLCC_V6_TRP_ATTR3_CFGn(n) (cfg->reg_offset[LLCC_TRP_ATTR3_CFG] + SZ_64 * (n))
|
||||
|
||||
#define LLCC_TRP_SCID_DIS_CAP_ALLOC 0x21f00
|
||||
#define LLCC_TRP_PCB_ACT 0x21f04
|
||||
@ -66,6 +75,7 @@
|
||||
#define LLCC_VERSION_2_0_0_0 0x02000000
|
||||
#define LLCC_VERSION_2_1_0_0 0x02010000
|
||||
#define LLCC_VERSION_4_1_0_0 0x04010000
|
||||
#define LLCC_VERSION_6_0_0_0 0X06000000
|
||||
|
||||
/**
|
||||
* struct llcc_slice_config - Data associated with the llcc slice
|
||||
@ -106,6 +116,7 @@
|
||||
* ovcap_en.
|
||||
* @vict_prio: When current scid is under-capacity, allocate over other
|
||||
* lower-than victim priority-line threshold scid.
|
||||
* @parent_slice_id: For grouped slices, specifies the slice id of the parent.
|
||||
*/
|
||||
struct llcc_slice_config {
|
||||
u32 usecase_id;
|
||||
@ -130,6 +141,7 @@ struct llcc_slice_config {
|
||||
bool ovcap_en;
|
||||
bool ovcap_prio;
|
||||
bool vict_prio;
|
||||
u32 parent_slice_id;
|
||||
};
|
||||
|
||||
struct qcom_llcc_config {
|
||||
@ -153,6 +165,21 @@ struct qcom_sct_config {
|
||||
enum llcc_reg_offset {
|
||||
LLCC_COMMON_HW_INFO,
|
||||
LLCC_COMMON_STATUS0,
|
||||
LLCC_TRP_ATTR0_CFG,
|
||||
LLCC_TRP_ATTR1_CFG,
|
||||
LLCC_TRP_ATTR2_CFG,
|
||||
LLCC_TRP_ATTR3_CFG,
|
||||
LLCC_TRP_SID_DIS_CAP_ALLOC,
|
||||
LLCC_TRP_ALGO_STALE_EN,
|
||||
LLCC_TRP_ALGO_STALE_CAP_EN,
|
||||
LLCC_TRP_ALGO_MRU0,
|
||||
LLCC_TRP_ALGO_MRU1,
|
||||
LLCC_TRP_ALGO_ALLOC0,
|
||||
LLCC_TRP_ALGO_ALLOC1,
|
||||
LLCC_TRP_ALGO_ALLOC2,
|
||||
LLCC_TRP_ALGO_ALLOC3,
|
||||
LLCC_TRP_WRS_EN,
|
||||
LLCC_TRP_WRS_CACHEABLE_EN,
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config ipq5424_data[] = {
|
||||
@ -2662,6 +2689,263 @@ static const struct llcc_slice_config sm8650_data[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config sm8750_data[] = {
|
||||
{
|
||||
.usecase_id = LLCC_CPUSS,
|
||||
.slice_id = 1,
|
||||
.max_cap = 5120,
|
||||
.priority = 1,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.activate_on_init = true,
|
||||
.write_scid_en = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_MDMHPFX,
|
||||
.slice_id = 24,
|
||||
.max_cap = 1024,
|
||||
.priority = 5,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDSC0,
|
||||
.slice_id = 2,
|
||||
.max_cap = 512,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_AUDIO,
|
||||
.slice_id = 35,
|
||||
.max_cap = 512,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_MDMHPGRW,
|
||||
.slice_id = 25,
|
||||
.max_cap = 1024,
|
||||
.priority = 5,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_MODHW,
|
||||
.slice_id = 26,
|
||||
.max_cap = 1024,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_CMPT,
|
||||
.slice_id = 34,
|
||||
.max_cap = 4096,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_GPUHTW,
|
||||
.slice_id = 11,
|
||||
.max_cap = 512,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_GPU,
|
||||
.slice_id = 9,
|
||||
.max_cap = 5632,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.write_scid_en = true,
|
||||
.write_scid_cacheable_en = true
|
||||
}, {
|
||||
.usecase_id = LLCC_MMUHWT,
|
||||
.slice_id = 18,
|
||||
.max_cap = 768,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_DISP,
|
||||
.slice_id = 16,
|
||||
.max_cap = 7168,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.cache_mode = 2,
|
||||
.stale_en = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDFW,
|
||||
.slice_id = 17,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_CAMFW,
|
||||
.slice_id = 20,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_MDMPNG,
|
||||
.slice_id = 27,
|
||||
.max_cap = 256,
|
||||
.priority = 5,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xf0000000,
|
||||
}, {
|
||||
.usecase_id = LLCC_AUDHW,
|
||||
.slice_id = 22,
|
||||
.max_cap = 512,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_CVP,
|
||||
.slice_id = 8,
|
||||
.max_cap = 800,
|
||||
.priority = 5,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.vict_prio = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_MODPE,
|
||||
.slice_id = 29,
|
||||
.max_cap = 256,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xf0000000,
|
||||
.alloc_oneway_en = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_WRCACHE,
|
||||
.slice_id = 31,
|
||||
.max_cap = 512,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.activate_on_init = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_CVPFW,
|
||||
.slice_id = 19,
|
||||
.max_cap = 64,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_CMPTHCP,
|
||||
.slice_id = 15,
|
||||
.max_cap = 256,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_LCPDARE,
|
||||
.slice_id = 30,
|
||||
.max_cap = 128,
|
||||
.priority = 5,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.activate_on_init = true,
|
||||
.alloc_oneway_en = true,
|
||||
}, {
|
||||
.usecase_id = LLCC_AENPU,
|
||||
.slice_id = 3,
|
||||
.max_cap = 3072,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.cache_mode = 2,
|
||||
}, {
|
||||
.usecase_id = LLCC_ISLAND1,
|
||||
.slice_id = 12,
|
||||
.max_cap = 7936,
|
||||
.priority = 7,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0x7fffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_DISP_WB,
|
||||
.slice_id = 23,
|
||||
.max_cap = 512,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDVSP,
|
||||
.slice_id = 4,
|
||||
.max_cap = 256,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
}, {
|
||||
.usecase_id = LLCC_VIDDEC,
|
||||
.slice_id = 5,
|
||||
.max_cap = 6144,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.cache_mode = 2,
|
||||
.ovcap_prio = true,
|
||||
.parent_slice_id = 33,
|
||||
}, {
|
||||
.usecase_id = LLCC_CAMOFE,
|
||||
.slice_id = 33,
|
||||
.max_cap = 6144,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.stale_en = true,
|
||||
.ovcap_prio = true,
|
||||
.parent_slice_id = 33,
|
||||
}, {
|
||||
.usecase_id = LLCC_CAMRTIP,
|
||||
.slice_id = 13,
|
||||
.max_cap = 1024,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.stale_en = true,
|
||||
.ovcap_prio = true,
|
||||
.parent_slice_id = 33,
|
||||
}, {
|
||||
.usecase_id = LLCC_CAMSRTIP,
|
||||
.slice_id = 14,
|
||||
.max_cap = 6144,
|
||||
.priority = 4,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.stale_en = true,
|
||||
.ovcap_prio = true,
|
||||
.parent_slice_id = 33,
|
||||
}, {
|
||||
.usecase_id = LLCC_CAMRTRF,
|
||||
.slice_id = 7,
|
||||
.max_cap = 3584,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.stale_en = true,
|
||||
.ovcap_prio = true,
|
||||
.parent_slice_id = 33,
|
||||
}, {
|
||||
.usecase_id = LLCC_CAMSRTRF,
|
||||
.slice_id = 21,
|
||||
.max_cap = 6144,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.stale_en = true,
|
||||
.ovcap_prio = true,
|
||||
.parent_slice_id = 33,
|
||||
}, {
|
||||
.usecase_id = LLCC_CPUSSMPAM,
|
||||
.slice_id = 6,
|
||||
.max_cap = 2048,
|
||||
.priority = 1,
|
||||
.fixed_size = true,
|
||||
.bonus_ways = 0xffffffff,
|
||||
.activate_on_init = true,
|
||||
.write_scid_en = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config qcs615_data[] = {
|
||||
{
|
||||
.usecase_id = LLCC_CPUSS,
|
||||
@ -3161,6 +3445,33 @@ static const struct llcc_edac_reg_offset llcc_v2_1_edac_reg_offset = {
|
||||
.drp_ecc_db_err_syn0 = 0x52120,
|
||||
};
|
||||
|
||||
static const struct llcc_edac_reg_offset llcc_v6_edac_reg_offset = {
|
||||
.trp_ecc_error_status0 = 0x47448,
|
||||
.trp_ecc_error_status1 = 0x47450,
|
||||
.trp_ecc_sb_err_syn0 = 0x47490,
|
||||
.trp_ecc_db_err_syn0 = 0x474d0,
|
||||
.trp_ecc_error_cntr_clear = 0x47444,
|
||||
.trp_interrupt_0_status = 0x47600,
|
||||
.trp_interrupt_0_clear = 0x47604,
|
||||
.trp_interrupt_0_enable = 0x47608,
|
||||
|
||||
/* LLCC Common registers */
|
||||
.cmn_status0 = 0x6400c,
|
||||
.cmn_interrupt_0_enable = 0x6401c,
|
||||
.cmn_interrupt_2_enable = 0x6403c,
|
||||
|
||||
/* LLCC DRP registers */
|
||||
.drp_ecc_error_cfg = 0x80000,
|
||||
.drp_ecc_error_cntr_clear = 0x80004,
|
||||
.drp_interrupt_status = 0x80020,
|
||||
.drp_interrupt_clear = 0x80028,
|
||||
.drp_interrupt_enable = 0x8002c,
|
||||
.drp_ecc_error_status0 = 0x820f4,
|
||||
.drp_ecc_error_status1 = 0x820f8,
|
||||
.drp_ecc_sb_err_syn0 = 0x820fc,
|
||||
.drp_ecc_db_err_syn0 = 0x82120,
|
||||
};
|
||||
|
||||
/* LLCC register offset starting from v1.0.0 */
|
||||
static const u32 llcc_v1_reg_offset[] = {
|
||||
[LLCC_COMMON_HW_INFO] = 0x00030000,
|
||||
@ -3173,6 +3484,27 @@ static const u32 llcc_v2_1_reg_offset[] = {
|
||||
[LLCC_COMMON_STATUS0] = 0x0003400c,
|
||||
};
|
||||
|
||||
/* LLCC register offset starting from v6.0.0 */
|
||||
static const u32 llcc_v6_reg_offset[] = {
|
||||
[LLCC_COMMON_HW_INFO] = 0x00064000,
|
||||
[LLCC_COMMON_STATUS0] = 0x0006400c,
|
||||
[LLCC_TRP_ATTR0_CFG] = 0x00041000,
|
||||
[LLCC_TRP_ATTR1_CFG] = 0x00041008,
|
||||
[LLCC_TRP_ATTR2_CFG] = 0x00041010,
|
||||
[LLCC_TRP_ATTR3_CFG] = 0x00041014,
|
||||
[LLCC_TRP_SID_DIS_CAP_ALLOC] = 0x00042000,
|
||||
[LLCC_TRP_ALGO_STALE_EN] = 0x00042008,
|
||||
[LLCC_TRP_ALGO_STALE_CAP_EN] = 0x00042010,
|
||||
[LLCC_TRP_ALGO_MRU0] = 0x00042018,
|
||||
[LLCC_TRP_ALGO_MRU1] = 0x00042020,
|
||||
[LLCC_TRP_ALGO_ALLOC0] = 0x00042028,
|
||||
[LLCC_TRP_ALGO_ALLOC1] = 0x00042030,
|
||||
[LLCC_TRP_ALGO_ALLOC2] = 0x00042038,
|
||||
[LLCC_TRP_ALGO_ALLOC3] = 0x00042040,
|
||||
[LLCC_TRP_WRS_EN] = 0x00042080,
|
||||
[LLCC_TRP_WRS_CACHEABLE_EN] = 0x00042088,
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config qcs615_cfg[] = {
|
||||
{
|
||||
.sct_data = qcs615_data,
|
||||
@ -3379,6 +3711,16 @@ static const struct qcom_llcc_config sm8650_cfg[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm8750_cfg[] = {
|
||||
{
|
||||
.sct_data = sm8750_data,
|
||||
.size = ARRAY_SIZE(sm8750_data),
|
||||
.skip_llcc_cfg = false,
|
||||
.reg_offset = llcc_v6_reg_offset,
|
||||
.edac_reg_offset = &llcc_v6_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config x1e80100_cfg[] = {
|
||||
{
|
||||
.sct_data = x1e80100_data,
|
||||
@ -3489,6 +3831,11 @@ static const struct qcom_sct_config sm8650_cfgs = {
|
||||
.num_config = ARRAY_SIZE(sm8650_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm8750_cfgs = {
|
||||
.llcc_config = sm8750_cfg,
|
||||
.num_config = ARRAY_SIZE(sm8750_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config x1e80100_cfgs = {
|
||||
.llcc_config = x1e80100_cfg,
|
||||
.num_config = ARRAY_SIZE(x1e80100_cfg),
|
||||
@ -3869,6 +4216,139 @@ static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _qcom_llcc_cfg_program_v6(const struct llcc_slice_config *config,
|
||||
const struct qcom_llcc_config *cfg)
|
||||
{
|
||||
u32 stale_en, stale_cap_en, mru_uncap_en, mru_rollover;
|
||||
u32 alloc_oneway_en, ovcap_en, ovcap_prio, vict_prio;
|
||||
u32 attr0_cfg, attr1_cfg, attr2_cfg, attr3_cfg;
|
||||
u32 attr0_val, attr1_val, attr2_val, attr3_val;
|
||||
u32 slice_offset, reg_offset;
|
||||
struct llcc_slice_desc *desc;
|
||||
u32 wren, wr_cache_en;
|
||||
int ret;
|
||||
|
||||
attr0_cfg = LLCC_V6_TRP_ATTR0_CFGn(config->slice_id);
|
||||
attr1_cfg = LLCC_V6_TRP_ATTR1_CFGn(config->slice_id);
|
||||
attr2_cfg = LLCC_V6_TRP_ATTR2_CFGn(config->slice_id);
|
||||
attr3_cfg = LLCC_V6_TRP_ATTR3_CFGn(config->slice_id);
|
||||
|
||||
attr0_val = config->res_ways;
|
||||
attr1_val = config->bonus_ways;
|
||||
attr2_val = config->cache_mode;
|
||||
attr2_val |= FIELD_PREP(ATTR2_PROBE_TARGET_WAYS_MASK, config->probe_target_ways);
|
||||
attr2_val |= FIELD_PREP(ATTR2_FIXED_SIZE_MASK, config->fixed_size);
|
||||
attr2_val |= FIELD_PREP(ATTR2_PRIORITY_MASK, config->priority);
|
||||
|
||||
if (config->parent_slice_id && config->fixed_size) {
|
||||
attr2_val |= FIELD_PREP(ATTR2_PARENT_SCID_MASK, config->parent_slice_id);
|
||||
attr2_val |= ATTR2_IN_A_GROUP_MASK;
|
||||
}
|
||||
|
||||
attr3_val = MAX_CAP_TO_BYTES(config->max_cap);
|
||||
attr3_val /= drv_data->num_banks;
|
||||
attr3_val >>= CACHE_LINE_SIZE_SHIFT;
|
||||
|
||||
ret = regmap_write(drv_data->bcast_regmap, attr0_cfg, attr0_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(drv_data->bcast_regmap, attr1_cfg, attr1_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(drv_data->bcast_regmap, attr2_cfg, attr2_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(drv_data->bcast_regmap, attr3_cfg, attr3_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
slice_offset = config->slice_id % 32;
|
||||
reg_offset = (config->slice_id / 32) * 4;
|
||||
|
||||
wren = config->write_scid_en << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_WRS_EN] + reg_offset,
|
||||
BIT(slice_offset), wren);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wr_cache_en = config->write_scid_cacheable_en << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_WRS_CACHEABLE_EN] + reg_offset,
|
||||
BIT(slice_offset), wr_cache_en);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stale_en = config->stale_en << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_STALE_EN] + reg_offset,
|
||||
BIT(slice_offset), stale_en);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stale_cap_en = config->stale_cap_en << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_STALE_CAP_EN] + reg_offset,
|
||||
BIT(slice_offset), stale_cap_en);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mru_uncap_en = config->mru_uncap_en << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_MRU0] + reg_offset,
|
||||
BIT(slice_offset), mru_uncap_en);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mru_rollover = config->mru_rollover << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_MRU1] + reg_offset,
|
||||
BIT(slice_offset), mru_rollover);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alloc_oneway_en = config->alloc_oneway_en << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_ALLOC0] + reg_offset,
|
||||
BIT(slice_offset), alloc_oneway_en);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ovcap_en = config->ovcap_en << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_ALLOC1] + reg_offset,
|
||||
BIT(slice_offset), ovcap_en);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ovcap_prio = config->ovcap_prio << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_ALLOC2] + reg_offset,
|
||||
BIT(slice_offset), ovcap_prio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vict_prio = config->vict_prio << slice_offset;
|
||||
ret = regmap_update_bits(drv_data->bcast_regmap,
|
||||
cfg->reg_offset[LLCC_TRP_ALGO_ALLOC3] + reg_offset,
|
||||
BIT(slice_offset), vict_prio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (config->activate_on_init) {
|
||||
desc = llcc_slice_getd(config->usecase_id);
|
||||
if (PTR_ERR_OR_ZERO(desc))
|
||||
return -EINVAL;
|
||||
|
||||
ret = llcc_slice_activate(desc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_llcc_cfg_program(struct platform_device *pdev,
|
||||
const struct qcom_llcc_config *cfg)
|
||||
{
|
||||
@ -3880,10 +4360,18 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev,
|
||||
sz = drv_data->cfg_size;
|
||||
llcc_table = drv_data->cfg;
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
ret = _qcom_llcc_cfg_program(&llcc_table[i], cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (drv_data->version >= LLCC_VERSION_6_0_0_0) {
|
||||
for (i = 0; i < sz; i++) {
|
||||
ret = _qcom_llcc_cfg_program_v6(&llcc_table[i], cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < sz; i++) {
|
||||
ret = _qcom_llcc_cfg_program(&llcc_table[i], cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -4102,6 +4590,7 @@ static const struct of_device_id qcom_llcc_of_match[] = {
|
||||
{ .compatible = "qcom,sm8450-llcc", .data = &sm8450_cfgs },
|
||||
{ .compatible = "qcom,sm8550-llcc", .data = &sm8550_cfgs },
|
||||
{ .compatible = "qcom,sm8650-llcc", .data = &sm8650_cfgs },
|
||||
{ .compatible = "qcom,sm8750-llcc", .data = &sm8750_cfgs },
|
||||
{ .compatible = "qcom,x1e80100-llcc", .data = &x1e80100_cfgs },
|
||||
{ }
|
||||
};
|
||||
|
@ -371,15 +371,11 @@ static void pmic_glink_remove(struct platform_device *pdev)
|
||||
__pmic_glink = NULL;
|
||||
}
|
||||
|
||||
static const unsigned long pmic_glink_sc8280xp_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |
|
||||
BIT(PMIC_GLINK_CLIENT_ALTMODE);
|
||||
|
||||
static const unsigned long pmic_glink_sm8450_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |
|
||||
BIT(PMIC_GLINK_CLIENT_ALTMODE) |
|
||||
BIT(PMIC_GLINK_CLIENT_UCSI);
|
||||
|
||||
static const struct of_device_id pmic_glink_of_match[] = {
|
||||
{ .compatible = "qcom,sc8280xp-pmic-glink", .data = &pmic_glink_sc8280xp_client_mask },
|
||||
{ .compatible = "qcom,pmic-glink", .data = &pmic_glink_sm8450_client_mask },
|
||||
{}
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user