2
0
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:
Linus Torvalds 2025-05-31 07:53:30 -07:00
commit 297d9111e9
136 changed files with 6466 additions and 716 deletions

View File

@ -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

View File

@ -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.
======= ===========================================================

View File

@ -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

View File

@ -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>;

View File

@ -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>;
};

View 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>;
};

View File

@ -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>;
};

View 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>;
};

View File

@ -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:

View File

@ -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:

View File

@ -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;
};
};
};
};

View File

@ -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

View File

@ -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>;
};

View File

@ -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

View File

@ -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

View File

@ -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>;
};
};

View File

@ -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

View File

@ -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

View File

@ -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>;
};

View File

@ -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

View File

@ -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>;
};
};

View File

@ -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

View File

@ -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>;
};

View File

@ -23,6 +23,7 @@ properties:
compatible:
oneOf:
- enum:
- mediatek,mt6893-dvfsrc
- mediatek,mt8183-dvfsrc
- mediatek,mt8195-dvfsrc
- items:

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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";
};

View File

@ -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

View File

@ -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

View File

@ -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,
},
};

View File

@ -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
*/

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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"))

View File

@ -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",

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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; \

View File

@ -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;

View File

@ -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,

View 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;
}
}
}

View 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 */

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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

View 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");

View 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");

View File

@ -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
================================================================

View File

@ -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

View File

@ -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

View 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");

View 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");

View File

@ -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" },

View File

@ -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;

View File

@ -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
};

View File

@ -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)

View File

@ -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,

View File

@ -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;
}

View File

@ -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");
/*

View File

@ -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"

View File

@ -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/

View File

@ -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,

View File

@ -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},

View File

@ -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
View 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");

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View 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;
}

View File

@ -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");

View File

@ -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);

View 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");

View File

@ -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)

View File

@ -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

View 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");

View 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");

View File

@ -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

View File

@ -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/

View File

@ -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 */ }
};

View File

@ -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;
}

View File

@ -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,

View File

@ -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 */

View File

@ -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[] = {

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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 */ }

View File

@ -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 },
{ }
};

View File

@ -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