soc: drivers for 6.10

As usual, these are updates for drivers that are specific to certain
 SoCs or firmware running on them. Notable updates include
 
  - The new STMicroelectronics STM32 "firewall" bus driver that is
    used to provide a barrier between different parts of an SoC
 
  - Lots of updates for the Qualcomm platform drivers, in particular
    SCM, which gets a rewrite of its initialization code
 
  - Firmware driver updates for Arm FF-A notification interrupts
    and indirect messaging, SCMI firmware support for pin control
    and vendor specific interfaces, and TEE firmware interface
    changes across multiple TEE drivers
 
  - A larger cleanup of the Mediatek CMDQ driver and some related bits
 
  - Kconfig changes for riscv drivers to prepare for adding Kanaan
    k230 support
 
  - Multiple minor updates for the TI sysc bus driver, memory controllers,
    hisilicon hccs and more
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmY+dbEACgkQYKtH/8kJ
 UifGTBAA3lh2qw++S5i6nk71388/nswb5fZKwqPKl1m+44SndE7r0/nauGm7IZhd
 oM5xiBZzsoYCKuesSuejkBNgPmUPtUhyHBJKSKjwrcak4k1mrjDgXxfSxCqGptVZ
 Ps683koJ/Ic7O/LQNxlVzUlssG/3gmhJELfpaVIB7rG8pmdgF9ocM73+iJrRwW1Q
 fTFXUXeCcXJ2N5Yki7z2+4oB3RebPzTBz4NeIYNdGQj5/u61oG0KzXwvk8eqWhNb
 0KJYsfAQZGzdyAys6XU1MHv4T4L2a3DQL6NMgLnovVEMhP2Hk0XlBmI7X+uAXYiM
 2z289d9Wx3HMoiekulDJ+rpDUPxPXrEqaRkfWZ8G+HSY4KcIeSP7YGmhylr0kdvw
 +Qo6orxZ9lkSPaT1aUkNIIywDzet/E2hY8zV1EcLBu9GWjkybAvT/Uy2lSSN+LLH
 yEQyDf+s90N6QuZwdXN8a3QliP39tHqlye8wou6UQG8aZ7z870fKAKlvA6DjTfPM
 JyhY1rXYH/bvC87sVTi5Qb09+2R6ftvk5xijiMOyXugPpO/6PQKULVataeUnzwgs
 YTgOPhaqXVadDR/nkrG3FzEtvpYeTspwGpDiEpDrNHf5H1tFg6VfPNS8y0QOlSPY
 JcmylQNCtwxCRLTw2NHOb3tLcY4ruDHNmrWf5INTzf6cJe49jaU=
 =4rf0
 -----END PGP SIGNATURE-----

Merge tag 'soc-drivers-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc

Pull SoC driver updates from Arnd Bergmann:
 "As usual, these are updates for drivers that are specific to certain
  SoCs or firmware running on them.

  Notable updates include

   - The new STMicroelectronics STM32 "firewall" bus driver that is used
     to provide a barrier between different parts of an SoC

   - Lots of updates for the Qualcomm platform drivers, in particular
     SCM, which gets a rewrite of its initialization code

   - Firmware driver updates for Arm FF-A notification interrupts and
     indirect messaging, SCMI firmware support for pin control and
     vendor specific interfaces, and TEE firmware interface changes
     across multiple TEE drivers

   - A larger cleanup of the Mediatek CMDQ driver and some related bits

   - Kconfig changes for riscv drivers to prepare for adding Kanaan k230
     support

   - Multiple minor updates for the TI sysc bus driver, memory
     controllers, hisilicon hccs and more"

* tag 'soc-drivers-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (103 commits)
  firmware: qcom: uefisecapp: Allow on sc8180x Primus and Flex 5G
  soc: qcom: pmic_glink: Make client-lock non-sleeping
  dt-bindings: soc: qcom,wcnss: fix bluetooth address example
  soc/tegra: pmc: Add EQOS wake event for Tegra194 and Tegra234
  bus: stm32_firewall: fix off by one in stm32_firewall_get_firewall()
  bus: etzpc: introduce ETZPC firewall controller driver
  firmware: arm_ffa: Avoid queuing work when running on the worker queue
  bus: ti-sysc: Drop legacy idle quirk handling
  bus: ti-sysc: Drop legacy quirk handling for smartreflex
  bus: ti-sysc: Drop legacy quirk handling for uarts
  bus: ti-sysc: Add a description and copyrights
  bus: ti-sysc: Move check for no-reset-on-init
  soc: hisilicon: kunpeng_hccs: replace MAILBOX dependency with PCC
  soc: hisilicon: kunpeng_hccs: Add the check for obtaining complete port attribute
  firmware: arm_ffa: Fix memory corruption in ffa_msg_send2()
  bus: rifsc: introduce RIFSC firewall controller driver
  of: property: fw_devlink: Add support for "access-controller"
  soc: mediatek: mtk-socinfo: Correct the marketing name for MT8188GV
  soc: mediatek: mtk-socinfo: Add entry for MT8395AV/ZA Genio 1200
  soc: mediatek: mtk-mutex: Add support for MT8188 VPPSYS
  ...
This commit is contained in:
Linus Torvalds 2024-05-13 08:48:42 -07:00
commit 14a60290ed
125 changed files with 5139 additions and 842 deletions

View File

@ -0,0 +1,84 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/access-controllers/access-controllers.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic Domain Access Controllers
maintainers:
- Oleksii Moisieiev <oleksii_moisieiev@epam.com>
description: |+
Common access controllers properties
Access controllers are in charge of stating which of the hardware blocks under
their responsibility (their domain) can be accesssed by which compartment. A
compartment can be a cluster of CPUs (or coprocessors), a range of addresses
or a group of hardware blocks. An access controller's domain is the set of
resources covered by the access controller.
This device tree binding can be used to bind devices to their access
controller provided by access-controllers property. In this case, the device
is a consumer and the access controller is the provider.
An access controller can be represented by any node in the device tree and
can provide one or more configuration parameters, needed to control parameters
of the consumer device. A consumer node can refer to the provider by phandle
and a set of phandle arguments, specified by '#access-controller-cells'
property in the access controller node.
Access controllers are typically used to set/read the permissions of a
hardware block and grant access to it. Any of which depends on the access
controller. The capabilities of each access controller are defined by the
binding of the access controller device.
Each node can be a consumer for the several access controllers.
# always select the core schema
select: true
properties:
"#access-controller-cells":
description:
Number of cells in an access-controllers specifier;
Can be any value as specified by device tree binding documentation
of a particular provider. The node is an access controller.
access-controller-names:
$ref: /schemas/types.yaml#/definitions/string-array
description:
A list of access-controllers names, sorted in the same order as
access-controllers entries. Consumer drivers will use
access-controller-names to match with existing access-controllers entries.
access-controllers:
$ref: /schemas/types.yaml#/definitions/phandle-array
description:
A list of access controller specifiers, as defined by the
bindings of the access-controllers provider.
additionalProperties: true
examples:
- |
clock_controller: access-controllers@50000 {
reg = <0x50000 0x400>;
#access-controller-cells = <2>;
};
bus_controller: bus@60000 {
reg = <0x60000 0x10000>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
#access-controller-cells = <3>;
uart4: serial@60100 {
reg = <0x60100 0x400>;
clocks = <&clk_serial>;
access-controllers = <&clock_controller 1 2>,
<&bus_controller 1 3 5>;
access-controller-names = "clock", "bus";
};
};

View File

@ -0,0 +1,96 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/bus/st,stm32-etzpc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STM32 Extended TrustZone protection controller
description: |
The ETZPC configures TrustZone security in a SoC having bus masters and
devices with programmable-security attributes (securable resources).
maintainers:
- Gatien Chevallier <gatien.chevallier@foss.st.com>
select:
properties:
compatible:
contains:
const: st,stm32-etzpc
required:
- compatible
properties:
compatible:
items:
- const: st,stm32-etzpc
- const: simple-bus
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 1
ranges: true
"#access-controller-cells":
const: 1
description:
Contains the firewall ID associated to the peripheral.
patternProperties:
"^.*@[0-9a-f]+$":
description: Peripherals
type: object
additionalProperties: true
required:
- access-controllers
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
- "#access-controller-cells"
- ranges
additionalProperties: false
examples:
- |
// In this example, the usart2 device refers to rifsc as its access
// controller.
// Access rights are verified before creating devices.
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/stm32mp13-clks.h>
#include <dt-bindings/reset/stm32mp13-resets.h>
etzpc: bus@5c007000 {
compatible = "st,stm32-etzpc", "simple-bus";
reg = <0x5c007000 0x400>;
#address-cells = <1>;
#size-cells = <1>;
#access-controller-cells = <1>;
ranges;
usart2: serial@4c001000 {
compatible = "st,stm32h7-uart";
reg = <0x4c001000 0x400>;
interrupts-extended = <&exti 27 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc USART2_K>;
resets = <&rcc USART2_R>;
wakeup-source;
dmas = <&dmamux1 43 0x400 0x5>,
<&dmamux1 44 0x400 0x1>;
dma-names = "rx", "tx";
access-controllers = <&etzpc 17>;
};
};

View File

@ -0,0 +1,105 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/bus/st,stm32mp25-rifsc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STM32 Resource isolation framework security controller
maintainers:
- Gatien Chevallier <gatien.chevallier@foss.st.com>
description: |
Resource isolation framework (RIF) is a comprehensive set of hardware blocks
designed to enforce and manage isolation of STM32 hardware resources like
memory and peripherals.
The RIFSC (RIF security controller) is composed of three sets of registers,
each managing a specific set of hardware resources:
- RISC registers associated with RISUP logic (resource isolation device unit
for peripherals), assign all non-RIF aware peripherals to zero, one or
any security domains (secure, privilege, compartment).
- RIMC registers: associated with RIMU logic (resource isolation master
unit), assign all non RIF-aware bus master to one security domain by
setting secure, privileged and compartment information on the system bus.
Alternatively, the RISUP logic controlling the device port access to a
peripheral can assign target bus attributes to this peripheral master port
(supported attribute: CID).
- RISC registers associated with RISAL logic (resource isolation device unit
for address space - Lite version), assign address space subregions to one
security domains (secure, privilege, compartment).
select:
properties:
compatible:
contains:
const: st,stm32mp25-rifsc
required:
- compatible
properties:
compatible:
items:
- const: st,stm32mp25-rifsc
- const: simple-bus
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 1
ranges: true
"#access-controller-cells":
const: 1
description:
Contains the firewall ID associated to the peripheral.
patternProperties:
"^.*@[0-9a-f]+$":
description: Peripherals
type: object
additionalProperties: true
required:
- access-controllers
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
- "#access-controller-cells"
- ranges
additionalProperties: false
examples:
- |
// In this example, the usart2 device refers to rifsc as its domain
// controller.
// Access rights are verified before creating devices.
#include <dt-bindings/interrupt-controller/arm-gic.h>
rifsc: bus@42080000 {
compatible = "st,stm32mp25-rifsc", "simple-bus";
reg = <0x42080000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
#access-controller-cells = <1>;
ranges;
usart2: serial@400e0000 {
compatible = "st,stm32h7-uart";
reg = <0x400e0000 0x400>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ck_flexgen_08>;
access-controllers = <&rifsc 32>;
};
};

View File

@ -46,6 +46,10 @@ properties:
power-domains:
maxItems: 1
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -51,6 +51,10 @@ properties:
power-domains:
maxItems: 1
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -82,6 +82,10 @@ properties:
description: if defined, it indicates that the controller
supports memory-to-memory transfer
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -28,6 +28,10 @@ properties:
resets:
maxItems: 1
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -247,6 +247,37 @@ properties:
reg:
const: 0x18
protocol@19:
type: object
allOf:
- $ref: '#/$defs/protocol-node'
- $ref: /schemas/pinctrl/pinctrl.yaml
unevaluatedProperties: false
properties:
reg:
const: 0x19
patternProperties:
'-pins$':
type: object
allOf:
- $ref: /schemas/pinctrl/pincfg-node.yaml#
- $ref: /schemas/pinctrl/pinmux-node.yaml#
unevaluatedProperties: false
description:
A pin multiplexing sub-node describes how to configure a
set of pins in some desired function.
A single sub-node may define several pin configurations.
This sub-node is using the default pinctrl bindings to configure
pin multiplexing and using SCMI protocol to apply a specified
configuration.
required:
- reg
additionalProperties: false
$defs:
@ -355,7 +386,7 @@ examples:
scmi_dvfs: protocol@13 {
reg = <0x13>;
#clock-cells = <1>;
#power-domain-cells = <1>;
mboxes = <&mhuB 1 0>,
<&mhuB 1 1>;
@ -401,6 +432,25 @@ examples:
scmi_powercap: protocol@18 {
reg = <0x18>;
};
scmi_pinctrl: protocol@19 {
reg = <0x19>;
i2c2-pins {
groups = "g_i2c2_a", "g_i2c2_b";
function = "f_i2c2";
};
mdio-pins {
groups = "g_avb_mdio";
drive-strength = <24>;
};
keys_pins: keys-pins {
pins = "gpio_5_17", "gpio_5_20", "gpio_5_22", "gpio_2_1";
bias-pull-up;
};
};
};
};
@ -468,7 +518,7 @@ examples:
reg = <0x13>;
linaro,optee-channel-id = <1>;
shmem = <&cpu_optee_lpri0>;
#clock-cells = <1>;
#power-domain-cells = <1>;
};
scmi_clk0: protocol@14 {

View File

@ -127,6 +127,10 @@ properties:
wakeup-source: true
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -93,6 +93,10 @@ properties:
'#size-cells':
const: 0
access-controllers:
minItems: 1
maxItems: 2
allOf:
- if:
properties:

View File

@ -59,6 +59,10 @@ properties:
If not, SPI CLKOUT frequency will not be accurate.
maximum: 20000000
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -45,6 +45,10 @@ properties:
'#size-cells':
const: 0
access-controllers:
minItems: 1
maxItems: 2
additionalProperties: false
required:

View File

@ -29,6 +29,10 @@ properties:
- const: cec
- const: hdmi-cec
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -36,6 +36,10 @@ properties:
resets:
maxItems: 1
access-controllers:
minItems: 1
maxItems: 2
port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false

View File

@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/memory-controllers/samsung,s5pv210-dmc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5Pv210 SoC Dynamic Memory Controller
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
description:
Dynamic Memory Controller interfaces external JEDEC DDR-type SDRAM.
properties:
compatible:
const: samsung,s5pv210-dmc
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
memory-controller@f0000000 {
compatible = "samsung,s5pv210-dmc";
reg = <0xf0000000 0x1000>;
};

View File

@ -50,6 +50,10 @@ properties:
Reflects the memory layout with four integer values per bank. Format:
<bank-number> 0 <address of the bank> <size>
access-controllers:
minItems: 1
maxItems: 2
patternProperties:
"^.*@[0-4],[a-f0-9]+$":
additionalProperties: true

View File

@ -44,6 +44,10 @@ properties:
wakeup-source: true
access-controllers:
minItems: 1
maxItems: 2
pwm:
type: object
additionalProperties: false

View File

@ -67,6 +67,10 @@ properties:
"#size-cells":
const: 0
access-controllers:
minItems: 1
maxItems: 2
pwm:
type: object
additionalProperties: false

View File

@ -79,6 +79,10 @@ properties:
- const: rx
- const: tx
access-controllers:
minItems: 1
maxItems: 2
power-domains: true
resets:

View File

@ -118,6 +118,10 @@ properties:
phys:
maxItems: 1
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -93,6 +93,10 @@ properties:
select RCC clock instead of ETH_REF_CLK.
type: boolean
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- clocks

View File

@ -55,6 +55,10 @@ properties:
description: number of clock cells for ck_usbo_48m consumer
const: 0
access-controllers:
minItems: 1
maxItems: 2
# Required child nodes:
patternProperties:

View File

@ -30,6 +30,10 @@ properties:
vdda-supply:
description: phandle to the vdda input analog voltage.
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -37,6 +37,10 @@ properties:
description: If set, the RNG configuration in RNG_CR, RNG_HTCR and
RNG_NSCR will be locked.
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -73,6 +73,10 @@ properties:
enum: [1, 2, 4, 8, 12, 14, 16]
default: 8
access-controllers:
minItems: 1
maxItems: 2
allOf:
- $ref: rs485.yaml#
- $ref: serial.yaml#

View File

@ -116,8 +116,8 @@ examples:
bluetooth {
compatible = "qcom,wcnss-bt";
/* BD address 00:11:22:33:44:55 */
local-bd-address = [ 55 44 33 22 11 00 ];
/* Updated by boot firmware (little-endian order) */
local-bd-address = [ 00 00 00 00 00 00 ];
};
wifi {

View File

@ -65,6 +65,10 @@ properties:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- "#sound-dai-cells"

View File

@ -48,6 +48,10 @@ properties:
clock-names:
maxItems: 3
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -50,6 +50,10 @@ properties:
resets:
maxItems: 1
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- "#sound-dai-cells"

View File

@ -46,6 +46,10 @@ properties:
- const: tx
- const: rx
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -52,6 +52,10 @@ properties:
- const: rx
- const: tx
access-controllers:
minItems: 1
maxItems: 2
required:
- compatible
- reg

View File

@ -172,6 +172,10 @@ properties:
tpl-support: true
access-controllers:
minItems: 1
maxItems: 2
dependencies:
port: [ usb-role-switch ]
role-switch-default-mode: [ usb-role-switch ]

View File

@ -10,6 +10,7 @@ TEE Subsystem
tee
op-tee
amd-tee
ts-tee
.. only:: subproject and html

View File

@ -0,0 +1,71 @@
.. SPDX-License-Identifier: GPL-2.0
=================================
TS-TEE (Trusted Services project)
=================================
This driver provides access to secure services implemented by Trusted Services.
Trusted Services [1] is a TrustedFirmware.org project that provides a framework
for developing and deploying device Root of Trust services in FF-A [2] S-EL0
Secure Partitions. The project hosts the reference implementation of the Arm
Platform Security Architecture [3] for Arm A-profile devices.
The FF-A Secure Partitions (SP) are accessible through the FF-A driver [4] which
provides the low level communication for this driver. On top of that the Trusted
Services RPC protocol is used [5]. To use the driver from user space a reference
implementation is provided at [6], which is part of the Trusted Services client
library called libts [7].
All Trusted Services (TS) SPs have the same FF-A UUID; it identifies the TS RPC
protocol. A TS SP can host one or more services (e.g. PSA Crypto, PSA ITS, etc).
A service is identified by its service UUID; the same type of service cannot be
present twice in the same SP. During SP boot each service in the SP is assigned
an "interface ID". This is just a short ID to simplify message addressing.
The generic TEE design is to share memory at once with the Trusted OS, which can
then be reused to communicate with multiple applications running on the Trusted
OS. However, in case of FF-A, memory sharing works on an endpoint level, i.e.
memory is shared with a specific SP. User space has to be able to separately
share memory with each SP based on its endpoint ID; therefore a separate TEE
device is registered for each discovered TS SP. Opening the SP corresponds to
opening the TEE device and creating a TEE context. A TS SP hosts one or more
services. Opening a service corresponds to opening a session in the given
tee_context.
Overview of a system with Trusted Services components::
User space Kernel space Secure world
~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~
+--------+ +-------------+
| Client | | Trusted |
+--------+ | Services SP |
/\ +-------------+
|| /\
|| ||
|| ||
\/ \/
+-------+ +----------+--------+ +-------------+
| libts | | TEE | TS-TEE | | FF-A SPMC |
| | | subsys | driver | | + SPMD |
+-------+----------------+----+-----+--------+-----------+-------------+
| Generic TEE API | | FF-A | TS RPC protocol |
| IOCTL (TEE_IOC_*) | | driver | over FF-A |
+-----------------------------+ +--------+-------------------------+
References
==========
[1] https://www.trustedfirmware.org/projects/trusted-services/
[2] https://developer.arm.com/documentation/den0077/
[3] https://www.arm.com/architecture/security-features/platform-security
[4] drivers/firmware/arm_ffa/
[5] https://trusted-services.readthedocs.io/en/v1.0.0/developer/service-access-protocols.html#abi
[6] https://git.trustedfirmware.org/TS/trusted-services.git/tree/components/rpc/ts_rpc/caller/linux/ts_rpc_caller_linux.c?h=v1.0.0
[7] https://git.trustedfirmware.org/TS/trusted-services.git/tree/deployments/libts/arm-linux/CMakeLists.txt?h=v1.0.0

View File

@ -2585,12 +2585,8 @@ F: arch/arm64/boot/dts/qcom/sc7180*
F: arch/arm64/boot/dts/qcom/sc7280*
F: arch/arm64/boot/dts/qcom/sdm845-cheza*
ARM/QUALCOMM SUPPORT
M: Bjorn Andersson <andersson@kernel.org>
M: Konrad Dybcio <konrad.dybcio@linaro.org>
ARM/QUALCOMM MAILING LIST
L: linux-arm-msm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git
F: Documentation/devicetree/bindings/*/qcom*
F: Documentation/devicetree/bindings/soc/qcom/
F: arch/arm/boot/dts/qcom/
@ -2627,6 +2623,33 @@ F: include/dt-bindings/*/qcom*
F: include/linux/*/qcom*
F: include/linux/soc/qcom/
ARM/QUALCOMM SUPPORT
M: Bjorn Andersson <andersson@kernel.org>
M: Konrad Dybcio <konrad.dybcio@linaro.org>
L: linux-arm-msm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git
F: Documentation/devicetree/bindings/arm/qcom-soc.yaml
F: Documentation/devicetree/bindings/arm/qcom.yaml
F: Documentation/devicetree/bindings/bus/qcom*
F: Documentation/devicetree/bindings/cache/qcom,llcc.yaml
F: Documentation/devicetree/bindings/firmware/qcom,scm.yaml
F: Documentation/devicetree/bindings/reserved-memory/qcom
F: Documentation/devicetree/bindings/soc/qcom/
F: arch/arm/boot/dts/qcom/
F: arch/arm/configs/qcom_defconfig
F: arch/arm/mach-qcom/
F: arch/arm64/boot/dts/qcom/
F: drivers/bus/qcom*
F: drivers/firmware/qcom/
F: drivers/soc/qcom/
F: include/dt-bindings/arm/qcom,ids.h
F: include/dt-bindings/firmware/qcom,scm.h
F: include/dt-bindings/soc/qcom*
F: include/linux/firmware/qcom
F: include/linux/soc/qcom/
F: include/soc/qcom/
ARM/RDA MICRO ARCHITECTURE
M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@ -20833,6 +20856,13 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml
F: drivers/media/i2c/st-mipid02.c
ST STM32 FIREWALL
M: Gatien Chevallier <gatien.chevallier@foss.st.com>
S: Maintained
F: drivers/bus/stm32_etzpc.c
F: drivers/bus/stm32_firewall.c
F: drivers/bus/stm32_rifsc.c
ST STM32 I2C/SMBUS DRIVER
M: Pierre-Yves MORDRET <pierre-yves.mordret@foss.st.com>
M: Alain Volmat <alain.volmat@foss.st.com>
@ -21475,6 +21505,7 @@ F: drivers/cpufreq/sc[mp]i-cpufreq.c
F: drivers/firmware/arm_scmi/
F: drivers/firmware/arm_scpi.c
F: drivers/hwmon/scmi-hwmon.c
F: drivers/pinctrl/pinctrl-scmi.c
F: drivers/pmdomain/arm/
F: drivers/powercap/arm_scmi_powercap.c
F: drivers/regulator/scmi-regulator.c
@ -21706,6 +21737,7 @@ F: Documentation/driver-api/tee.rst
F: Documentation/tee/
F: Documentation/userspace-api/tee.rst
F: drivers/tee/
F: include/linux/tee_core.h
F: include/linux/tee_drv.h
F: include/uapi/linux/tee.h
@ -22499,6 +22531,15 @@ F: Documentation/ABI/testing/configfs-tsm
F: drivers/virt/coco/tsm.c
F: include/linux/tsm.h
TRUSTED SERVICES TEE DRIVER
M: Balint Dobszay <balint.dobszay@arm.com>
M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: trusted-services@lists.trustedfirmware.org
S: Maintained
F: Documentation/tee/ts-tee.rst
F: drivers/tee/tstee/
TTY LAYER AND SERIAL DRIVERS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
M: Jiri Slaby <jirislaby@kernel.org>

View File

@ -12,6 +12,7 @@ menuconfig ARCH_STM32
select PINCTRL
select RESET_CONTROLLER
select STM32_EXTI
select STM32_FIREWALL
help
Support for STMicroelectronics STM32 processors.

View File

@ -305,6 +305,7 @@ config ARCH_STM32
select ARM_SMC_MBOX
select ARM_SCMI_PROTOCOL
select COMMON_CLK_SCMI
select STM32_FIREWALL
help
This enables support for ARMv8 based STMicroelectronics
STM32 family, including:

View File

@ -1,12 +1,12 @@
menu "SoC selection"
config ARCH_MICROCHIP_POLARFIRE
def_bool SOC_MICROCHIP_POLARFIRE
def_bool ARCH_MICROCHIP
config SOC_MICROCHIP_POLARFIRE
bool "Microchip PolarFire SoCs"
config ARCH_MICROCHIP
bool "Microchip SoCs"
help
This enables support for Microchip PolarFire SoC platforms.
This enables support for Microchip SoC platforms.
config ARCH_RENESAS
bool "Renesas RISC-V SoCs"
@ -14,9 +14,6 @@ config ARCH_RENESAS
This enables support for the RISC-V based Renesas SoCs.
config ARCH_SIFIVE
def_bool SOC_SIFIVE
config SOC_SIFIVE
bool "SiFive SoCs"
select ERRATA_SIFIVE if !XIP_KERNEL
help
@ -55,9 +52,6 @@ config ARCH_THEAD
This enables support for the RISC-V based T-HEAD SoCs.
config ARCH_VIRT
def_bool SOC_VIRT
config SOC_VIRT
bool "QEMU Virt Machine"
select CLINT_TIMER if RISCV_M_MODE
select POWER_RESET
@ -72,11 +66,13 @@ config SOC_VIRT
This enables support for QEMU Virt Machine.
config ARCH_CANAAN
def_bool SOC_CANAAN
bool "Canaan Kendryte SoC"
help
This enables support for Canaan Kendryte series SoC platform hardware.
config SOC_CANAAN
config SOC_CANAAN_K210
bool "Canaan Kendryte K210 SoC"
depends on !MMU
depends on !MMU && ARCH_CANAAN
select CLINT_TIMER if RISCV_M_MODE
select ARCH_HAS_RESET_CONTROLLER
select PINCTRL

View File

@ -154,7 +154,7 @@ vdso-install-y += arch/riscv/kernel/vdso/vdso.so.dbg
vdso-install-$(CONFIG_COMPAT) += arch/riscv/kernel/compat_vdso/compat_vdso.so.dbg
ifneq ($(CONFIG_XIP_KERNEL),y)
ifeq ($(CONFIG_RISCV_M_MODE)$(CONFIG_ARCH_CANAAN),yy)
ifeq ($(CONFIG_RISCV_M_MODE)$(CONFIG_SOC_CANAAN_K210),yy)
KBUILD_IMAGE := $(boot)/loader.bin
else
ifeq ($(CONFIG_EFI_ZBOOT),)

View File

@ -25,14 +25,15 @@ CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
# CONFIG_SYSFS_SYSCALL is not set
CONFIG_PROFILING=y
CONFIG_SOC_MICROCHIP_POLARFIRE=y
CONFIG_ARCH_MICROCHIP=y
CONFIG_ARCH_RENESAS=y
CONFIG_SOC_SIFIVE=y
CONFIG_ARCH_SIFIVE=y
CONFIG_ARCH_SOPHGO=y
CONFIG_SOC_STARFIVE=y
CONFIG_ARCH_SUNXI=y
CONFIG_ARCH_THEAD=y
CONFIG_SOC_VIRT=y
CONFIG_ARCH_VIRT=y
CONFIG_ARCH_CANAAN=y
CONFIG_SMP=y
CONFIG_HOTPLUG_CPU=y
CONFIG_PM=y

View File

@ -27,7 +27,8 @@ CONFIG_EXPERT=y
CONFIG_SLUB=y
CONFIG_SLUB_TINY=y
# CONFIG_MMU is not set
CONFIG_SOC_CANAAN=y
CONFIG_ARCH_CANAAN=y
CONFIG_SOC_CANAAN_K210=y
CONFIG_NONPORTABLE=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2

View File

@ -19,7 +19,8 @@ CONFIG_EXPERT=y
CONFIG_SLUB=y
CONFIG_SLUB_TINY=y
# CONFIG_MMU is not set
CONFIG_SOC_CANAAN=y
CONFIG_ARCH_CANAAN=y
CONFIG_SOC_CANAAN_K210=y
CONFIG_NONPORTABLE=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2

View File

@ -24,7 +24,7 @@ CONFIG_EXPERT=y
CONFIG_SLUB=y
CONFIG_SLUB_TINY=y
# CONFIG_MMU is not set
CONFIG_SOC_VIRT=y
CONFIG_ARCH_VIRT=y
CONFIG_NONPORTABLE=y
CONFIG_SMP=y
CONFIG_CMDLINE="root=/dev/vda rw earlycon=uart8250,mmio,0x10000000,115200n8 console=ttyS0"

View File

@ -163,6 +163,16 @@ config QCOM_SSC_BLOCK_BUS
i2c/spi/uart controllers, a hexagon core, and a clock controller
which provides clocks for the above.
config STM32_FIREWALL
bool "STM32 Firewall framework"
depends on (ARCH_STM32 || COMPILE_TEST) && OF
select OF_DYNAMIC
help
Say y to enable STM32 firewall framework and its services. Firewall
controllers will be able to register to the framework. Access for
hardware resources linked to a firewall controller can be requested
through this STM32 framework.
config SUN50I_DE2_BUS
bool "Allwinner A64 DE2 Bus Driver"
default ARM64

View File

@ -26,6 +26,7 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
obj-$(CONFIG_QCOM_EBI2) += qcom-ebi2.o
obj-$(CONFIG_QCOM_SSC_BLOCK_BUS) += qcom-ssc-block-bus.o
obj-$(CONFIG_STM32_FIREWALL) += stm32_firewall.o stm32_rifsc.o stm32_etzpc.o
obj-$(CONFIG_SUN50I_DE2_BUS) += sun50i-de2.o
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
obj-$(CONFIG_OF) += simple-pm-bus.o

View File

@ -410,6 +410,7 @@ static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
{ .compatible = "brcm,bcm74165-gisb-arb", .data = gisb_offsets_bcm74165 },
{ },
};
MODULE_DEVICE_TABLE(of, brcmstb_gisb_arb_of_match);
static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
{

141
drivers/bus/stm32_etzpc.c Normal file
View File

@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023, STMicroelectronics - All Rights Reserved
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include "stm32_firewall.h"
/*
* ETZPC registers
*/
#define ETZPC_DECPROT 0x10
#define ETZPC_HWCFGR 0x3F0
/*
* HWCFGR register
*/
#define ETZPC_HWCFGR_NUM_TZMA GENMASK(7, 0)
#define ETZPC_HWCFGR_NUM_PER_SEC GENMASK(15, 8)
#define ETZPC_HWCFGR_NUM_AHB_SEC GENMASK(23, 16)
#define ETZPC_HWCFGR_CHUNKS1N4 GENMASK(31, 24)
/*
* ETZPC miscellaneous
*/
#define ETZPC_PROT_MASK GENMASK(1, 0)
#define ETZPC_PROT_A7NS 0x3
#define ETZPC_DECPROT_SHIFT 1
#define IDS_PER_DECPROT_REGS 16
static int stm32_etzpc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
{
u32 offset, reg_offset, sec_val;
if (firewall_id >= ctrl->max_entries) {
dev_err(ctrl->dev, "Invalid sys bus ID %u", firewall_id);
return -EINVAL;
}
/* Check access configuration, 16 peripherals per register */
reg_offset = ETZPC_DECPROT + 0x4 * (firewall_id / IDS_PER_DECPROT_REGS);
offset = (firewall_id % IDS_PER_DECPROT_REGS) << ETZPC_DECPROT_SHIFT;
/* Verify peripheral is non-secure and attributed to cortex A7 */
sec_val = (readl(ctrl->mmio + reg_offset) >> offset) & ETZPC_PROT_MASK;
if (sec_val != ETZPC_PROT_A7NS) {
dev_dbg(ctrl->dev, "Invalid bus configuration: reg_offset %#x, value %d\n",
reg_offset, sec_val);
return -EACCES;
}
return 0;
}
static void stm32_etzpc_release_access(struct stm32_firewall_controller *ctrl __maybe_unused,
u32 firewall_id __maybe_unused)
{
}
static int stm32_etzpc_probe(struct platform_device *pdev)
{
struct stm32_firewall_controller *etzpc_controller;
struct device_node *np = pdev->dev.of_node;
u32 nb_per, nb_master;
struct resource *res;
void __iomem *mmio;
int rc;
etzpc_controller = devm_kzalloc(&pdev->dev, sizeof(*etzpc_controller), GFP_KERNEL);
if (!etzpc_controller)
return -ENOMEM;
mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
etzpc_controller->dev = &pdev->dev;
etzpc_controller->mmio = mmio;
etzpc_controller->name = dev_driver_string(etzpc_controller->dev);
etzpc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL;
etzpc_controller->grant_access = stm32_etzpc_grant_access;
etzpc_controller->release_access = stm32_etzpc_release_access;
/* Get number of etzpc entries*/
nb_per = FIELD_GET(ETZPC_HWCFGR_NUM_PER_SEC,
readl(etzpc_controller->mmio + ETZPC_HWCFGR));
nb_master = FIELD_GET(ETZPC_HWCFGR_NUM_AHB_SEC,
readl(etzpc_controller->mmio + ETZPC_HWCFGR));
etzpc_controller->max_entries = nb_per + nb_master;
platform_set_drvdata(pdev, etzpc_controller);
rc = stm32_firewall_controller_register(etzpc_controller);
if (rc) {
dev_err(etzpc_controller->dev, "Couldn't register as a firewall controller: %d",
rc);
return rc;
}
rc = stm32_firewall_populate_bus(etzpc_controller);
if (rc) {
dev_err(etzpc_controller->dev, "Couldn't populate ETZPC bus: %d",
rc);
return rc;
}
/* Populate all allowed nodes */
return of_platform_populate(np, NULL, NULL, &pdev->dev);
}
static const struct of_device_id stm32_etzpc_of_match[] = {
{ .compatible = "st,stm32-etzpc" },
{}
};
MODULE_DEVICE_TABLE(of, stm32_etzpc_of_match);
static struct platform_driver stm32_etzpc_driver = {
.probe = stm32_etzpc_probe,
.driver = {
.name = "stm32-etzpc",
.of_match_table = stm32_etzpc_of_match,
},
};
module_platform_driver(stm32_etzpc_driver);
MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>");
MODULE_DESCRIPTION("STMicroelectronics ETZPC driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,294 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023, STMicroelectronics - All Rights Reserved
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/bus/stm32_firewall_device.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/slab.h>
#include "stm32_firewall.h"
/* Corresponds to STM32_FIREWALL_MAX_EXTRA_ARGS + firewall ID */
#define STM32_FIREWALL_MAX_ARGS (STM32_FIREWALL_MAX_EXTRA_ARGS + 1)
static LIST_HEAD(firewall_controller_list);
static DEFINE_MUTEX(firewall_controller_list_lock);
/* Firewall device API */
int stm32_firewall_get_firewall(struct device_node *np, struct stm32_firewall *firewall,
unsigned int nb_firewall)
{
struct stm32_firewall_controller *ctrl;
struct of_phandle_iterator it;
unsigned int i, j = 0;
int err;
if (!firewall || !nb_firewall)
return -EINVAL;
/* Parse property with phandle parsed out */
of_for_each_phandle(&it, err, np, "access-controllers", "#access-controller-cells", 0) {
struct of_phandle_args provider_args;
struct device_node *provider = it.node;
const char *fw_entry;
bool match = false;
if (err) {
pr_err("Unable to get access-controllers property for node %s\n, err: %d",
np->full_name, err);
of_node_put(provider);
return err;
}
if (j >= nb_firewall) {
pr_err("Too many firewall controllers");
of_node_put(provider);
return -EINVAL;
}
provider_args.args_count = of_phandle_iterator_args(&it, provider_args.args,
STM32_FIREWALL_MAX_ARGS);
/* Check if the parsed phandle corresponds to a registered firewall controller */
mutex_lock(&firewall_controller_list_lock);
list_for_each_entry(ctrl, &firewall_controller_list, entry) {
if (ctrl->dev->of_node->phandle == it.phandle) {
match = true;
firewall[j].firewall_ctrl = ctrl;
break;
}
}
mutex_unlock(&firewall_controller_list_lock);
if (!match) {
firewall[j].firewall_ctrl = NULL;
pr_err("No firewall controller registered for %s\n", np->full_name);
of_node_put(provider);
return -ENODEV;
}
err = of_property_read_string_index(np, "access-controller-names", j, &fw_entry);
if (err == 0)
firewall[j].entry = fw_entry;
/* Handle the case when there are no arguments given along with the phandle */
if (provider_args.args_count < 0 ||
provider_args.args_count > STM32_FIREWALL_MAX_ARGS) {
of_node_put(provider);
return -EINVAL;
} else if (provider_args.args_count == 0) {
firewall[j].extra_args_size = 0;
firewall[j].firewall_id = U32_MAX;
j++;
continue;
}
/* The firewall ID is always the first argument */
firewall[j].firewall_id = provider_args.args[0];
/* Extra args start at the second argument */
for (i = 0; i < provider_args.args_count - 1; i++)
firewall[j].extra_args[i] = provider_args.args[i + 1];
/* Remove the firewall ID arg that is not an extra argument */
firewall[j].extra_args_size = provider_args.args_count - 1;
j++;
}
return 0;
}
EXPORT_SYMBOL_GPL(stm32_firewall_get_firewall);
int stm32_firewall_grant_access(struct stm32_firewall *firewall)
{
struct stm32_firewall_controller *firewall_controller;
if (!firewall || firewall->firewall_id == U32_MAX)
return -EINVAL;
firewall_controller = firewall->firewall_ctrl;
if (!firewall_controller)
return -ENODEV;
return firewall_controller->grant_access(firewall_controller, firewall->firewall_id);
}
EXPORT_SYMBOL_GPL(stm32_firewall_grant_access);
int stm32_firewall_grant_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id)
{
struct stm32_firewall_controller *firewall_controller;
if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX)
return -EINVAL;
firewall_controller = firewall->firewall_ctrl;
if (!firewall_controller)
return -ENODEV;
return firewall_controller->grant_access(firewall_controller, subsystem_id);
}
EXPORT_SYMBOL_GPL(stm32_firewall_grant_access_by_id);
void stm32_firewall_release_access(struct stm32_firewall *firewall)
{
struct stm32_firewall_controller *firewall_controller;
if (!firewall || firewall->firewall_id == U32_MAX) {
pr_debug("Incorrect arguments when releasing a firewall access\n");
return;
}
firewall_controller = firewall->firewall_ctrl;
if (!firewall_controller) {
pr_debug("No firewall controller to release\n");
return;
}
firewall_controller->release_access(firewall_controller, firewall->firewall_id);
}
EXPORT_SYMBOL_GPL(stm32_firewall_release_access);
void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id)
{
struct stm32_firewall_controller *firewall_controller;
if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) {
pr_debug("Incorrect arguments when releasing a firewall access");
return;
}
firewall_controller = firewall->firewall_ctrl;
if (!firewall_controller) {
pr_debug("No firewall controller to release");
return;
}
firewall_controller->release_access(firewall_controller, subsystem_id);
}
EXPORT_SYMBOL_GPL(stm32_firewall_release_access_by_id);
/* Firewall controller API */
int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller)
{
struct stm32_firewall_controller *ctrl;
if (!firewall_controller)
return -ENODEV;
pr_info("Registering %s firewall controller\n", firewall_controller->name);
mutex_lock(&firewall_controller_list_lock);
list_for_each_entry(ctrl, &firewall_controller_list, entry) {
if (ctrl == firewall_controller) {
pr_debug("%s firewall controller already registered\n",
firewall_controller->name);
mutex_unlock(&firewall_controller_list_lock);
return 0;
}
}
list_add_tail(&firewall_controller->entry, &firewall_controller_list);
mutex_unlock(&firewall_controller_list_lock);
return 0;
}
EXPORT_SYMBOL_GPL(stm32_firewall_controller_register);
void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller)
{
struct stm32_firewall_controller *ctrl;
bool controller_removed = false;
if (!firewall_controller) {
pr_debug("Null reference while unregistering firewall controller\n");
return;
}
mutex_lock(&firewall_controller_list_lock);
list_for_each_entry(ctrl, &firewall_controller_list, entry) {
if (ctrl == firewall_controller) {
controller_removed = true;
list_del_init(&ctrl->entry);
break;
}
}
mutex_unlock(&firewall_controller_list_lock);
if (!controller_removed)
pr_debug("There was no firewall controller named %s to unregister\n",
firewall_controller->name);
}
EXPORT_SYMBOL_GPL(stm32_firewall_controller_unregister);
int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller)
{
struct stm32_firewall *firewalls;
struct device_node *child;
struct device *parent;
unsigned int i;
int len;
int err;
parent = firewall_controller->dev;
dev_dbg(parent, "Populating %s system bus\n", dev_name(firewall_controller->dev));
for_each_available_child_of_node(dev_of_node(parent), child) {
/* The access-controllers property is mandatory for firewall bus devices */
len = of_count_phandle_with_args(child, "access-controllers",
"#access-controller-cells");
if (len <= 0) {
of_node_put(child);
return -EINVAL;
}
firewalls = kcalloc(len, sizeof(*firewalls), GFP_KERNEL);
if (!firewalls) {
of_node_put(child);
return -ENOMEM;
}
err = stm32_firewall_get_firewall(child, firewalls, (unsigned int)len);
if (err) {
kfree(firewalls);
of_node_put(child);
return err;
}
for (i = 0; i < len; i++) {
if (firewall_controller->grant_access(firewall_controller,
firewalls[i].firewall_id)) {
/*
* Peripheral access not allowed or not defined.
* Mark the node as populated so platform bus won't probe it
*/
of_detach_node(child);
dev_err(parent, "%s: Device driver will not be probed\n",
child->full_name);
}
}
kfree(firewalls);
}
return 0;
}
EXPORT_SYMBOL_GPL(stm32_firewall_populate_bus);

View File

@ -0,0 +1,83 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2023, STMicroelectronics - All Rights Reserved
*/
#ifndef _STM32_FIREWALL_H
#define _STM32_FIREWALL_H
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/types.h>
/**
* STM32_PERIPHERAL_FIREWALL: This type of firewall protects peripherals
* STM32_MEMORY_FIREWALL: This type of firewall protects memories/subsets of memory
* zones
* STM32_NOTYPE_FIREWALL: Undefined firewall type
*/
#define STM32_PERIPHERAL_FIREWALL BIT(1)
#define STM32_MEMORY_FIREWALL BIT(2)
#define STM32_NOTYPE_FIREWALL BIT(3)
/**
* struct stm32_firewall_controller - Information on firewall controller supplying services
*
* @name: Name of the firewall controller
* @dev: Device reference of the firewall controller
* @mmio: Base address of the firewall controller
* @entry: List entry of the firewall controller list
* @type: Type of firewall
* @max_entries: Number of entries covered by the firewall
* @grant_access: Callback used to grant access for a device access against a
* firewall controller
* @release_access: Callback used to release resources taken by a device when access was
* granted
* @grant_memory_range_access: Callback used to grant access for a device to a given memory region
*/
struct stm32_firewall_controller {
const char *name;
struct device *dev;
void __iomem *mmio;
struct list_head entry;
unsigned int type;
unsigned int max_entries;
int (*grant_access)(struct stm32_firewall_controller *ctrl, u32 id);
void (*release_access)(struct stm32_firewall_controller *ctrl, u32 id);
int (*grant_memory_range_access)(struct stm32_firewall_controller *ctrl, phys_addr_t paddr,
size_t size);
};
/**
* stm32_firewall_controller_register - Register a firewall controller to the STM32 firewall
* framework
* @firewall_controller: Firewall controller to register
*
* Returns 0 in case of success or -ENODEV if no controller was given.
*/
int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller);
/**
* stm32_firewall_controller_unregister - Unregister a firewall controller from the STM32
* firewall framework
* @firewall_controller: Firewall controller to unregister
*/
void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller);
/**
* stm32_firewall_populate_bus - Populate device tree nodes that have a correct firewall
* configuration. This is used at boot-time only, as a sanity check
* between device tree and firewalls hardware configurations to
* prevent a kernel crash when a device driver is not granted access
*
* @firewall_controller: Firewall controller which nodes will be populated or not
*
* Returns 0 in case of success or appropriate errno code if error occurred.
*/
int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller);
#endif /* _STM32_FIREWALL_H */

252
drivers/bus/stm32_rifsc.c Normal file
View File

@ -0,0 +1,252 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023, STMicroelectronics - All Rights Reserved
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include "stm32_firewall.h"
/*
* RIFSC offset register
*/
#define RIFSC_RISC_SECCFGR0 0x10
#define RIFSC_RISC_PRIVCFGR0 0x30
#define RIFSC_RISC_PER0_CIDCFGR 0x100
#define RIFSC_RISC_PER0_SEMCR 0x104
#define RIFSC_RISC_HWCFGR2 0xFEC
/*
* SEMCR register
*/
#define SEMCR_MUTEX BIT(0)
/*
* HWCFGR2 register
*/
#define HWCFGR2_CONF1_MASK GENMASK(15, 0)
#define HWCFGR2_CONF2_MASK GENMASK(23, 16)
#define HWCFGR2_CONF3_MASK GENMASK(31, 24)
/*
* RIFSC miscellaneous
*/
#define RIFSC_RISC_CFEN_MASK BIT(0)
#define RIFSC_RISC_SEM_EN_MASK BIT(1)
#define RIFSC_RISC_SCID_MASK GENMASK(6, 4)
#define RIFSC_RISC_SEML_SHIFT 16
#define RIFSC_RISC_SEMWL_MASK GENMASK(23, 16)
#define RIFSC_RISC_PER_ID_MASK GENMASK(31, 24)
#define RIFSC_RISC_PERx_CID_MASK (RIFSC_RISC_CFEN_MASK | \
RIFSC_RISC_SEM_EN_MASK | \
RIFSC_RISC_SCID_MASK | \
RIFSC_RISC_SEMWL_MASK)
#define IDS_PER_RISC_SEC_PRIV_REGS 32
/* RIF miscellaneous */
/*
* CIDCFGR register fields
*/
#define CIDCFGR_CFEN BIT(0)
#define CIDCFGR_SEMEN BIT(1)
#define CIDCFGR_SEMWL(x) BIT(RIFSC_RISC_SEML_SHIFT + (x))
#define SEMWL_SHIFT 16
/* Compartiment IDs */
#define RIF_CID0 0x0
#define RIF_CID1 0x1
static bool stm32_rifsc_is_semaphore_available(void __iomem *addr)
{
return !(readl(addr) & SEMCR_MUTEX);
}
static int stm32_rif_acquire_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
int id)
{
void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
writel(SEMCR_MUTEX, addr);
/* Check that CID1 has the semaphore */
if (stm32_rifsc_is_semaphore_available(addr) ||
FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) != RIF_CID1)
return -EACCES;
return 0;
}
static void stm32_rif_release_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
int id)
{
void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
if (stm32_rifsc_is_semaphore_available(addr))
return;
writel(SEMCR_MUTEX, addr);
/* Ok if another compartment takes the semaphore before the check */
WARN_ON(!stm32_rifsc_is_semaphore_available(addr) &&
FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) == RIF_CID1);
}
static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
{
struct stm32_firewall_controller *rifsc_controller = ctrl;
u32 reg_offset, reg_id, sec_reg_value, cid_reg_value;
int rc;
if (firewall_id >= rifsc_controller->max_entries) {
dev_err(rifsc_controller->dev, "Invalid sys bus ID %u", firewall_id);
return -EINVAL;
}
/*
* RIFSC_RISC_PRIVCFGRx and RIFSC_RISC_SECCFGRx both handle configuration access for
* 32 peripherals. On the other hand, there is one _RIFSC_RISC_PERx_CIDCFGR register
* per peripheral
*/
reg_id = firewall_id / IDS_PER_RISC_SEC_PRIV_REGS;
reg_offset = firewall_id % IDS_PER_RISC_SEC_PRIV_REGS;
sec_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id);
cid_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * firewall_id);
/* First check conditions for semaphore mode, which doesn't take into account static CID. */
if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) {
if (cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT)) {
/* Static CID is irrelevant if semaphore mode */
goto skip_cid_check;
} else {
dev_dbg(rifsc_controller->dev,
"Invalid bus semaphore configuration: index %d\n", firewall_id);
return -EACCES;
}
}
/*
* Skip CID check if CID filtering isn't enabled or filtering is enabled on CID0, which
* corresponds to whatever CID.
*/
if (!(cid_reg_value & CIDCFGR_CFEN) ||
FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) == RIF_CID0)
goto skip_cid_check;
/* Coherency check with the CID configuration */
if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) {
dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n",
firewall_id);
return -EACCES;
}
skip_cid_check:
/* Check security configuration */
if (sec_reg_value & BIT(reg_offset)) {
dev_dbg(rifsc_controller->dev,
"Invalid security configuration for peripheral: %d\n", firewall_id);
return -EACCES;
}
/*
* If the peripheral is in semaphore mode, take the semaphore so that
* the CID1 has the ownership.
*/
if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) {
rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id);
if (rc) {
dev_err(rifsc_controller->dev,
"Couldn't acquire semaphore for peripheral: %d\n", firewall_id);
return rc;
}
}
return 0;
}
static void stm32_rifsc_release_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
{
stm32_rif_release_semaphore(ctrl, firewall_id);
}
static int stm32_rifsc_probe(struct platform_device *pdev)
{
struct stm32_firewall_controller *rifsc_controller;
struct device_node *np = pdev->dev.of_node;
u32 nb_risup, nb_rimu, nb_risal;
struct resource *res;
void __iomem *mmio;
int rc;
rifsc_controller = devm_kzalloc(&pdev->dev, sizeof(*rifsc_controller), GFP_KERNEL);
if (!rifsc_controller)
return -ENOMEM;
mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
rifsc_controller->dev = &pdev->dev;
rifsc_controller->mmio = mmio;
rifsc_controller->name = dev_driver_string(rifsc_controller->dev);
rifsc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL;
rifsc_controller->grant_access = stm32_rifsc_grant_access;
rifsc_controller->release_access = stm32_rifsc_release_access;
/* Get number of RIFSC entries*/
nb_risup = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF1_MASK;
nb_rimu = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF2_MASK;
nb_risal = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF3_MASK;
rifsc_controller->max_entries = nb_risup + nb_rimu + nb_risal;
platform_set_drvdata(pdev, rifsc_controller);
rc = stm32_firewall_controller_register(rifsc_controller);
if (rc) {
dev_err(rifsc_controller->dev, "Couldn't register as a firewall controller: %d",
rc);
return rc;
}
rc = stm32_firewall_populate_bus(rifsc_controller);
if (rc) {
dev_err(rifsc_controller->dev, "Couldn't populate RIFSC bus: %d",
rc);
return rc;
}
/* Populate all allowed nodes */
return of_platform_populate(np, NULL, NULL, &pdev->dev);
}
static const struct of_device_id stm32_rifsc_of_match[] = {
{ .compatible = "st,stm32mp25-rifsc" },
{}
};
MODULE_DEVICE_TABLE(of, stm32_rifsc_of_match);
static struct platform_driver stm32_rifsc_driver = {
.probe = stm32_rifsc_probe,
.driver = {
.name = "stm32-rifsc",
.of_match_table = stm32_rifsc_of_match,
},
};
module_platform_driver(stm32_rifsc_driver);
MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>");
MODULE_DESCRIPTION("STMicroelectronics RIFSC driver");
MODULE_LICENSE("GPL");

View File

@ -1,6 +1,17 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ti-sysc.c - Texas Instruments sysc interconnect target driver
*
* TI SoCs have an interconnect target wrapper IP for many devices. The wrapper
* IP manages clock gating, resets, and PM capabilities for the connected devices.
*
* Copyright (C) 2017-2024 Texas Instruments Incorporated - https://www.ti.com/
*
* Many features are based on the earlier omap_hwmod arch code with thanks to all
* the people who developed and debugged the code over the years:
*
* Copyright (C) 2009-2011 Nokia Corporation
* Copyright (C) 2011-2021 Texas Instruments Incorporated - https://www.ti.com/
*/
#include <linux/io.h>
@ -1458,8 +1469,7 @@ static int __maybe_unused sysc_noirq_suspend(struct device *dev)
ddata = dev_get_drvdata(dev);
if (ddata->cfg.quirks &
(SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE))
if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE)
return 0;
if (!ddata->enabled)
@ -1477,8 +1487,7 @@ static int __maybe_unused sysc_noirq_resume(struct device *dev)
ddata = dev_get_drvdata(dev);
if (ddata->cfg.quirks &
(SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE))
if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE)
return 0;
if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_RESUME) {
@ -1529,19 +1538,6 @@ struct sysc_revision_quirk {
}
static const struct sysc_revision_quirk sysc_revision_quirks[] = {
/* These drivers need to be fixed to not use pm_runtime_irq_safe() */
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE),
/* Uarts on omap4 and later */
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff,
SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47424e03, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE),
/* Quirks that need to be set based on the module address */
SYSC_QUIRK("mcpdm", 0x40132000, 0, 0x10, -ENODEV, 0x50000800, 0xffffffff,
SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT |
@ -1599,6 +1595,17 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("sata", 0, 0xfc, 0x1100, -ENODEV, 0x5e412000, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT),
/* Uarts on omap4 and later */
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff,
SYSC_QUIRK_SWSUP_SIDLE_ACT),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47424e03, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -ENODEV, 0x50700101, 0xffffffff,
@ -2145,8 +2152,7 @@ static int sysc_reset(struct sysc *ddata)
sysc_offset = ddata->offsets[SYSC_SYSCONFIG];
if (ddata->legacy_mode ||
ddata->cap->regbits->srst_shift < 0 ||
ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)
ddata->cap->regbits->srst_shift < 0)
return 0;
sysc_mask = BIT(ddata->cap->regbits->srst_shift);
@ -2240,12 +2246,14 @@ static int sysc_init_module(struct sysc *ddata)
goto err_main_clocks;
}
error = sysc_reset(ddata);
if (error)
dev_err(ddata->dev, "Reset failed with %d\n", error);
if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) {
error = sysc_reset(ddata);
if (error)
dev_err(ddata->dev, "Reset failed with %d\n", error);
if (error && !ddata->legacy_mode)
sysc_disable_module(ddata->dev);
if (error && !ddata->legacy_mode)
sysc_disable_module(ddata->dev);
}
err_main_clocks:
if (error)
@ -2447,89 +2455,6 @@ static int __maybe_unused sysc_child_runtime_resume(struct device *dev)
return pm_generic_runtime_resume(dev);
}
#ifdef CONFIG_PM_SLEEP
static int sysc_child_suspend_noirq(struct device *dev)
{
struct sysc *ddata;
int error;
ddata = sysc_child_to_parent(dev);
dev_dbg(ddata->dev, "%s %s\n", __func__,
ddata->name ? ddata->name : "");
error = pm_generic_suspend_noirq(dev);
if (error) {
dev_err(dev, "%s error at %i: %i\n",
__func__, __LINE__, error);
return error;
}
if (!pm_runtime_status_suspended(dev)) {
error = pm_generic_runtime_suspend(dev);
if (error) {
dev_dbg(dev, "%s busy at %i: %i\n",
__func__, __LINE__, error);
return 0;
}
error = sysc_runtime_suspend(ddata->dev);
if (error) {
dev_err(dev, "%s error at %i: %i\n",
__func__, __LINE__, error);
return error;
}
ddata->child_needs_resume = true;
}
return 0;
}
static int sysc_child_resume_noirq(struct device *dev)
{
struct sysc *ddata;
int error;
ddata = sysc_child_to_parent(dev);
dev_dbg(ddata->dev, "%s %s\n", __func__,
ddata->name ? ddata->name : "");
if (ddata->child_needs_resume) {
ddata->child_needs_resume = false;
error = sysc_runtime_resume(ddata->dev);
if (error)
dev_err(ddata->dev,
"%s runtime resume error: %i\n",
__func__, error);
error = pm_generic_runtime_resume(dev);
if (error)
dev_err(ddata->dev,
"%s generic runtime resume: %i\n",
__func__, error);
}
return pm_generic_resume_noirq(dev);
}
#endif
static struct dev_pm_domain sysc_child_pm_domain = {
.ops = {
SET_RUNTIME_PM_OPS(sysc_child_runtime_suspend,
sysc_child_runtime_resume,
NULL)
USE_PLATFORM_PM_SLEEP_OPS
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_child_suspend_noirq,
sysc_child_resume_noirq)
}
};
/* Caller needs to take list_lock if ever used outside of cpu_pm */
static void sysc_reinit_modules(struct sysc_soc_info *soc)
{
@ -2600,25 +2525,6 @@ out_unlock:
mutex_unlock(&sysc_soc->list_lock);
}
/**
* sysc_legacy_idle_quirk - handle children in omap_device compatible way
* @ddata: device driver data
* @child: child device driver
*
* Allow idle for child devices as done with _od_runtime_suspend().
* Otherwise many child devices will not idle because of the permanent
* parent usecount set in pm_runtime_irq_safe().
*
* Note that the long term solution is to just modify the child device
* drivers to not set pm_runtime_irq_safe() and then this can be just
* dropped.
*/
static void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child)
{
if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
dev_pm_domain_set(child, &sysc_child_pm_domain);
}
static int sysc_notifier_call(struct notifier_block *nb,
unsigned long event, void *device)
{
@ -2635,7 +2541,6 @@ static int sysc_notifier_call(struct notifier_block *nb,
error = sysc_child_add_clocks(ddata, dev);
if (error)
return error;
sysc_legacy_idle_quirk(ddata, dev);
break;
default:
break;
@ -2859,8 +2764,7 @@ static const struct sysc_capabilities sysc_34xx_sr = {
.type = TI_SYSC_OMAP34XX_SR,
.sysc_mask = SYSC_OMAP2_CLOCKACTIVITY,
.regbits = &sysc_regbits_omap34xx_sr,
.mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED |
SYSC_QUIRK_LEGACY_IDLE,
.mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED,
};
/*
@ -2881,13 +2785,12 @@ static const struct sysc_capabilities sysc_36xx_sr = {
.type = TI_SYSC_OMAP36XX_SR,
.sysc_mask = SYSC_OMAP3_SR_ENAWAKEUP,
.regbits = &sysc_regbits_omap36xx_sr,
.mod_quirks = SYSC_QUIRK_UNCACHED | SYSC_QUIRK_LEGACY_IDLE,
.mod_quirks = SYSC_QUIRK_UNCACHED,
};
static const struct sysc_capabilities sysc_omap4_sr = {
.type = TI_SYSC_OMAP4_SR,
.regbits = &sysc_regbits_omap36xx_sr,
.mod_quirks = SYSC_QUIRK_LEGACY_IDLE,
};
/*

View File

@ -451,8 +451,8 @@ config COMMON_CLK_FIXED_MMIO
config COMMON_CLK_K210
bool "Clock driver for the Canaan Kendryte K210 SoC"
depends on OF && RISCV && SOC_CANAAN
default SOC_CANAAN
depends on OF && RISCV && SOC_CANAAN_K210
default SOC_CANAAN_K210
help
Support for the Canaan Kendryte K210 RISC-V SoC clocks.

View File

@ -101,11 +101,12 @@ struct ffa_drv_info {
bool bitmap_created;
bool notif_enabled;
unsigned int sched_recv_irq;
unsigned int notif_pend_irq;
unsigned int cpuhp_state;
struct ffa_pcpu_irq __percpu *irq_pcpu;
struct workqueue_struct *notif_pcpu_wq;
struct work_struct notif_pcpu_work;
struct work_struct irq_work;
struct work_struct sched_recv_irq_work;
struct xarray partition_info;
DECLARE_HASHTABLE(notifier_hash, ilog2(FFA_MAX_NOTIFICATIONS));
struct mutex notify_lock; /* lock to protect notifier hashtable */
@ -344,6 +345,38 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
return -EINVAL;
}
static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
{
u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
struct ffa_indirect_msg_hdr *msg;
ffa_value_t ret;
int retval = 0;
if (sz > (RXTX_BUFFER_SIZE - sizeof(*msg)))
return -ERANGE;
mutex_lock(&drv_info->tx_lock);
msg = drv_info->tx_buffer;
msg->flags = 0;
msg->res0 = 0;
msg->offset = sizeof(*msg);
msg->send_recv_id = src_dst_ids;
msg->size = sz;
memcpy((u8 *)msg + msg->offset, buf, sz);
/* flags = 0, sender VMID = 0 works for both physical/virtual NS */
invoke_ffa_fn((ffa_value_t){
.a0 = FFA_MSG_SEND2, .a1 = 0, .a2 = 0
}, &ret);
if (ret.a0 == FFA_ERROR)
retval = ffa_to_linux_errno((int)ret.a2);
mutex_unlock(&drv_info->tx_lock);
return retval;
}
static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
u32 frag_len, u32 len, u64 *handle)
{
@ -870,6 +903,11 @@ static int ffa_sync_send_receive(struct ffa_device *dev,
dev->mode_32bit, data);
}
static int ffa_indirect_msg_send(struct ffa_device *dev, void *buf, size_t sz)
{
return ffa_msg_send2(drv_info->vm_id, dev->vm_id, buf, sz);
}
static int ffa_memory_share(struct ffa_mem_ops_args *args)
{
if (drv_info->mem_ops_native)
@ -1108,7 +1146,7 @@ static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
}
}
static void notif_pcpu_irq_work_fn(struct work_struct *work)
static void notif_get_and_handle(void *unused)
{
int rc;
struct ffa_notify_bitmaps bitmaps;
@ -1131,10 +1169,17 @@ ffa_self_notif_handle(u16 vcpu, bool is_per_vcpu, void *cb_data)
struct ffa_drv_info *info = cb_data;
if (!is_per_vcpu)
notif_pcpu_irq_work_fn(&info->notif_pcpu_work);
notif_get_and_handle(info);
else
queue_work_on(vcpu, info->notif_pcpu_wq,
&info->notif_pcpu_work);
smp_call_function_single(vcpu, notif_get_and_handle, info, 0);
}
static void notif_pcpu_irq_work_fn(struct work_struct *work)
{
struct ffa_drv_info *info = container_of(work, struct ffa_drv_info,
notif_pcpu_work);
ffa_self_notif_handle(smp_processor_id(), true, info);
}
static const struct ffa_info_ops ffa_drv_info_ops = {
@ -1145,6 +1190,7 @@ static const struct ffa_info_ops ffa_drv_info_ops = {
static const struct ffa_msg_ops ffa_drv_msg_ops = {
.mode_32bit_set = ffa_mode_32bit_set,
.sync_send_receive = ffa_sync_send_receive,
.indirect_send = ffa_indirect_msg_send,
};
static const struct ffa_mem_ops ffa_drv_mem_ops = {
@ -1227,6 +1273,8 @@ static int ffa_setup_partitions(void)
continue;
}
ffa_dev->properties = tpbuf->properties;
if (drv_info->version > FFA_VERSION_1_0 &&
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
ffa_mode_32bit_set(ffa_dev);
@ -1291,12 +1339,23 @@ static void ffa_partitions_cleanup(void)
#define FFA_FEAT_SCHEDULE_RECEIVER_INT (2)
#define FFA_FEAT_MANAGED_EXIT_INT (3)
static irqreturn_t irq_handler(int irq, void *irq_data)
static irqreturn_t ffa_sched_recv_irq_handler(int irq, void *irq_data)
{
struct ffa_pcpu_irq *pcpu = irq_data;
struct ffa_drv_info *info = pcpu->info;
queue_work(info->notif_pcpu_wq, &info->irq_work);
queue_work(info->notif_pcpu_wq, &info->sched_recv_irq_work);
return IRQ_HANDLED;
}
static irqreturn_t notif_pend_irq_handler(int irq, void *irq_data)
{
struct ffa_pcpu_irq *pcpu = irq_data;
struct ffa_drv_info *info = pcpu->info;
queue_work_on(smp_processor_id(), info->notif_pcpu_wq,
&info->notif_pcpu_work);
return IRQ_HANDLED;
}
@ -1306,15 +1365,23 @@ static void ffa_sched_recv_irq_work_fn(struct work_struct *work)
ffa_notification_info_get();
}
static int ffa_sched_recv_irq_map(void)
static int ffa_irq_map(u32 id)
{
int ret, irq, sr_intid;
char *err_str;
int ret, irq, intid;
/* The returned sr_intid is assumed to be SGI donated to NS world */
ret = ffa_features(FFA_FEAT_SCHEDULE_RECEIVER_INT, 0, &sr_intid, NULL);
if (id == FFA_FEAT_NOTIFICATION_PENDING_INT)
err_str = "Notification Pending Interrupt";
else if (id == FFA_FEAT_SCHEDULE_RECEIVER_INT)
err_str = "Schedule Receiver Interrupt";
else
err_str = "Unknown ID";
/* The returned intid is assumed to be SGI donated to NS world */
ret = ffa_features(id, 0, &intid, NULL);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
pr_err("Failed to retrieve scheduler Rx interrupt\n");
pr_err("Failed to retrieve FF-A %s %u\n", err_str, id);
return ret;
}
@ -1329,12 +1396,12 @@ static int ffa_sched_recv_irq_map(void)
oirq.np = gic;
oirq.args_count = 1;
oirq.args[0] = sr_intid;
oirq.args[0] = intid;
irq = irq_create_of_mapping(&oirq);
of_node_put(gic);
#ifdef CONFIG_ACPI
} else {
irq = acpi_register_gsi(NULL, sr_intid, ACPI_EDGE_SENSITIVE,
irq = acpi_register_gsi(NULL, intid, ACPI_EDGE_SENSITIVE,
ACPI_ACTIVE_HIGH);
#endif
}
@ -1347,23 +1414,28 @@ static int ffa_sched_recv_irq_map(void)
return irq;
}
static void ffa_sched_recv_irq_unmap(void)
static void ffa_irq_unmap(unsigned int irq)
{
if (drv_info->sched_recv_irq) {
irq_dispose_mapping(drv_info->sched_recv_irq);
drv_info->sched_recv_irq = 0;
}
if (!irq)
return;
irq_dispose_mapping(irq);
}
static int ffa_cpuhp_pcpu_irq_enable(unsigned int cpu)
{
enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE);
if (drv_info->sched_recv_irq)
enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE);
if (drv_info->notif_pend_irq)
enable_percpu_irq(drv_info->notif_pend_irq, IRQ_TYPE_NONE);
return 0;
}
static int ffa_cpuhp_pcpu_irq_disable(unsigned int cpu)
{
disable_percpu_irq(drv_info->sched_recv_irq);
if (drv_info->sched_recv_irq)
disable_percpu_irq(drv_info->sched_recv_irq);
if (drv_info->notif_pend_irq)
disable_percpu_irq(drv_info->notif_pend_irq);
return 0;
}
@ -1382,13 +1454,16 @@ static void ffa_uninit_pcpu_irq(void)
if (drv_info->sched_recv_irq)
free_percpu_irq(drv_info->sched_recv_irq, drv_info->irq_pcpu);
if (drv_info->notif_pend_irq)
free_percpu_irq(drv_info->notif_pend_irq, drv_info->irq_pcpu);
if (drv_info->irq_pcpu) {
free_percpu(drv_info->irq_pcpu);
drv_info->irq_pcpu = NULL;
}
}
static int ffa_init_pcpu_irq(unsigned int irq)
static int ffa_init_pcpu_irq(void)
{
struct ffa_pcpu_irq __percpu *irq_pcpu;
int ret, cpu;
@ -1402,13 +1477,31 @@ static int ffa_init_pcpu_irq(unsigned int irq)
drv_info->irq_pcpu = irq_pcpu;
ret = request_percpu_irq(irq, irq_handler, "ARM-FFA", irq_pcpu);
if (ret) {
pr_err("Error registering notification IRQ %d: %d\n", irq, ret);
return ret;
if (drv_info->sched_recv_irq) {
ret = request_percpu_irq(drv_info->sched_recv_irq,
ffa_sched_recv_irq_handler,
"ARM-FFA-SRI", irq_pcpu);
if (ret) {
pr_err("Error registering percpu SRI nIRQ %d : %d\n",
drv_info->sched_recv_irq, ret);
drv_info->sched_recv_irq = 0;
return ret;
}
}
INIT_WORK(&drv_info->irq_work, ffa_sched_recv_irq_work_fn);
if (drv_info->notif_pend_irq) {
ret = request_percpu_irq(drv_info->notif_pend_irq,
notif_pend_irq_handler,
"ARM-FFA-NPI", irq_pcpu);
if (ret) {
pr_err("Error registering percpu NPI nIRQ %d : %d\n",
drv_info->notif_pend_irq, ret);
drv_info->notif_pend_irq = 0;
return ret;
}
}
INIT_WORK(&drv_info->sched_recv_irq_work, ffa_sched_recv_irq_work_fn);
INIT_WORK(&drv_info->notif_pcpu_work, notif_pcpu_irq_work_fn);
drv_info->notif_pcpu_wq = create_workqueue("ffa_pcpu_irq_notification");
if (!drv_info->notif_pcpu_wq)
@ -1428,7 +1521,10 @@ static int ffa_init_pcpu_irq(unsigned int irq)
static void ffa_notifications_cleanup(void)
{
ffa_uninit_pcpu_irq();
ffa_sched_recv_irq_unmap();
ffa_irq_unmap(drv_info->sched_recv_irq);
drv_info->sched_recv_irq = 0;
ffa_irq_unmap(drv_info->notif_pend_irq);
drv_info->notif_pend_irq = 0;
if (drv_info->bitmap_created) {
ffa_notification_bitmap_destroy();
@ -1439,30 +1535,31 @@ static void ffa_notifications_cleanup(void)
static void ffa_notifications_setup(void)
{
int ret, irq;
int ret;
ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL);
if (ret) {
pr_info("Notifications not supported, continuing with it ..\n");
return;
if (!ret) {
ret = ffa_notification_bitmap_create();
if (ret) {
pr_err("Notification bitmap create error %d\n", ret);
return;
}
drv_info->bitmap_created = true;
}
ret = ffa_notification_bitmap_create();
if (ret) {
pr_info("Notification bitmap create error %d\n", ret);
return;
}
drv_info->bitmap_created = true;
ret = ffa_irq_map(FFA_FEAT_SCHEDULE_RECEIVER_INT);
if (ret > 0)
drv_info->sched_recv_irq = ret;
irq = ffa_sched_recv_irq_map();
if (irq <= 0) {
ret = irq;
ret = ffa_irq_map(FFA_FEAT_NOTIFICATION_PENDING_INT);
if (ret > 0)
drv_info->notif_pend_irq = ret;
if (!drv_info->sched_recv_irq && !drv_info->notif_pend_irq)
goto cleanup;
}
drv_info->sched_recv_irq = irq;
ret = ffa_init_pcpu_irq(irq);
ret = ffa_init_pcpu_irq();
if (ret)
goto cleanup;

View File

@ -10,7 +10,8 @@ scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += optee.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
scmi-protocols-y := base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
scmi-protocols-y += pinctrl.o
scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o

View File

@ -301,6 +301,17 @@ extern const struct scmi_desc scmi_optee_desc;
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);
enum scmi_bad_msg {
MSG_UNEXPECTED = -1,
MSG_INVALID = -2,
MSG_UNKNOWN = -3,
MSG_NOMEM = -4,
MSG_MBOX_SPURIOUS = -5,
};
void scmi_bad_message_trace(struct scmi_chan_info *cinfo, u32 msg_hdr,
enum scmi_bad_msg err);
/* shmem related declarations */
struct scmi_shared_mem;

View File

@ -33,6 +33,7 @@
#include <linux/processor.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/xarray.h>
#include "common.h"
#include "notify.h"
@ -44,8 +45,7 @@
static DEFINE_IDA(scmi_id);
static DEFINE_IDR(scmi_protocols);
static DEFINE_SPINLOCK(protocol_lock);
static DEFINE_XARRAY(scmi_protocols);
/* List of all SCMI devices active in system */
static LIST_HEAD(scmi_list);
@ -194,11 +194,94 @@ struct scmi_info {
#define bus_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, bus_nb)
#define req_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, dev_req_nb)
static const struct scmi_protocol *scmi_protocol_get(int protocol_id)
static unsigned long
scmi_vendor_protocol_signature(unsigned int protocol_id, char *vendor_id,
char *sub_vendor_id, u32 impl_ver)
{
const struct scmi_protocol *proto;
char *signature, *p;
unsigned long hash = 0;
proto = idr_find(&scmi_protocols, protocol_id);
/* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */
signature = kasprintf(GFP_KERNEL, "%02X|%s|%s|0x%08X", protocol_id,
vendor_id ?: "", sub_vendor_id ?: "", impl_ver);
if (!signature)
return 0;
p = signature;
while (*p)
hash = partial_name_hash(tolower(*p++), hash);
hash = end_name_hash(hash);
kfree(signature);
return hash;
}
static unsigned long
scmi_protocol_key_calculate(int protocol_id, char *vendor_id,
char *sub_vendor_id, u32 impl_ver)
{
if (protocol_id < SCMI_PROTOCOL_VENDOR_BASE)
return protocol_id;
else
return scmi_vendor_protocol_signature(protocol_id, vendor_id,
sub_vendor_id, impl_ver);
}
static const struct scmi_protocol *
__scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id,
char *sub_vendor_id, u32 impl_ver)
{
unsigned long key;
struct scmi_protocol *proto = NULL;
key = scmi_protocol_key_calculate(protocol_id, vendor_id,
sub_vendor_id, impl_ver);
if (key)
proto = xa_load(&scmi_protocols, key);
return proto;
}
static const struct scmi_protocol *
scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id,
char *sub_vendor_id, u32 impl_ver)
{
const struct scmi_protocol *proto = NULL;
/* Searching for closest match ...*/
proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
sub_vendor_id, impl_ver);
if (proto)
return proto;
/* Any match just on vendor/sub_vendor ? */
if (impl_ver) {
proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
sub_vendor_id, 0);
if (proto)
return proto;
}
/* Any match just on the vendor ? */
if (sub_vendor_id)
proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
NULL, 0);
return proto;
}
static const struct scmi_protocol *
scmi_protocol_get(int protocol_id, struct scmi_revision_info *version)
{
const struct scmi_protocol *proto = NULL;
if (protocol_id < SCMI_PROTOCOL_VENDOR_BASE)
proto = xa_load(&scmi_protocols, protocol_id);
else
proto = scmi_vendor_protocol_lookup(protocol_id,
version->vendor_id,
version->sub_vendor_id,
version->impl_ver);
if (!proto || !try_module_get(proto->owner)) {
pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
return NULL;
@ -206,21 +289,46 @@ static const struct scmi_protocol *scmi_protocol_get(int protocol_id)
pr_debug("Found SCMI Protocol 0x%x\n", protocol_id);
if (protocol_id >= SCMI_PROTOCOL_VENDOR_BASE)
pr_info("Loaded SCMI Vendor Protocol 0x%x - %s %s %X\n",
protocol_id, proto->vendor_id ?: "",
proto->sub_vendor_id ?: "", proto->impl_ver);
return proto;
}
static void scmi_protocol_put(int protocol_id)
static void scmi_protocol_put(const struct scmi_protocol *proto)
{
const struct scmi_protocol *proto;
proto = idr_find(&scmi_protocols, protocol_id);
if (proto)
module_put(proto->owner);
}
static int scmi_vendor_protocol_check(const struct scmi_protocol *proto)
{
if (!proto->vendor_id) {
pr_err("missing vendor_id for protocol 0x%x\n", proto->id);
return -EINVAL;
}
if (strlen(proto->vendor_id) >= SCMI_SHORT_NAME_MAX_SIZE) {
pr_err("malformed vendor_id for protocol 0x%x\n", proto->id);
return -EINVAL;
}
if (proto->sub_vendor_id &&
strlen(proto->sub_vendor_id) >= SCMI_SHORT_NAME_MAX_SIZE) {
pr_err("malformed sub_vendor_id for protocol 0x%x\n",
proto->id);
return -EINVAL;
}
return 0;
}
int scmi_protocol_register(const struct scmi_protocol *proto)
{
int ret;
unsigned long key;
if (!proto) {
pr_err("invalid protocol\n");
@ -232,12 +340,23 @@ int scmi_protocol_register(const struct scmi_protocol *proto)
return -EINVAL;
}
spin_lock(&protocol_lock);
ret = idr_alloc(&scmi_protocols, (void *)proto,
proto->id, proto->id + 1, GFP_ATOMIC);
spin_unlock(&protocol_lock);
if (ret != proto->id) {
pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
if (proto->id >= SCMI_PROTOCOL_VENDOR_BASE &&
scmi_vendor_protocol_check(proto))
return -EINVAL;
/*
* Calculate a protocol key to register this protocol with the core;
* key value 0 is considered invalid.
*/
key = scmi_protocol_key_calculate(proto->id, proto->vendor_id,
proto->sub_vendor_id,
proto->impl_ver);
if (!key)
return -EINVAL;
ret = xa_insert(&scmi_protocols, key, (void *)proto, GFP_KERNEL);
if (ret) {
pr_err("unable to allocate SCMI protocol slot for 0x%x - err %d\n",
proto->id, ret);
return ret;
}
@ -250,9 +369,15 @@ EXPORT_SYMBOL_GPL(scmi_protocol_register);
void scmi_protocol_unregister(const struct scmi_protocol *proto)
{
spin_lock(&protocol_lock);
idr_remove(&scmi_protocols, proto->id);
spin_unlock(&protocol_lock);
unsigned long key;
key = scmi_protocol_key_calculate(proto->id, proto->vendor_id,
proto->sub_vendor_id,
proto->impl_ver);
if (!key)
return;
xa_erase(&scmi_protocols, key);
pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);
}
@ -696,6 +821,45 @@ scmi_xfer_lookup_unlocked(struct scmi_xfers_info *minfo, u16 xfer_id)
return xfer ?: ERR_PTR(-EINVAL);
}
/**
* scmi_bad_message_trace - A helper to trace weird messages
*
* @cinfo: A reference to the channel descriptor on which the message was
* received
* @msg_hdr: Message header to track
* @err: A specific error code used as a status value in traces.
*
* This helper can be used to trace any kind of weird, incomplete, unexpected,
* timed-out message that arrives and as such, can be traced only referring to
* the header content, since the payload is missing/unreliable.
*/
void scmi_bad_message_trace(struct scmi_chan_info *cinfo, u32 msg_hdr,
enum scmi_bad_msg err)
{
char *tag;
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
switch (MSG_XTRACT_TYPE(msg_hdr)) {
case MSG_TYPE_COMMAND:
tag = "!RESP";
break;
case MSG_TYPE_DELAYED_RESP:
tag = "!DLYD";
break;
case MSG_TYPE_NOTIFICATION:
tag = "!NOTI";
break;
default:
tag = "!UNKN";
break;
}
trace_scmi_msg_dump(info->id, cinfo->id,
MSG_XTRACT_PROT_ID(msg_hdr),
MSG_XTRACT_ID(msg_hdr), tag,
MSG_XTRACT_TOKEN(msg_hdr), err, NULL, 0);
}
/**
* scmi_msg_response_validate - Validate message type against state of related
* xfer
@ -822,6 +986,9 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
"Message for %d type %d is not expected!\n",
xfer_id, msg_type);
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
scmi_bad_message_trace(cinfo, msg_hdr, MSG_UNEXPECTED);
return xfer;
}
refcount_inc(&xfer->users);
@ -846,6 +1013,9 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
dev_err(cinfo->dev,
"Invalid message type:%d for %d - HDR:0x%X state:%d\n",
msg_type, xfer_id, msg_hdr, xfer->state);
scmi_bad_message_trace(cinfo, msg_hdr, MSG_INVALID);
/* On error the refcount incremented above has to be dropped */
__scmi_xfer_put(minfo, xfer);
xfer = ERR_PTR(-EINVAL);
@ -882,6 +1052,9 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
if (IS_ERR(xfer)) {
dev_err(dev, "failed to get free message slot (%ld)\n",
PTR_ERR(xfer));
scmi_bad_message_trace(cinfo, msg_hdr, MSG_NOMEM);
scmi_clear_channel(info, cinfo);
return;
}
@ -1001,6 +1174,7 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv)
break;
default:
WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
scmi_bad_message_trace(cinfo, msg_hdr, MSG_UNKNOWN);
break;
}
}
@ -1488,6 +1662,20 @@ out:
return ret;
}
/**
* scmi_common_get_max_msg_size - Get maximum message size
* @ph: A protocol handle reference.
*
* Return: Maximum message size for the current protocol.
*/
static int scmi_common_get_max_msg_size(const struct scmi_protocol_handle *ph)
{
const struct scmi_protocol_instance *pi = ph_to_pi(ph);
struct scmi_info *info = handle_to_scmi_info(pi->handle);
return info->desc->max_msg_size;
}
/**
* struct scmi_iterator - Iterator descriptor
* @msg: A reference to the message TX buffer; filled by @prepare_message with
@ -1799,6 +1987,7 @@ static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
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,
.iter_response_init = scmi_iterator_init,
.iter_response_run = scmi_iterator_run,
.protocol_msg_check = scmi_protocol_msg_check,
@ -1891,7 +2080,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
/* Protocol specific devres group */
gid = devres_open_group(handle->dev, NULL, GFP_KERNEL);
if (!gid) {
scmi_protocol_put(proto->id);
scmi_protocol_put(proto);
goto out;
}
@ -1955,7 +2144,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
clean:
/* Take care to put the protocol module's owner before releasing all */
scmi_protocol_put(proto->id);
scmi_protocol_put(proto);
devres_release_group(handle->dev, gid);
out:
return ERR_PTR(ret);
@ -1989,7 +2178,7 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
const struct scmi_protocol *proto;
/* Fails if protocol not registered on bus */
proto = scmi_protocol_get(protocol_id);
proto = scmi_protocol_get(protocol_id, &info->version);
if (proto)
pi = scmi_alloc_init_protocol_instance(info, proto);
else
@ -2044,7 +2233,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
idr_remove(&info->protocols, protocol_id);
scmi_protocol_put(protocol_id);
scmi_protocol_put(pi->proto);
devres_release_group(handle->dev, gid);
dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",
@ -2491,6 +2680,10 @@ scmi_txrx_setup(struct scmi_info *info, struct device_node *of_node,
ret = 0;
}
if (ret)
dev_err(info->dev,
"failed to setup channel for protocol:0x%X\n", prot_id);
return ret;
}
@ -2760,6 +2953,7 @@ static int scmi_debugfs_raw_mode_setup(struct scmi_info *info)
static int scmi_probe(struct platform_device *pdev)
{
int ret;
char *err_str = "probe failure\n";
struct scmi_handle *handle;
const struct scmi_desc *desc;
struct scmi_info *info;
@ -2810,27 +3004,37 @@ static int scmi_probe(struct platform_device *pdev)
if (desc->ops->link_supplier) {
ret = desc->ops->link_supplier(dev);
if (ret)
if (ret) {
err_str = "transport not ready\n";
goto clear_ida;
}
}
/* Setup all channels described in the DT at first */
ret = scmi_channels_setup(info);
if (ret)
if (ret) {
err_str = "failed to setup channels\n";
goto clear_ida;
}
ret = bus_register_notifier(&scmi_bus_type, &info->bus_nb);
if (ret)
if (ret) {
err_str = "failed to register bus notifier\n";
goto clear_txrx_setup;
}
ret = blocking_notifier_chain_register(&scmi_requested_devices_nh,
&info->dev_req_nb);
if (ret)
if (ret) {
err_str = "failed to register device notifier\n";
goto clear_bus_notifier;
}
ret = scmi_xfer_info_init(info);
if (ret)
if (ret) {
err_str = "failed to init xfers pool\n";
goto clear_dev_req_notifier;
}
if (scmi_top_dentry) {
info->dbg = scmi_debugfs_common_setup(info);
@ -2867,9 +3071,11 @@ static int scmi_probe(struct platform_device *pdev)
*/
ret = scmi_protocol_acquire(handle, SCMI_PROTOCOL_BASE);
if (ret) {
dev_err(dev, "unable to communicate with SCMI\n");
if (coex)
err_str = "unable to communicate with SCMI\n";
if (coex) {
dev_err(dev, "%s", err_str);
return 0;
}
goto notification_exit;
}
@ -2923,7 +3129,8 @@ clear_txrx_setup:
scmi_cleanup_txrx_channels(info);
clear_ida:
ida_free(&scmi_id, info->id);
return ret;
return dev_err_probe(dev, ret, "%s", err_str);
}
static void scmi_remove(struct platform_device *pdev)
@ -3127,6 +3334,7 @@ static int __init scmi_driver_init(void)
scmi_voltage_register();
scmi_system_register();
scmi_powercap_register();
scmi_pinctrl_register();
return platform_driver_register(&scmi_driver);
}
@ -3144,6 +3352,7 @@ static void __exit scmi_driver_exit(void)
scmi_voltage_unregister();
scmi_system_unregister();
scmi_powercap_unregister();
scmi_pinctrl_unregister();
scmi_transports_exit();

View File

@ -56,6 +56,9 @@ static void rx_callback(struct mbox_client *cl, void *m)
*/
if (cl->knows_txdone && !shmem_channel_free(smbox->shmem)) {
dev_warn(smbox->cinfo->dev, "Ignoring spurious A2P IRQ !\n");
scmi_bad_message_trace(smbox->cinfo,
shmem_read_header(smbox->shmem),
MSG_MBOX_SPURIOUS);
return;
}

View File

@ -1513,17 +1513,12 @@ static int scmi_devm_notifier_register(struct scmi_device *sdev,
static int scmi_devm_notifier_match(struct device *dev, void *res, void *data)
{
struct scmi_notifier_devres *dres = res;
struct scmi_notifier_devres *xres = data;
struct notifier_block *nb = data;
if (WARN_ON(!dres || !xres))
if (WARN_ON(!dres || !nb))
return 0;
return dres->proto_id == xres->proto_id &&
dres->evt_id == xres->evt_id &&
dres->nb == xres->nb &&
((!dres->src_id && !xres->src_id) ||
(dres->src_id && xres->src_id &&
dres->__src_id == xres->__src_id));
return dres->nb == nb;
}
/**
@ -1531,10 +1526,6 @@ static int scmi_devm_notifier_match(struct device *dev, void *res, void *data)
* notifier_block for an event
* @sdev: A reference to an scmi_device whose embedded struct device is to
* be used for devres accounting.
* @proto_id: Protocol ID
* @evt_id: Event ID
* @src_id: Source ID, when NULL register for events coming form ALL possible
* sources
* @nb: A standard notifier block to register for the specified event
*
* Generic devres managed helper to explicitly un-register a notifier_block
@ -1544,25 +1535,12 @@ static int scmi_devm_notifier_match(struct device *dev, void *res, void *data)
* Return: 0 on Success
*/
static int scmi_devm_notifier_unregister(struct scmi_device *sdev,
u8 proto_id, u8 evt_id,
const u32 *src_id,
struct notifier_block *nb)
{
int ret;
struct scmi_notifier_devres dres;
dres.handle = sdev->handle;
dres.proto_id = proto_id;
dres.evt_id = evt_id;
if (src_id) {
dres.__src_id = *src_id;
dres.src_id = &dres.__src_id;
} else {
dres.src_id = NULL;
}
ret = devres_release(&sdev->dev, scmi_devm_release_notifier,
scmi_devm_notifier_match, &dres);
scmi_devm_notifier_match, nb);
WARN_ON(ret);

View File

@ -387,8 +387,8 @@ process_response_opp(struct device *dev, struct perf_dom_info *dom,
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
if (ret)
dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
opp->perf, ret);
dev_warn(dev, "Failed to add opps_by_lvl at %d for %s - ret:%d\n",
opp->perf, dom->info.name, ret);
}
static inline void
@ -405,8 +405,8 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
if (ret)
dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
opp->perf, ret);
dev_warn(dev, "Failed to add opps_by_lvl at %d for %s - ret:%d\n",
opp->perf, dom->info.name, ret);
/* Note that PERF v4 reports always five 32-bit words */
opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq);
@ -417,8 +417,8 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
GFP_KERNEL);
if (ret)
dev_warn(dev,
"Failed to add opps_by_idx at %d - ret:%d\n",
opp->level_index, ret);
"Failed to add opps_by_idx at %d for %s - ret:%d\n",
opp->level_index, dom->info.name, ret);
hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq);
}
@ -879,7 +879,8 @@ static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph,
ret = dev_pm_opp_add_dynamic(dev, &data);
if (ret) {
dev_warn(dev, "failed to add opp %luHz\n", freq);
dev_warn(dev, "[%d][%s]: Failed to add OPP[%d] %lu\n",
domain, dom->info.name, idx, freq);
dev_pm_opp_remove_all_dynamic(dev);
return ret;
}

View File

@ -0,0 +1,916 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface (SCMI) Pinctrl Protocol
*
* Copyright (C) 2024 EPAM
* Copyright 2024 NXP
*/
#include <asm/byteorder.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/scmi_protocol.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include "common.h"
#include "protocols.h"
/* Updated only after ALL the mandatory features for that version are merged */
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000
#define GET_GROUPS_NR(x) le32_get_bits((x), GENMASK(31, 16))
#define GET_PINS_NR(x) le32_get_bits((x), GENMASK(15, 0))
#define GET_FUNCTIONS_NR(x) le32_get_bits((x), GENMASK(15, 0))
#define EXT_NAME_FLAG(x) le32_get_bits((x), BIT(31))
#define NUM_ELEMS(x) le32_get_bits((x), GENMASK(15, 0))
#define REMAINING(x) le32_get_bits((x), GENMASK(31, 16))
#define RETURNED(x) le32_get_bits((x), GENMASK(11, 0))
#define CONFIG_FLAG_MASK GENMASK(19, 18)
#define SELECTOR_MASK GENMASK(17, 16)
#define SKIP_CONFIGS_MASK GENMASK(15, 8)
#define CONFIG_TYPE_MASK GENMASK(7, 0)
enum scmi_pinctrl_protocol_cmd {
PINCTRL_ATTRIBUTES = 0x3,
PINCTRL_LIST_ASSOCIATIONS = 0x4,
PINCTRL_SETTINGS_GET = 0x5,
PINCTRL_SETTINGS_CONFIGURE = 0x6,
PINCTRL_REQUEST = 0x7,
PINCTRL_RELEASE = 0x8,
PINCTRL_NAME_GET = 0x9,
PINCTRL_SET_PERMISSIONS = 0xa,
};
struct scmi_msg_settings_conf {
__le32 identifier;
__le32 function_id;
__le32 attributes;
__le32 configs[];
};
struct scmi_msg_settings_get {
__le32 identifier;
__le32 attributes;
};
struct scmi_resp_settings_get {
__le32 function_selected;
__le32 num_configs;
__le32 configs[];
};
struct scmi_msg_pinctrl_protocol_attributes {
__le32 attributes_low;
__le32 attributes_high;
};
struct scmi_msg_pinctrl_attributes {
__le32 identifier;
__le32 flags;
};
struct scmi_resp_pinctrl_attributes {
__le32 attributes;
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
};
struct scmi_msg_pinctrl_list_assoc {
__le32 identifier;
__le32 flags;
__le32 index;
};
struct scmi_resp_pinctrl_list_assoc {
__le32 flags;
__le16 array[];
};
struct scmi_msg_request {
__le32 identifier;
__le32 flags;
};
struct scmi_group_info {
char name[SCMI_MAX_STR_SIZE];
bool present;
u32 *group_pins;
u32 nr_pins;
};
struct scmi_function_info {
char name[SCMI_MAX_STR_SIZE];
bool present;
u32 *groups;
u32 nr_groups;
};
struct scmi_pin_info {
char name[SCMI_MAX_STR_SIZE];
bool present;
};
struct scmi_pinctrl_info {
u32 version;
int nr_groups;
int nr_functions;
int nr_pins;
struct scmi_group_info *groups;
struct scmi_function_info *functions;
struct scmi_pin_info *pins;
};
static int scmi_pinctrl_attributes_get(const struct scmi_protocol_handle *ph,
struct scmi_pinctrl_info *pi)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_pinctrl_protocol_attributes *attr;
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) {
pi->nr_functions = GET_FUNCTIONS_NR(attr->attributes_high);
pi->nr_groups = GET_GROUPS_NR(attr->attributes_low);
pi->nr_pins = GET_PINS_NR(attr->attributes_low);
if (pi->nr_pins == 0) {
dev_warn(ph->dev, "returned zero pins\n");
ret = -EINVAL;
}
}
ph->xops->xfer_put(ph, t);
return ret;
}
static int scmi_pinctrl_count_get(const struct scmi_protocol_handle *ph,
enum scmi_pinctrl_selector_type type)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
switch (type) {
case PIN_TYPE:
return pi->nr_pins;
case GROUP_TYPE:
return pi->nr_groups;
case FUNCTION_TYPE:
return pi->nr_functions;
default:
return -EINVAL;
}
}
static int scmi_pinctrl_validate_id(const struct scmi_protocol_handle *ph,
u32 selector,
enum scmi_pinctrl_selector_type type)
{
int value;
value = scmi_pinctrl_count_get(ph, type);
if (value < 0)
return value;
if (selector >= value || value == 0)
return -EINVAL;
return 0;
}
static int scmi_pinctrl_attributes(const struct scmi_protocol_handle *ph,
enum scmi_pinctrl_selector_type type,
u32 selector, char *name,
u32 *n_elems)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_pinctrl_attributes *tx;
struct scmi_resp_pinctrl_attributes *rx;
bool ext_name_flag;
if (!name)
return -EINVAL;
ret = scmi_pinctrl_validate_id(ph, selector, type);
if (ret)
return ret;
ret = ph->xops->xfer_get_init(ph, PINCTRL_ATTRIBUTES, sizeof(*tx),
sizeof(*rx), &t);
if (ret)
return ret;
tx = t->tx.buf;
rx = t->rx.buf;
tx->identifier = cpu_to_le32(selector);
tx->flags = cpu_to_le32(type);
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
if (n_elems)
*n_elems = NUM_ELEMS(rx->attributes);
strscpy(name, rx->name, SCMI_SHORT_NAME_MAX_SIZE);
ext_name_flag = !!EXT_NAME_FLAG(rx->attributes);
}
ph->xops->xfer_put(ph, t);
if (ret)
return ret;
/*
* If supported overwrite short name with the extended one;
* on error just carry on and use already provided short name.
*/
if (ext_name_flag)
ret = ph->hops->extended_name_get(ph, PINCTRL_NAME_GET,
selector, (u32 *)&type, name,
SCMI_MAX_STR_SIZE);
return ret;
}
struct scmi_pinctrl_ipriv {
u32 selector;
enum scmi_pinctrl_selector_type type;
u32 *array;
};
static void iter_pinctrl_assoc_prepare_message(void *message,
u32 desc_index,
const void *priv)
{
struct scmi_msg_pinctrl_list_assoc *msg = message;
const struct scmi_pinctrl_ipriv *p = priv;
msg->identifier = cpu_to_le32(p->selector);
msg->flags = cpu_to_le32(p->type);
msg->index = cpu_to_le32(desc_index);
}
static int iter_pinctrl_assoc_update_state(struct scmi_iterator_state *st,
const void *response, void *priv)
{
const struct scmi_resp_pinctrl_list_assoc *r = response;
st->num_returned = RETURNED(r->flags);
st->num_remaining = REMAINING(r->flags);
return 0;
}
static int
iter_pinctrl_assoc_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st, void *priv)
{
const struct scmi_resp_pinctrl_list_assoc *r = response;
struct scmi_pinctrl_ipriv *p = priv;
p->array[st->desc_index + st->loop_idx] =
le16_to_cpu(r->array[st->loop_idx]);
return 0;
}
static int scmi_pinctrl_list_associations(const struct scmi_protocol_handle *ph,
u32 selector,
enum scmi_pinctrl_selector_type type,
u16 size, u32 *array)
{
int ret;
void *iter;
struct scmi_iterator_ops ops = {
.prepare_message = iter_pinctrl_assoc_prepare_message,
.update_state = iter_pinctrl_assoc_update_state,
.process_response = iter_pinctrl_assoc_process_response,
};
struct scmi_pinctrl_ipriv ipriv = {
.selector = selector,
.type = type,
.array = array,
};
if (!array || !size || type == PIN_TYPE)
return -EINVAL;
ret = scmi_pinctrl_validate_id(ph, selector, type);
if (ret)
return ret;
iter = ph->hops->iter_response_init(ph, &ops, size,
PINCTRL_LIST_ASSOCIATIONS,
sizeof(struct scmi_msg_pinctrl_list_assoc),
&ipriv);
if (IS_ERR(iter))
return PTR_ERR(iter);
return ph->hops->iter_response_run(iter);
}
struct scmi_settings_get_ipriv {
u32 selector;
enum scmi_pinctrl_selector_type type;
bool get_all;
unsigned int *nr_configs;
enum scmi_pinctrl_conf_type *config_types;
u32 *config_values;
};
static void
iter_pinctrl_settings_get_prepare_message(void *message, u32 desc_index,
const void *priv)
{
struct scmi_msg_settings_get *msg = message;
const struct scmi_settings_get_ipriv *p = priv;
u32 attributes;
attributes = FIELD_PREP(SELECTOR_MASK, p->type);
if (p->get_all) {
attributes |= FIELD_PREP(CONFIG_FLAG_MASK, 1) |
FIELD_PREP(SKIP_CONFIGS_MASK, desc_index);
} else {
attributes |= FIELD_PREP(CONFIG_TYPE_MASK, p->config_types[0]);
}
msg->attributes = cpu_to_le32(attributes);
msg->identifier = cpu_to_le32(p->selector);
}
static int
iter_pinctrl_settings_get_update_state(struct scmi_iterator_state *st,
const void *response, void *priv)
{
const struct scmi_resp_settings_get *r = response;
struct scmi_settings_get_ipriv *p = priv;
if (p->get_all) {
st->num_returned = le32_get_bits(r->num_configs, GENMASK(7, 0));
st->num_remaining = le32_get_bits(r->num_configs, GENMASK(31, 24));
} else {
st->num_returned = 1;
st->num_remaining = 0;
}
return 0;
}
static int
iter_pinctrl_settings_get_process_response(const struct scmi_protocol_handle *ph,
const void *response,
struct scmi_iterator_state *st,
void *priv)
{
const struct scmi_resp_settings_get *r = response;
struct scmi_settings_get_ipriv *p = priv;
u32 type = le32_get_bits(r->configs[st->loop_idx * 2], GENMASK(7, 0));
u32 val = le32_to_cpu(r->configs[st->loop_idx * 2 + 1]);
if (p->get_all) {
p->config_types[st->desc_index + st->loop_idx] = type;
} else {
if (p->config_types[0] != type)
return -EINVAL;
}
p->config_values[st->desc_index + st->loop_idx] = val;
++*p->nr_configs;
return 0;
}
static int
scmi_pinctrl_settings_get(const struct scmi_protocol_handle *ph, u32 selector,
enum scmi_pinctrl_selector_type type,
unsigned int *nr_configs,
enum scmi_pinctrl_conf_type *config_types,
u32 *config_values)
{
int ret;
void *iter;
unsigned int max_configs = *nr_configs;
struct scmi_iterator_ops ops = {
.prepare_message = iter_pinctrl_settings_get_prepare_message,
.update_state = iter_pinctrl_settings_get_update_state,
.process_response = iter_pinctrl_settings_get_process_response,
};
struct scmi_settings_get_ipriv ipriv = {
.selector = selector,
.type = type,
.get_all = (max_configs > 1),
.nr_configs = nr_configs,
.config_types = config_types,
.config_values = config_values,
};
if (!config_types || !config_values || type == FUNCTION_TYPE)
return -EINVAL;
ret = scmi_pinctrl_validate_id(ph, selector, type);
if (ret)
return ret;
/* Prepare to count returned configs */
*nr_configs = 0;
iter = ph->hops->iter_response_init(ph, &ops, max_configs,
PINCTRL_SETTINGS_GET,
sizeof(struct scmi_msg_settings_get),
&ipriv);
if (IS_ERR(iter))
return PTR_ERR(iter);
return ph->hops->iter_response_run(iter);
}
static int scmi_pinctrl_settings_get_one(const struct scmi_protocol_handle *ph,
u32 selector,
enum scmi_pinctrl_selector_type type,
enum scmi_pinctrl_conf_type config_type,
u32 *config_value)
{
unsigned int nr_configs = 1;
return scmi_pinctrl_settings_get(ph, selector, type, &nr_configs,
&config_type, config_value);
}
static int scmi_pinctrl_settings_get_all(const struct scmi_protocol_handle *ph,
u32 selector,
enum scmi_pinctrl_selector_type type,
unsigned int *nr_configs,
enum scmi_pinctrl_conf_type *config_types,
u32 *config_values)
{
if (!nr_configs || *nr_configs == 0)
return -EINVAL;
return scmi_pinctrl_settings_get(ph, selector, type, nr_configs,
config_types, config_values);
}
static int
scmi_pinctrl_settings_conf(const struct scmi_protocol_handle *ph,
u32 selector,
enum scmi_pinctrl_selector_type type,
u32 nr_configs,
enum scmi_pinctrl_conf_type *config_type,
u32 *config_value)
{
struct scmi_xfer *t;
struct scmi_msg_settings_conf *tx;
u32 attributes;
int ret, i;
u32 configs_in_chunk, conf_num = 0;
u32 chunk;
int max_msg_size = ph->hops->get_max_msg_size(ph);
if (!config_type || !config_value || type == FUNCTION_TYPE)
return -EINVAL;
ret = scmi_pinctrl_validate_id(ph, selector, type);
if (ret)
return ret;
configs_in_chunk = (max_msg_size - sizeof(*tx)) / (sizeof(__le32) * 2);
while (conf_num < nr_configs) {
chunk = (nr_configs - conf_num > configs_in_chunk) ?
configs_in_chunk : nr_configs - conf_num;
ret = ph->xops->xfer_get_init(ph, PINCTRL_SETTINGS_CONFIGURE,
sizeof(*tx) +
chunk * 2 * sizeof(__le32), 0, &t);
if (ret)
break;
tx = t->tx.buf;
tx->identifier = cpu_to_le32(selector);
tx->function_id = cpu_to_le32(0xFFFFFFFF);
attributes = FIELD_PREP(GENMASK(1, 0), type) |
FIELD_PREP(GENMASK(9, 2), chunk);
tx->attributes = cpu_to_le32(attributes);
for (i = 0; i < chunk; i++) {
tx->configs[i * 2] =
cpu_to_le32(config_type[conf_num + i]);
tx->configs[i * 2 + 1] =
cpu_to_le32(config_value[conf_num + i]);
}
ret = ph->xops->do_xfer(ph, t);
ph->xops->xfer_put(ph, t);
if (ret)
break;
conf_num += chunk;
}
return ret;
}
static int scmi_pinctrl_function_select(const struct scmi_protocol_handle *ph,
u32 group,
enum scmi_pinctrl_selector_type type,
u32 function_id)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_settings_conf *tx;
u32 attributes;
ret = scmi_pinctrl_validate_id(ph, group, type);
if (ret)
return ret;
ret = ph->xops->xfer_get_init(ph, PINCTRL_SETTINGS_CONFIGURE,
sizeof(*tx), 0, &t);
if (ret)
return ret;
tx = t->tx.buf;
tx->identifier = cpu_to_le32(group);
tx->function_id = cpu_to_le32(function_id);
attributes = FIELD_PREP(GENMASK(1, 0), type) | BIT(10);
tx->attributes = cpu_to_le32(attributes);
ret = ph->xops->do_xfer(ph, t);
ph->xops->xfer_put(ph, t);
return ret;
}
static int scmi_pinctrl_request_free(const struct scmi_protocol_handle *ph,
u32 identifier,
enum scmi_pinctrl_selector_type type,
enum scmi_pinctrl_protocol_cmd cmd)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_request *tx;
if (type == FUNCTION_TYPE)
return -EINVAL;
if (cmd != PINCTRL_REQUEST && cmd != PINCTRL_RELEASE)
return -EINVAL;
ret = scmi_pinctrl_validate_id(ph, identifier, type);
if (ret)
return ret;
ret = ph->xops->xfer_get_init(ph, cmd, sizeof(*tx), 0, &t);
if (ret)
return ret;
tx = t->tx.buf;
tx->identifier = cpu_to_le32(identifier);
tx->flags = cpu_to_le32(type);
ret = ph->xops->do_xfer(ph, t);
ph->xops->xfer_put(ph, t);
return ret;
}
static int scmi_pinctrl_pin_request(const struct scmi_protocol_handle *ph,
u32 pin)
{
return scmi_pinctrl_request_free(ph, pin, PIN_TYPE, PINCTRL_REQUEST);
}
static int scmi_pinctrl_pin_free(const struct scmi_protocol_handle *ph, u32 pin)
{
return scmi_pinctrl_request_free(ph, pin, PIN_TYPE, PINCTRL_RELEASE);
}
static int scmi_pinctrl_get_group_info(const struct scmi_protocol_handle *ph,
u32 selector,
struct scmi_group_info *group)
{
int ret;
ret = scmi_pinctrl_attributes(ph, GROUP_TYPE, selector, group->name,
&group->nr_pins);
if (ret)
return ret;
if (!group->nr_pins) {
dev_err(ph->dev, "Group %d has 0 elements", selector);
return -ENODATA;
}
group->group_pins = kmalloc_array(group->nr_pins,
sizeof(*group->group_pins),
GFP_KERNEL);
if (!group->group_pins)
return -ENOMEM;
ret = scmi_pinctrl_list_associations(ph, selector, GROUP_TYPE,
group->nr_pins, group->group_pins);
if (ret) {
kfree(group->group_pins);
return ret;
}
group->present = true;
return 0;
}
static int scmi_pinctrl_get_group_name(const struct scmi_protocol_handle *ph,
u32 selector, const char **name)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
if (!name)
return -EINVAL;
if (selector >= pi->nr_groups || pi->nr_groups == 0)
return -EINVAL;
if (!pi->groups[selector].present) {
int ret;
ret = scmi_pinctrl_get_group_info(ph, selector,
&pi->groups[selector]);
if (ret)
return ret;
}
*name = pi->groups[selector].name;
return 0;
}
static int scmi_pinctrl_group_pins_get(const struct scmi_protocol_handle *ph,
u32 selector, const u32 **pins,
u32 *nr_pins)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
if (!pins || !nr_pins)
return -EINVAL;
if (selector >= pi->nr_groups || pi->nr_groups == 0)
return -EINVAL;
if (!pi->groups[selector].present) {
int ret;
ret = scmi_pinctrl_get_group_info(ph, selector,
&pi->groups[selector]);
if (ret)
return ret;
}
*pins = pi->groups[selector].group_pins;
*nr_pins = pi->groups[selector].nr_pins;
return 0;
}
static int scmi_pinctrl_get_function_info(const struct scmi_protocol_handle *ph,
u32 selector,
struct scmi_function_info *func)
{
int ret;
ret = scmi_pinctrl_attributes(ph, FUNCTION_TYPE, selector, func->name,
&func->nr_groups);
if (ret)
return ret;
if (!func->nr_groups) {
dev_err(ph->dev, "Function %d has 0 elements", selector);
return -ENODATA;
}
func->groups = kmalloc_array(func->nr_groups, sizeof(*func->groups),
GFP_KERNEL);
if (!func->groups)
return -ENOMEM;
ret = scmi_pinctrl_list_associations(ph, selector, FUNCTION_TYPE,
func->nr_groups, func->groups);
if (ret) {
kfree(func->groups);
return ret;
}
func->present = true;
return 0;
}
static int scmi_pinctrl_get_function_name(const struct scmi_protocol_handle *ph,
u32 selector, const char **name)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
if (!name)
return -EINVAL;
if (selector >= pi->nr_functions || pi->nr_functions == 0)
return -EINVAL;
if (!pi->functions[selector].present) {
int ret;
ret = scmi_pinctrl_get_function_info(ph, selector,
&pi->functions[selector]);
if (ret)
return ret;
}
*name = pi->functions[selector].name;
return 0;
}
static int
scmi_pinctrl_function_groups_get(const struct scmi_protocol_handle *ph,
u32 selector, u32 *nr_groups,
const u32 **groups)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
if (!groups || !nr_groups)
return -EINVAL;
if (selector >= pi->nr_functions || pi->nr_functions == 0)
return -EINVAL;
if (!pi->functions[selector].present) {
int ret;
ret = scmi_pinctrl_get_function_info(ph, selector,
&pi->functions[selector]);
if (ret)
return ret;
}
*groups = pi->functions[selector].groups;
*nr_groups = pi->functions[selector].nr_groups;
return 0;
}
static int scmi_pinctrl_mux_set(const struct scmi_protocol_handle *ph,
u32 selector, u32 group)
{
return scmi_pinctrl_function_select(ph, group, GROUP_TYPE, selector);
}
static int scmi_pinctrl_get_pin_info(const struct scmi_protocol_handle *ph,
u32 selector, struct scmi_pin_info *pin)
{
int ret;
if (!pin)
return -EINVAL;
ret = scmi_pinctrl_attributes(ph, PIN_TYPE, selector, pin->name, NULL);
if (ret)
return ret;
pin->present = true;
return 0;
}
static int scmi_pinctrl_get_pin_name(const struct scmi_protocol_handle *ph,
u32 selector, const char **name)
{
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
if (!name)
return -EINVAL;
if (selector >= pi->nr_pins)
return -EINVAL;
if (!pi->pins[selector].present) {
int ret;
ret = scmi_pinctrl_get_pin_info(ph, selector, &pi->pins[selector]);
if (ret)
return ret;
}
*name = pi->pins[selector].name;
return 0;
}
static int scmi_pinctrl_name_get(const struct scmi_protocol_handle *ph,
u32 selector,
enum scmi_pinctrl_selector_type type,
const char **name)
{
switch (type) {
case PIN_TYPE:
return scmi_pinctrl_get_pin_name(ph, selector, name);
case GROUP_TYPE:
return scmi_pinctrl_get_group_name(ph, selector, name);
case FUNCTION_TYPE:
return scmi_pinctrl_get_function_name(ph, selector, name);
default:
return -EINVAL;
}
}
static const struct scmi_pinctrl_proto_ops pinctrl_proto_ops = {
.count_get = scmi_pinctrl_count_get,
.name_get = scmi_pinctrl_name_get,
.group_pins_get = scmi_pinctrl_group_pins_get,
.function_groups_get = scmi_pinctrl_function_groups_get,
.mux_set = scmi_pinctrl_mux_set,
.settings_get_one = scmi_pinctrl_settings_get_one,
.settings_get_all = scmi_pinctrl_settings_get_all,
.settings_conf = scmi_pinctrl_settings_conf,
.pin_request = scmi_pinctrl_pin_request,
.pin_free = scmi_pinctrl_pin_free,
};
static int scmi_pinctrl_protocol_init(const struct scmi_protocol_handle *ph)
{
int ret;
u32 version;
struct scmi_pinctrl_info *pinfo;
ret = ph->xops->version_get(ph, &version);
if (ret)
return ret;
dev_dbg(ph->dev, "Pinctrl Version %d.%d\n",
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
return -ENOMEM;
ret = scmi_pinctrl_attributes_get(ph, pinfo);
if (ret)
return ret;
pinfo->pins = devm_kcalloc(ph->dev, pinfo->nr_pins,
sizeof(*pinfo->pins), GFP_KERNEL);
if (!pinfo->pins)
return -ENOMEM;
pinfo->groups = devm_kcalloc(ph->dev, pinfo->nr_groups,
sizeof(*pinfo->groups), GFP_KERNEL);
if (!pinfo->groups)
return -ENOMEM;
pinfo->functions = devm_kcalloc(ph->dev, pinfo->nr_functions,
sizeof(*pinfo->functions), GFP_KERNEL);
if (!pinfo->functions)
return -ENOMEM;
pinfo->version = version;
return ph->set_priv(ph, pinfo, version);
}
static int scmi_pinctrl_protocol_deinit(const struct scmi_protocol_handle *ph)
{
int i;
struct scmi_pinctrl_info *pi = ph->get_priv(ph);
/* Free groups_pins allocated in scmi_pinctrl_get_group_info */
for (i = 0; i < pi->nr_groups; i++) {
if (pi->groups[i].present) {
kfree(pi->groups[i].group_pins);
pi->groups[i].present = false;
}
}
/* Free groups allocated in scmi_pinctrl_get_function_info */
for (i = 0; i < pi->nr_functions; i++) {
if (pi->functions[i].present) {
kfree(pi->functions[i].groups);
pi->functions[i].present = false;
}
}
return 0;
}
static const struct scmi_protocol scmi_pinctrl = {
.id = SCMI_PROTOCOL_PINCTRL,
.owner = THIS_MODULE,
.instance_init = &scmi_pinctrl_protocol_init,
.instance_deinit = &scmi_pinctrl_protocol_deinit,
.ops = &pinctrl_proto_ops,
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(pinctrl, scmi_pinctrl)

View File

@ -29,6 +29,8 @@
#define PROTOCOL_REV_MAJOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))))
#define PROTOCOL_REV_MINOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x))))
#define SCMI_PROTOCOL_VENDOR_BASE 0x80
enum scmi_common_cmd {
PROTOCOL_VERSION = 0x0,
PROTOCOL_ATTRIBUTES = 0x1,
@ -258,6 +260,7 @@ struct scmi_fc_info {
* @fastchannel_init: A common helper used to initialize FC descriptors by
* gathering FC descriptions from the SCMI platform server.
* @fastchannel_db_ring: A common helper to ring a FC doorbell.
* @get_max_msg_size: A common helper to get the maximum message size.
*/
struct scmi_proto_helpers_ops {
int (*extended_name_get)(const struct scmi_protocol_handle *ph,
@ -277,6 +280,7 @@ struct scmi_proto_helpers_ops {
struct scmi_fc_db_info **p_db,
u32 *rate_limit);
void (*fastchannel_db_ring)(struct scmi_fc_db_info *db);
int (*get_max_msg_size)(const struct scmi_protocol_handle *ph);
};
/**
@ -323,6 +327,16 @@ typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
* protocol by the agent. Each protocol implementation
* in the agent is supposed to downgrade to match the
* protocol version supported by the platform.
* @vendor_id: A firmware vendor string for vendor protocols matching.
* Ignored when @id identifies a standard protocol, cannot be NULL
* otherwise.
* @sub_vendor_id: A firmware sub_vendor string for vendor protocols matching.
* Ignored if NULL or when @id identifies a standard protocol.
* @impl_ver: A firmware implementation version for vendor protocols matching.
* Ignored if zero or if @id identifies a standard protocol.
*
* Note that vendor protocols matching at load time is performed by attempting
* the closest match first against the tuple (vendor, sub_vendor, impl_ver)
*/
struct scmi_protocol {
const u8 id;
@ -332,6 +346,9 @@ struct scmi_protocol {
const void *ops;
const struct scmi_protocol_events *events;
unsigned int supported_version;
char *vendor_id;
char *sub_vendor_id;
u32 impl_ver;
};
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
@ -353,6 +370,7 @@ void __exit scmi_##name##_unregister(void) \
DECLARE_SCMI_REGISTER_UNREGISTER(base);
DECLARE_SCMI_REGISTER_UNREGISTER(clock);
DECLARE_SCMI_REGISTER_UNREGISTER(perf);
DECLARE_SCMI_REGISTER_UNREGISTER(pinctrl);
DECLARE_SCMI_REGISTER_UNREGISTER(power);
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);

View File

@ -4,6 +4,8 @@
*/
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/cpumask.h>
@ -114,6 +116,10 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
#define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0)
#define QCOM_SMC_WAITQ_FLAG_WAKE_ALL BIT(1)
#define QCOM_DLOAD_MASK GENMASK(5, 4)
#define QCOM_DLOAD_NODUMP 0
#define QCOM_DLOAD_FULLDUMP 1
static const char * const qcom_scm_convention_names[] = {
[SMC_CONVENTION_UNKNOWN] = "unknown",
[SMC_CONVENTION_ARM_32] = "smc arm 32",
@ -163,9 +169,6 @@ static int qcom_scm_bw_enable(void)
if (!__scm->path)
return 0;
if (IS_ERR(__scm->path))
return -EINVAL;
mutex_lock(&__scm->scm_bw_lock);
if (!__scm->scm_vote_count) {
ret = icc_set_bw(__scm->path, 0, UINT_MAX);
@ -183,7 +186,7 @@ err_bw:
static void qcom_scm_bw_disable(void)
{
if (IS_ERR_OR_NULL(__scm->path))
if (!__scm->path)
return;
mutex_lock(&__scm->scm_bw_lock);
@ -496,19 +499,32 @@ static int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
}
static int qcom_scm_io_rmw(phys_addr_t addr, unsigned int mask, unsigned int val)
{
unsigned int old;
unsigned int new;
int ret;
ret = qcom_scm_io_readl(addr, &old);
if (ret)
return ret;
new = (old & ~mask) | (val & mask);
return qcom_scm_io_writel(addr, new);
}
static void qcom_scm_set_download_mode(bool enable)
{
bool avail;
u32 val = enable ? QCOM_DLOAD_FULLDUMP : QCOM_DLOAD_NODUMP;
int ret = 0;
avail = __qcom_scm_is_call_available(__scm->dev,
QCOM_SCM_SVC_BOOT,
QCOM_SCM_BOOT_SET_DLOAD_MODE);
if (avail) {
if (__scm->dload_mode_addr) {
ret = qcom_scm_io_rmw(__scm->dload_mode_addr, QCOM_DLOAD_MASK,
FIELD_PREP(QCOM_DLOAD_MASK, val));
} else if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_BOOT,
QCOM_SCM_BOOT_SET_DLOAD_MODE)) {
ret = __qcom_scm_set_dload_mode(__scm->dev, enable);
} else if (__scm->dload_mode_addr) {
ret = qcom_scm_io_writel(__scm->dload_mode_addr,
enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0);
} else {
dev_err(__scm->dev,
"No available mechanism for setting download mode\n");
@ -557,10 +573,9 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
*/
mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
GFP_KERNEL);
if (!mdata_buf) {
dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
if (!mdata_buf)
return -ENOMEM;
}
memcpy(mdata_buf, metadata, size);
ret = qcom_scm_clk_enable();
@ -569,13 +584,14 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
ret = qcom_scm_bw_enable();
if (ret)
return ret;
goto disable_clk;
desc.args[1] = mdata_phys;
ret = qcom_scm_call(__scm->dev, &desc, &res);
qcom_scm_bw_disable();
disable_clk:
qcom_scm_clk_disable();
out:
@ -637,10 +653,12 @@ int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
ret = qcom_scm_bw_enable();
if (ret)
return ret;
goto disable_clk;
ret = qcom_scm_call(__scm->dev, &desc, &res);
qcom_scm_bw_disable();
disable_clk:
qcom_scm_clk_disable();
return ret ? : res.result[0];
@ -672,10 +690,12 @@ int qcom_scm_pas_auth_and_reset(u32 peripheral)
ret = qcom_scm_bw_enable();
if (ret)
return ret;
goto disable_clk;
ret = qcom_scm_call(__scm->dev, &desc, &res);
qcom_scm_bw_disable();
disable_clk:
qcom_scm_clk_disable();
return ret ? : res.result[0];
@ -706,11 +726,12 @@ int qcom_scm_pas_shutdown(u32 peripheral)
ret = qcom_scm_bw_enable();
if (ret)
return ret;
goto disable_clk;
ret = qcom_scm_call(__scm->dev, &desc, &res);
qcom_scm_bw_disable();
disable_clk:
qcom_scm_clk_disable();
return ret ? : res.result[0];
@ -1624,8 +1645,10 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send);
* We do not yet support re-entrant calls via the qseecom interface. To prevent
+ any potential issues with this, only allow validated machines for now.
*/
static const struct of_device_id qcom_scm_qseecom_allowlist[] = {
static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
{ .compatible = "lenovo,flex-5g" },
{ .compatible = "lenovo,thinkpad-x13s", },
{ .compatible = "qcom,sc8180x-primus" },
{ }
};
@ -1713,7 +1736,7 @@ static int qcom_scm_qseecom_init(struct qcom_scm *scm)
*/
bool qcom_scm_is_available(void)
{
return !!__scm;
return !!READ_ONCE(__scm);
}
EXPORT_SYMBOL_GPL(qcom_scm_is_available);
@ -1744,7 +1767,7 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
return 0;
}
static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx)
static int qcom_scm_waitq_wakeup(unsigned int wq_ctx)
{
int ret;
@ -1776,7 +1799,7 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data)
goto out;
}
ret = qcom_scm_waitq_wakeup(scm, wq_ctx);
ret = qcom_scm_waitq_wakeup(wq_ctx);
if (ret)
goto out;
} while (more_pending);
@ -1794,10 +1817,12 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (!scm)
return -ENOMEM;
scm->dev = &pdev->dev;
ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
if (ret < 0)
return ret;
init_completion(&scm->waitq_comp);
mutex_init(&scm->scm_bw_lock);
scm->path = devm_of_icc_get(&pdev->dev, NULL);
@ -1829,10 +1854,8 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (ret)
return ret;
__scm = scm;
__scm->dev = &pdev->dev;
init_completion(&__scm->waitq_comp);
/* Let all above stores be available after this */
smp_store_release(&__scm, scm);
irq = platform_get_irq_optional(pdev, 0);
if (irq < 0) {

View File

@ -87,7 +87,6 @@ struct ti_sci_desc {
* struct ti_sci_info - Structure representing a TI SCI instance
* @dev: Device pointer
* @desc: SoC description for this instance
* @nb: Reboot Notifier block
* @d: Debugfs file entry
* @debug_region: Memory region where the debug message are available
* @debug_region_size: Debug region size
@ -103,7 +102,6 @@ struct ti_sci_desc {
*/
struct ti_sci_info {
struct device *dev;
struct notifier_block nb;
const struct ti_sci_desc *desc;
struct dentry *d;
void __iomem *debug_region;
@ -122,7 +120,6 @@ struct ti_sci_info {
#define cl_to_ti_sci_info(c) container_of(c, struct ti_sci_info, cl)
#define handle_to_ti_sci_info(h) container_of(h, struct ti_sci_info, handle)
#define reboot_to_ti_sci_info(n) container_of(n, struct ti_sci_info, nb)
#ifdef CONFIG_DEBUG_FS
@ -3254,10 +3251,9 @@ devm_ti_sci_get_resource(const struct ti_sci_handle *handle, struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_ti_sci_get_resource);
static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
void *cmd)
static int tisci_reboot_handler(struct sys_off_data *data)
{
struct ti_sci_info *info = reboot_to_ti_sci_info(nb);
struct ti_sci_info *info = data->cb_data;
const struct ti_sci_handle *handle = &info->handle;
ti_sci_cmd_core_reboot(handle);
@ -3303,7 +3299,6 @@ static int ti_sci_probe(struct platform_device *pdev)
struct mbox_client *cl;
int ret = -EINVAL;
int i;
int reboot = 0;
u32 h_id;
desc = device_get_match_data(dev);
@ -3327,8 +3322,6 @@ static int ti_sci_probe(struct platform_device *pdev)
}
}
reboot = of_property_read_bool(dev->of_node,
"ti,system-reboot-controller");
INIT_LIST_HEAD(&info->node);
minfo = &info->minfo;
@ -3399,15 +3392,10 @@ static int ti_sci_probe(struct platform_device *pdev)
ti_sci_setup_ops(info);
if (reboot) {
info->nb.notifier_call = tisci_reboot_handler;
info->nb.priority = 128;
ret = register_restart_handler(&info->nb);
if (ret) {
dev_err(dev, "reboot registration fail(%d)\n", ret);
goto out;
}
ret = devm_register_restart_handler(dev, tisci_reboot_handler, info);
if (ret) {
dev_err(dev, "reboot registration fail(%d)\n", ret);
goto out;
}
dev_info(dev, "ABI: %d.%d (firmware rev 0x%04x '%s')\n",

View File

@ -243,6 +243,7 @@ static const struct of_device_id brcmstb_memc_of_match[] = {
},
{}
};
MODULE_DEVICE_TABLE(of, brcmstb_memc_of_match);
static int brcmstb_memc_suspend(struct device *dev)
{

View File

@ -450,6 +450,7 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = {
{.compatible = "mediatek,mt8195-smi-larb", .data = &mtk_smi_larb_mt8195},
{}
};
MODULE_DEVICE_TABLE(of, mtk_smi_larb_of_ids);
static int mtk_smi_larb_sleep_ctrl_enable(struct mtk_smi_larb *larb)
{
@ -735,6 +736,7 @@ static const struct of_device_id mtk_smi_common_of_ids[] = {
{.compatible = "mediatek,mt8365-smi-common", .data = &mtk_smi_common_mt8365},
{}
};
MODULE_DEVICE_TABLE(of, mtk_smi_common_of_ids);
static int mtk_smi_common_probe(struct platform_device *pdev)
{

View File

@ -1252,6 +1252,7 @@ DEFINE_SIMPLE_PROP(backlight, "backlight", NULL)
DEFINE_SIMPLE_PROP(panel, "panel", NULL)
DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells")
DEFINE_SIMPLE_PROP(post_init_providers, "post-init-providers", NULL)
DEFINE_SIMPLE_PROP(access_controllers, "access-controllers", "#access-controller-cells")
DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
@ -1359,6 +1360,7 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_msi_parent, },
{ .parse_prop = parse_gpio_compat, },
{ .parse_prop = parse_interrupts, },
{ .parse_prop = parse_access_controllers, },
{ .parse_prop = parse_regulators, },
{ .parse_prop = parse_gpio, },
{ .parse_prop = parse_gpios, },

View File

@ -235,13 +235,13 @@ config PINCTRL_INGENIC
config PINCTRL_K210
bool "Pinctrl driver for the Canaan Kendryte K210 SoC"
depends on RISCV && SOC_CANAAN && OF
depends on RISCV && SOC_CANAAN_K210 && OF
select GENERIC_PINMUX_FUNCTIONS
select GENERIC_PINCONF
select GPIOLIB
select OF_GPIO
select REGMAP_MMIO
default SOC_CANAAN
default SOC_CANAAN_K210
help
Add support for the Canaan Kendryte K210 RISC-V SOC Field
Programmable IO Array (FPIOA) controller.
@ -450,6 +450,17 @@ config PINCTRL_ROCKCHIP
help
This support pinctrl and GPIO driver for Rockchip SoCs.
config PINCTRL_SCMI
tristate "Pinctrl driver using SCMI protocol interface"
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
select PINMUX
select GENERIC_PINCONF
help
This driver provides support for pinctrl which is controlled
by firmware that implements the SCMI interface.
It uses SCMI Message Protocol to interact with the
firmware providing all the pinctrl controls.
config PINCTRL_SINGLE
tristate "One-register-per-pin type device tree based pinctrl driver"
depends on OF

View File

@ -45,6 +45,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o
obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o

View File

@ -0,0 +1,571 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Power Interface (SCMI) Protocol based pinctrl driver
*
* Copyright (C) 2024 EPAM
* Copyright 2024 NXP
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/scmi_protocol.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include "pinctrl-utils.h"
#include "core.h"
#include "pinconf.h"
#define DRV_NAME "scmi-pinctrl"
/* Define num configs, if not large than 4 use stack, else use kcalloc() */
#define SCMI_NUM_CONFIGS 4
static const struct scmi_pinctrl_proto_ops *pinctrl_ops;
struct scmi_pinctrl {
struct device *dev;
struct scmi_protocol_handle *ph;
struct pinctrl_dev *pctldev;
struct pinctrl_desc pctl_desc;
struct pinfunction *functions;
unsigned int nr_functions;
struct pinctrl_pin_desc *pins;
unsigned int nr_pins;
};
static int pinctrl_scmi_get_groups_count(struct pinctrl_dev *pctldev)
{
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
return pinctrl_ops->count_get(pmx->ph, GROUP_TYPE);
}
static const char *pinctrl_scmi_get_group_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
int ret;
const char *name;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
ret = pinctrl_ops->name_get(pmx->ph, selector, GROUP_TYPE, &name);
if (ret) {
dev_err(pmx->dev, "get name failed with err %d", ret);
return NULL;
}
return name;
}
static int pinctrl_scmi_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int selector,
const unsigned int **pins,
unsigned int *num_pins)
{
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
return pinctrl_ops->group_pins_get(pmx->ph, selector, pins, num_pins);
}
static const struct pinctrl_ops pinctrl_scmi_pinctrl_ops = {
.get_groups_count = pinctrl_scmi_get_groups_count,
.get_group_name = pinctrl_scmi_get_group_name,
.get_group_pins = pinctrl_scmi_get_group_pins,
#ifdef CONFIG_OF
.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
.dt_free_map = pinconf_generic_dt_free_map,
#endif
};
static int pinctrl_scmi_get_functions_count(struct pinctrl_dev *pctldev)
{
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
return pinctrl_ops->count_get(pmx->ph, FUNCTION_TYPE);
}
static const char *pinctrl_scmi_get_function_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
int ret;
const char *name;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
ret = pinctrl_ops->name_get(pmx->ph, selector, FUNCTION_TYPE, &name);
if (ret) {
dev_err(pmx->dev, "get name failed with err %d", ret);
return NULL;
}
return name;
}
static int pinctrl_scmi_get_function_groups(struct pinctrl_dev *pctldev,
unsigned int selector,
const char * const **p_groups,
unsigned int * const p_num_groups)
{
struct pinfunction *func;
const unsigned int *group_ids;
unsigned int num_groups;
const char **groups;
int ret, i;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
if (!p_groups || !p_num_groups)
return -EINVAL;
if (selector >= pmx->nr_functions)
return -EINVAL;
func = &pmx->functions[selector];
if (func->ngroups)
goto done;
ret = pinctrl_ops->function_groups_get(pmx->ph, selector, &num_groups,
&group_ids);
if (ret) {
dev_err(pmx->dev, "Unable to get function groups, err %d", ret);
return ret;
}
if (!num_groups)
return -EINVAL;
groups = kcalloc(num_groups, sizeof(*groups), GFP_KERNEL);
if (!groups)
return -ENOMEM;
for (i = 0; i < num_groups; i++) {
groups[i] = pinctrl_scmi_get_group_name(pctldev, group_ids[i]);
if (!groups[i]) {
ret = -EINVAL;
goto err_free;
}
}
func->ngroups = num_groups;
func->groups = groups;
done:
*p_groups = func->groups;
*p_num_groups = func->ngroups;
return 0;
err_free:
kfree(groups);
return ret;
}
static int pinctrl_scmi_func_set_mux(struct pinctrl_dev *pctldev,
unsigned int selector, unsigned int group)
{
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
return pinctrl_ops->mux_set(pmx->ph, selector, group);
}
static int pinctrl_scmi_request(struct pinctrl_dev *pctldev,
unsigned int offset)
{
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
return pinctrl_ops->pin_request(pmx->ph, offset);
}
static int pinctrl_scmi_free(struct pinctrl_dev *pctldev, unsigned int offset)
{
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
return pinctrl_ops->pin_free(pmx->ph, offset);
}
static const struct pinmux_ops pinctrl_scmi_pinmux_ops = {
.request = pinctrl_scmi_request,
.free = pinctrl_scmi_free,
.get_functions_count = pinctrl_scmi_get_functions_count,
.get_function_name = pinctrl_scmi_get_function_name,
.get_function_groups = pinctrl_scmi_get_function_groups,
.set_mux = pinctrl_scmi_func_set_mux,
};
static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param,
enum scmi_pinctrl_conf_type *type)
{
u32 arg = param;
switch (arg) {
case PIN_CONFIG_BIAS_BUS_HOLD:
*type = SCMI_PIN_BIAS_BUS_HOLD;
break;
case PIN_CONFIG_BIAS_DISABLE:
*type = SCMI_PIN_BIAS_DISABLE;
break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
*type = SCMI_PIN_BIAS_HIGH_IMPEDANCE;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
*type = SCMI_PIN_BIAS_PULL_DOWN;
break;
case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
*type = SCMI_PIN_BIAS_PULL_DEFAULT;
break;
case PIN_CONFIG_BIAS_PULL_UP:
*type = SCMI_PIN_BIAS_PULL_UP;
break;
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
*type = SCMI_PIN_DRIVE_OPEN_DRAIN;
break;
case PIN_CONFIG_DRIVE_OPEN_SOURCE:
*type = SCMI_PIN_DRIVE_OPEN_SOURCE;
break;
case PIN_CONFIG_DRIVE_PUSH_PULL:
*type = SCMI_PIN_DRIVE_PUSH_PULL;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
*type = SCMI_PIN_DRIVE_STRENGTH;
break;
case PIN_CONFIG_DRIVE_STRENGTH_UA:
*type = SCMI_PIN_DRIVE_STRENGTH;
break;
case PIN_CONFIG_INPUT_DEBOUNCE:
*type = SCMI_PIN_INPUT_DEBOUNCE;
break;
case PIN_CONFIG_INPUT_ENABLE:
*type = SCMI_PIN_INPUT_MODE;
break;
case PIN_CONFIG_INPUT_SCHMITT:
*type = SCMI_PIN_INPUT_SCHMITT;
break;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
*type = SCMI_PIN_INPUT_MODE;
break;
case PIN_CONFIG_MODE_LOW_POWER:
*type = SCMI_PIN_LOW_POWER_MODE;
break;
case PIN_CONFIG_OUTPUT:
*type = SCMI_PIN_OUTPUT_VALUE;
break;
case PIN_CONFIG_OUTPUT_ENABLE:
*type = SCMI_PIN_OUTPUT_MODE;
break;
case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS:
*type = SCMI_PIN_OUTPUT_VALUE;
break;
case PIN_CONFIG_POWER_SOURCE:
*type = SCMI_PIN_POWER_SOURCE;
break;
case PIN_CONFIG_SLEW_RATE:
*type = SCMI_PIN_SLEW_RATE;
break;
case SCMI_PIN_OEM_START ... SCMI_PIN_OEM_END:
*type = arg;
break;
default:
return -EINVAL;
}
return 0;
}
static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *config)
{
int ret;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
enum pin_config_param config_type;
enum scmi_pinctrl_conf_type type;
u32 config_value;
if (!config)
return -EINVAL;
config_type = pinconf_to_config_param(*config);
ret = pinctrl_scmi_map_pinconf_type(config_type, &type);
if (ret)
return ret;
ret = pinctrl_ops->settings_get_one(pmx->ph, pin, PIN_TYPE, type,
&config_value);
/* Convert SCMI error code to PINCTRL expected error code */
if (ret == -EOPNOTSUPP)
return -ENOTSUPP;
if (ret)
return ret;
*config = pinconf_to_config_packed(config_type, config_value);
return 0;
}
static int
pinctrl_scmi_alloc_configs(struct pinctrl_dev *pctldev, u32 num_configs,
u32 **p_config_value,
enum scmi_pinctrl_conf_type **p_config_type)
{
if (num_configs <= SCMI_NUM_CONFIGS)
return 0;
*p_config_value = kcalloc(num_configs, sizeof(**p_config_value), GFP_KERNEL);
if (!*p_config_value)
return -ENOMEM;
*p_config_type = kcalloc(num_configs, sizeof(**p_config_type), GFP_KERNEL);
if (!*p_config_type) {
kfree(*p_config_value);
return -ENOMEM;
}
return 0;
}
static void
pinctrl_scmi_free_configs(struct pinctrl_dev *pctldev, u32 num_configs,
u32 **p_config_value,
enum scmi_pinctrl_conf_type **p_config_type)
{
if (num_configs <= SCMI_NUM_CONFIGS)
return;
kfree(*p_config_value);
kfree(*p_config_type);
}
static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev,
unsigned int pin,
unsigned long *configs,
unsigned int num_configs)
{
int i, ret;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS];
u32 config_value[SCMI_NUM_CONFIGS];
enum scmi_pinctrl_conf_type *p_config_type = config_type;
u32 *p_config_value = config_value;
enum pin_config_param param;
if (!configs || !num_configs)
return -EINVAL;
ret = pinctrl_scmi_alloc_configs(pctldev, num_configs, &p_config_type,
&p_config_value);
if (ret)
return ret;
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]);
if (ret) {
dev_err(pmx->dev, "Error map pinconf_type %d\n", ret);
goto free_config;
}
p_config_value[i] = pinconf_to_config_argument(configs[i]);
}
ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, num_configs,
p_config_type, p_config_value);
if (ret)
dev_err(pmx->dev, "Error parsing config %d\n", ret);
free_config:
pinctrl_scmi_free_configs(pctldev, num_configs, &p_config_type,
&p_config_value);
return ret;
}
static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev,
unsigned int group,
unsigned long *configs,
unsigned int num_configs)
{
int i, ret;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS];
u32 config_value[SCMI_NUM_CONFIGS];
enum scmi_pinctrl_conf_type *p_config_type = config_type;
u32 *p_config_value = config_value;
enum pin_config_param param;
if (!configs || !num_configs)
return -EINVAL;
ret = pinctrl_scmi_alloc_configs(pctldev, num_configs, &p_config_type,
&p_config_value);
if (ret)
return ret;
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]);
if (ret) {
dev_err(pmx->dev, "Error map pinconf_type %d\n", ret);
goto free_config;
}
p_config_value[i] = pinconf_to_config_argument(configs[i]);
}
ret = pinctrl_ops->settings_conf(pmx->ph, group, GROUP_TYPE,
num_configs, p_config_type,
p_config_value);
if (ret)
dev_err(pmx->dev, "Error parsing config %d", ret);
free_config:
pinctrl_scmi_free_configs(pctldev, num_configs, &p_config_type,
&p_config_value);
return ret;
};
static int pinctrl_scmi_pinconf_group_get(struct pinctrl_dev *pctldev,
unsigned int group,
unsigned long *config)
{
int ret;
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
enum pin_config_param config_type;
enum scmi_pinctrl_conf_type type;
u32 config_value;
if (!config)
return -EINVAL;
config_type = pinconf_to_config_param(*config);
ret = pinctrl_scmi_map_pinconf_type(config_type, &type);
if (ret) {
dev_err(pmx->dev, "Error map pinconf_type %d\n", ret);
return ret;
}
ret = pinctrl_ops->settings_get_one(pmx->ph, group, GROUP_TYPE, type,
&config_value);
/* Convert SCMI error code to PINCTRL expected error code */
if (ret == -EOPNOTSUPP)
return -ENOTSUPP;
if (ret)
return ret;
*config = pinconf_to_config_packed(config_type, config_value);
return 0;
}
static const struct pinconf_ops pinctrl_scmi_pinconf_ops = {
.is_generic = true,
.pin_config_get = pinctrl_scmi_pinconf_get,
.pin_config_set = pinctrl_scmi_pinconf_set,
.pin_config_group_set = pinctrl_scmi_pinconf_group_set,
.pin_config_group_get = pinctrl_scmi_pinconf_group_get,
.pin_config_config_dbg_show = pinconf_generic_dump_config,
};
static int pinctrl_scmi_get_pins(struct scmi_pinctrl *pmx,
struct pinctrl_desc *desc)
{
struct pinctrl_pin_desc *pins;
unsigned int npins;
int ret, i;
npins = pinctrl_ops->count_get(pmx->ph, PIN_TYPE);
/*
* npins will never be zero, the scmi pinctrl driver has bailed out
* if npins is zero.
*/
pins = devm_kmalloc_array(pmx->dev, npins, sizeof(*pins), GFP_KERNEL);
if (!pins)
return -ENOMEM;
for (i = 0; i < npins; i++) {
pins[i].number = i;
/*
* The memory for name is handled by the scmi firmware driver,
* no need free here
*/
ret = pinctrl_ops->name_get(pmx->ph, i, PIN_TYPE, &pins[i].name);
if (ret)
return dev_err_probe(pmx->dev, ret,
"Can't get name for pin %d", i);
}
desc->npins = npins;
desc->pins = pins;
dev_dbg(pmx->dev, "got pins %u", npins);
return 0;
}
static int scmi_pinctrl_probe(struct scmi_device *sdev)
{
int ret;
struct device *dev = &sdev->dev;
struct scmi_pinctrl *pmx;
const struct scmi_handle *handle;
struct scmi_protocol_handle *ph;
if (!sdev->handle)
return -EINVAL;
handle = sdev->handle;
pinctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PINCTRL, &ph);
if (IS_ERR(pinctrl_ops))
return PTR_ERR(pinctrl_ops);
pmx = devm_kzalloc(dev, sizeof(*pmx), GFP_KERNEL);
if (!pmx)
return -ENOMEM;
pmx->ph = ph;
pmx->dev = dev;
pmx->pctl_desc.name = DRV_NAME;
pmx->pctl_desc.owner = THIS_MODULE;
pmx->pctl_desc.pctlops = &pinctrl_scmi_pinctrl_ops;
pmx->pctl_desc.pmxops = &pinctrl_scmi_pinmux_ops;
pmx->pctl_desc.confops = &pinctrl_scmi_pinconf_ops;
ret = pinctrl_scmi_get_pins(pmx, &pmx->pctl_desc);
if (ret)
return ret;
ret = devm_pinctrl_register_and_init(dev, &pmx->pctl_desc, pmx,
&pmx->pctldev);
if (ret)
return dev_err_probe(dev, ret, "Failed to register pinctrl\n");
pmx->nr_functions = pinctrl_scmi_get_functions_count(pmx->pctldev);
pmx->functions = devm_kcalloc(dev, pmx->nr_functions,
sizeof(*pmx->functions), GFP_KERNEL);
if (!pmx->functions)
return -ENOMEM;
return pinctrl_enable(pmx->pctldev);
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_PINCTRL, "pinctrl" },
{ }
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_pinctrl_driver = {
.name = DRV_NAME,
.probe = scmi_pinctrl_probe,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_pinctrl_driver);
MODULE_AUTHOR("Oleksii Moisieiev <oleksii_moisieiev@epam.com>");
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
MODULE_DESCRIPTION("ARM SCMI pin controller driver");
MODULE_LICENSE("GPL");

View File

@ -103,9 +103,9 @@ config RESET_INTEL_GW
config RESET_K210
bool "Reset controller driver for Canaan Kendryte K210 SoC"
depends on (SOC_CANAAN || COMPILE_TEST) && OF
depends on (SOC_CANAAN_K210 || COMPILE_TEST) && OF
select MFD_SYSCON
default SOC_CANAAN
default SOC_CANAAN_K210
help
Support for the Canaan Kendryte K210 RISC-V SoC reset controller.
Say Y if you want to control reset signals provided by this

View File

@ -7,7 +7,7 @@ obj-y += apple/
obj-y += aspeed/
obj-$(CONFIG_ARCH_AT91) += atmel/
obj-y += bcm/
obj-$(CONFIG_SOC_CANAAN) += canaan/
obj-$(CONFIG_ARCH_CANAAN) += canaan/
obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/

View File

@ -2,9 +2,9 @@
config SOC_K210_SYSCTL
bool "Canaan Kendryte K210 SoC system controller"
depends on RISCV && SOC_CANAAN && OF
depends on RISCV && SOC_CANAAN_K210 && OF
depends on COMMON_CLK_K210
default SOC_CANAAN
default SOC_CANAAN_K210
select PM
select MFD_SYSCON
help

View File

@ -6,7 +6,7 @@ menu "Hisilicon SoC drivers"
config KUNPENG_HCCS
tristate "HCCS driver on Kunpeng SoC"
depends on ACPI
depends on MAILBOX
depends on PCC
depends on ARM64 || COMPILE_TEST
help
The Huawei Cache Coherence System (HCCS) is a multi-chip

View File

@ -556,6 +556,12 @@ static int hccs_get_all_port_attr(struct hccs_dev *hdev,
start_id = rsp_head.next_id;
}
if (left_buf_len != 0) {
dev_err(hdev->dev, "failed to get the expected port number(%u) attribute.\n",
size);
return -EINVAL;
}
return 0;
}

View File

@ -12,9 +12,12 @@
#define CMDQ_WRITE_ENABLE_MASK BIT(0)
#define CMDQ_POLL_ENABLE_MASK BIT(0)
/* dedicate the last GPR_R15 to assign the register address to be poll */
#define CMDQ_POLL_ADDR_GPR (15)
#define CMDQ_EOC_IRQ_EN BIT(0)
#define CMDQ_REG_TYPE 1
#define CMDQ_JUMP_RELATIVE 1
#define CMDQ_JUMP_RELATIVE 0
#define CMDQ_JUMP_ABSOLUTE 1
struct cmdq_instruction {
union {
@ -55,7 +58,7 @@ int cmdq_dev_get_client_reg(struct device *dev,
"mediatek,gce-client-reg",
3, idx, &spec);
if (err < 0) {
dev_err(dev,
dev_warn(dev,
"error %d can't parse gce-client-reg property (%d)",
err, idx);
@ -105,22 +108,16 @@ void cmdq_mbox_destroy(struct cmdq_client *client)
}
EXPORT_SYMBOL(cmdq_mbox_destroy);
struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size)
int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t size)
{
struct cmdq_pkt *pkt;
struct device *dev;
dma_addr_t dma_addr;
pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
if (!pkt)
return ERR_PTR(-ENOMEM);
pkt->va_base = kzalloc(size, GFP_KERNEL);
if (!pkt->va_base) {
kfree(pkt);
return ERR_PTR(-ENOMEM);
}
if (!pkt->va_base)
return -ENOMEM;
pkt->buf_size = size;
pkt->cl = (void *)client;
dev = client->chan->mbox->dev;
dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size,
@ -128,24 +125,20 @@ struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size)
if (dma_mapping_error(dev, dma_addr)) {
dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size);
kfree(pkt->va_base);
kfree(pkt);
return ERR_PTR(-ENOMEM);
return -ENOMEM;
}
pkt->pa_base = dma_addr;
return pkt;
return 0;
}
EXPORT_SYMBOL(cmdq_pkt_create);
void cmdq_pkt_destroy(struct cmdq_pkt *pkt)
void cmdq_pkt_destroy(struct cmdq_client *client, struct cmdq_pkt *pkt)
{
struct cmdq_client *client = (struct cmdq_client *)pkt->cl;
dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size,
DMA_TO_DEVICE);
kfree(pkt->va_base);
kfree(pkt);
}
EXPORT_SYMBOL(cmdq_pkt_destroy);
@ -299,6 +292,32 @@ int cmdq_pkt_write_s_mask_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx,
}
EXPORT_SYMBOL(cmdq_pkt_write_s_mask_value);
int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_addr)
{
const u16 high_addr_reg_idx = CMDQ_THR_SPR_IDX0;
const u16 value_reg_idx = CMDQ_THR_SPR_IDX1;
int ret;
/* read the value of src_addr into high_addr_reg_idx */
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr));
if (ret < 0)
return ret;
ret = cmdq_pkt_read_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(src_addr), value_reg_idx);
if (ret < 0)
return ret;
/* write the value of value_reg_idx into dst_addr */
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr));
if (ret < 0)
return ret;
ret = cmdq_pkt_write_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(dst_addr), value_reg_idx);
if (ret < 0)
return ret;
return 0;
}
EXPORT_SYMBOL(cmdq_pkt_mem_move);
int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear)
{
struct cmdq_instruction inst = { {0} };
@ -315,6 +334,21 @@ int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear)
}
EXPORT_SYMBOL(cmdq_pkt_wfe);
int cmdq_pkt_acquire_event(struct cmdq_pkt *pkt, u16 event)
{
struct cmdq_instruction inst = {};
if (event >= CMDQ_MAX_EVENT)
return -EINVAL;
inst.op = CMDQ_CODE_WFE;
inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE | CMDQ_WFE_WAIT;
inst.event = event;
return cmdq_pkt_append_command(pkt, inst);
}
EXPORT_SYMBOL(cmdq_pkt_acquire_event);
int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event)
{
struct cmdq_instruction inst = { {0} };
@ -380,6 +414,53 @@ int cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys,
}
EXPORT_SYMBOL(cmdq_pkt_poll_mask);
int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mask)
{
struct cmdq_instruction inst = { {0} };
u8 use_mask = 0;
int ret;
/*
* Append an MASK instruction to set the mask for following POLL instruction
* which enables use_mask bit.
*/
if (mask != GENMASK(31, 0)) {
inst.op = CMDQ_CODE_MASK;
inst.mask = ~mask;
ret = cmdq_pkt_append_command(pkt, inst);
if (ret < 0)
return ret;
use_mask = CMDQ_POLL_ENABLE_MASK;
}
/*
* POLL is an legacy operation in GCE and it does not support SPR and CMDQ_CODE_LOGIC,
* so it can not use cmdq_pkt_assign to keep polling register address to SPR.
* If user wants to poll a register address which doesn't have a subsys id,
* user needs to use GPR and CMDQ_CODE_MASK to move polling register address to GPR.
*/
inst.op = CMDQ_CODE_MASK;
inst.dst_t = CMDQ_REG_TYPE;
inst.sop = CMDQ_POLL_ADDR_GPR;
inst.value = addr;
ret = cmdq_pkt_append_command(pkt, inst);
if (ret < 0)
return ret;
/* Append POLL instruction to poll the register address assign to GPR previously. */
inst.op = CMDQ_CODE_POLL;
inst.dst_t = CMDQ_REG_TYPE;
inst.sop = CMDQ_POLL_ADDR_GPR;
inst.offset = use_mask;
inst.value = value;
ret = cmdq_pkt_append_command(pkt, inst);
if (ret < 0)
return ret;
return 0;
}
EXPORT_SYMBOL(cmdq_pkt_poll_addr);
int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value)
{
struct cmdq_instruction inst = {};
@ -392,17 +473,36 @@ int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value)
}
EXPORT_SYMBOL(cmdq_pkt_assign);
int cmdq_pkt_jump(struct cmdq_pkt *pkt, dma_addr_t addr)
int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa)
{
struct cmdq_instruction inst = {};
inst.op = CMDQ_CODE_JUMP;
inst.offset = CMDQ_JUMP_RELATIVE;
inst.value = addr >>
cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan);
inst.offset = CMDQ_JUMP_ABSOLUTE;
inst.value = addr >> shift_pa;
return cmdq_pkt_append_command(pkt, inst);
}
EXPORT_SYMBOL(cmdq_pkt_jump);
EXPORT_SYMBOL(cmdq_pkt_jump_abs);
int cmdq_pkt_jump_rel(struct cmdq_pkt *pkt, s32 offset, u8 shift_pa)
{
struct cmdq_instruction inst = { {0} };
inst.op = CMDQ_CODE_JUMP;
inst.value = (u32)offset >> shift_pa;
return cmdq_pkt_append_command(pkt, inst);
}
EXPORT_SYMBOL(cmdq_pkt_jump_rel);
int cmdq_pkt_eoc(struct cmdq_pkt *pkt)
{
struct cmdq_instruction inst = { {0} };
inst.op = CMDQ_CODE_EOC;
inst.value = CMDQ_EOC_IRQ_EN;
return cmdq_pkt_append_command(pkt, inst);
}
EXPORT_SYMBOL(cmdq_pkt_eoc);
int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
{
@ -426,19 +526,4 @@ int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
}
EXPORT_SYMBOL(cmdq_pkt_finalize);
int cmdq_pkt_flush_async(struct cmdq_pkt *pkt)
{
int err;
struct cmdq_client *client = (struct cmdq_client *)pkt->cl;
err = mbox_send_message(client->chan, pkt);
if (err < 0)
return err;
/* We can send next packet immediately, so just call txdone. */
mbox_client_txdone(client->chan, 0);
return 0;
}
EXPORT_SYMBOL(cmdq_pkt_flush_async);
MODULE_LICENSE("GPL v2");

View File

@ -496,6 +496,39 @@ static const unsigned int mt8188_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_MERGE5] = MT8188_MUTEX_MOD_DISP1_VPP_MERGE4,
};
static const unsigned int mt8188_mdp_mutex_table_mod[MUTEX_MOD_IDX_MAX] = {
[MUTEX_MOD_IDX_MDP_RDMA0] = MT8195_MUTEX_MOD_MDP_RDMA0,
[MUTEX_MOD_IDX_MDP_RDMA2] = MT8195_MUTEX_MOD_MDP_RDMA2,
[MUTEX_MOD_IDX_MDP_RDMA3] = MT8195_MUTEX_MOD_MDP_RDMA3,
[MUTEX_MOD_IDX_MDP_FG0] = MT8195_MUTEX_MOD_MDP_FG0,
[MUTEX_MOD_IDX_MDP_FG2] = MT8195_MUTEX_MOD_MDP_FG2,
[MUTEX_MOD_IDX_MDP_FG3] = MT8195_MUTEX_MOD_MDP_FG3,
[MUTEX_MOD_IDX_MDP_HDR0] = MT8195_MUTEX_MOD_MDP_HDR0,
[MUTEX_MOD_IDX_MDP_HDR2] = MT8195_MUTEX_MOD_MDP_HDR2,
[MUTEX_MOD_IDX_MDP_HDR3] = MT8195_MUTEX_MOD_MDP_HDR3,
[MUTEX_MOD_IDX_MDP_AAL0] = MT8195_MUTEX_MOD_MDP_AAL0,
[MUTEX_MOD_IDX_MDP_AAL2] = MT8195_MUTEX_MOD_MDP_AAL2,
[MUTEX_MOD_IDX_MDP_AAL3] = MT8195_MUTEX_MOD_MDP_AAL3,
[MUTEX_MOD_IDX_MDP_RSZ0] = MT8195_MUTEX_MOD_MDP_RSZ0,
[MUTEX_MOD_IDX_MDP_RSZ2] = MT8195_MUTEX_MOD_MDP_RSZ2,
[MUTEX_MOD_IDX_MDP_RSZ3] = MT8195_MUTEX_MOD_MDP_RSZ3,
[MUTEX_MOD_IDX_MDP_MERGE2] = MT8195_MUTEX_MOD_MDP_MERGE2,
[MUTEX_MOD_IDX_MDP_MERGE3] = MT8195_MUTEX_MOD_MDP_MERGE3,
[MUTEX_MOD_IDX_MDP_TDSHP0] = MT8195_MUTEX_MOD_MDP_TDSHP0,
[MUTEX_MOD_IDX_MDP_TDSHP2] = MT8195_MUTEX_MOD_MDP_TDSHP2,
[MUTEX_MOD_IDX_MDP_TDSHP3] = MT8195_MUTEX_MOD_MDP_TDSHP3,
[MUTEX_MOD_IDX_MDP_COLOR0] = MT8195_MUTEX_MOD_MDP_COLOR0,
[MUTEX_MOD_IDX_MDP_COLOR2] = MT8195_MUTEX_MOD_MDP_COLOR2,
[MUTEX_MOD_IDX_MDP_COLOR3] = MT8195_MUTEX_MOD_MDP_COLOR3,
[MUTEX_MOD_IDX_MDP_OVL0] = MT8195_MUTEX_MOD_MDP_OVL0,
[MUTEX_MOD_IDX_MDP_PAD0] = MT8195_MUTEX_MOD_MDP_PAD0,
[MUTEX_MOD_IDX_MDP_PAD2] = MT8195_MUTEX_MOD_MDP_PAD2,
[MUTEX_MOD_IDX_MDP_PAD3] = MT8195_MUTEX_MOD_MDP_PAD3,
[MUTEX_MOD_IDX_MDP_WROT0] = MT8195_MUTEX_MOD_MDP_WROT0,
[MUTEX_MOD_IDX_MDP_WROT2] = MT8195_MUTEX_MOD_MDP_WROT2,
[MUTEX_MOD_IDX_MDP_WROT3] = MT8195_MUTEX_MOD_MDP_WROT3,
};
static const unsigned int mt8192_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_AAL0] = MT8192_MUTEX_MOD_DISP_AAL0,
[DDP_COMPONENT_CCORR] = MT8192_MUTEX_MOD_DISP_CCORR0,
@ -735,6 +768,13 @@ static const struct mtk_mutex_data mt8188_mutex_driver_data = {
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
};
static const struct mtk_mutex_data mt8188_vpp_mutex_driver_data = {
.mutex_sof = mt8188_mutex_sof,
.mutex_mod_reg = MT8183_MUTEX0_MOD0,
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
.mutex_table_mod = mt8188_mdp_mutex_table_mod,
};
static const struct mtk_mutex_data mt8192_mutex_driver_data = {
.mutex_mod = mt8192_mutex_mod,
.mutex_sof = mt8183_mutex_sof,
@ -1089,6 +1129,7 @@ static const struct of_device_id mutex_driver_dt_match[] = {
{ .compatible = "mediatek,mt8186-disp-mutex", .data = &mt8186_mutex_driver_data },
{ .compatible = "mediatek,mt8186-mdp3-mutex", .data = &mt8186_mdp_mutex_driver_data },
{ .compatible = "mediatek,mt8188-disp-mutex", .data = &mt8188_mutex_driver_data },
{ .compatible = "mediatek,mt8188-vpp-mutex", .data = &mt8188_vpp_mutex_driver_data },
{ .compatible = "mediatek,mt8192-disp-mutex", .data = &mt8192_mutex_driver_data },
{ .compatible = "mediatek,mt8195-disp-mutex", .data = &mt8195_mutex_driver_data },
{ .compatible = "mediatek,mt8195-vpp-mutex", .data = &mt8195_vpp_mutex_driver_data },

View File

@ -48,14 +48,15 @@ static struct socinfo_data socinfo_data_table[] = {
MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000940),
MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 830", 0x81880000, 0x00000010),
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 830", 0x81880000, 0x00000011),
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 838", 0x81880000, 0x00000010),
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 838", 0x81880000, 0x00000011),
MTK_SOCINFO_ENTRY("MT8192", "MT8192V/AZA", "Kompanio 820", 0x00001100, 0x00040080),
MTK_SOCINFO_ENTRY("MT8192T", "MT8192V/ATZA", "Kompanio 828", 0x00000100, 0x000400C0),
MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EZA", "Kompanio 1200", 0x81950300, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EHZA", "Kompanio 1200", 0x81950304, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8395", "MT8395AV/ZA", "Genio 1200", 0x83950100, CELL_NOT_USED),
};
static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop)
@ -144,7 +145,14 @@ static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop)
}
}
return match_socinfo_index >= 0 ? match_socinfo_index : -ENOENT;
if (match_socinfo_index < 0) {
dev_warn(mtk_socinfop->dev,
"Unknown MediaTek SoC with ID 0x%08x 0x%08x\n",
cell_data[0], cell_data[1]);
return -ENOENT;
}
return match_socinfo_index;
}
static int mtk_socinfo_probe(struct platform_device *pdev)

View File

@ -1,6 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. */
/*
* Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -17,6 +21,8 @@
#define MAX_SLV_ID 8
#define SLAVE_ID_MASK 0x7
#define SLAVE_ID_SHIFT 16
#define SLAVE_ID(addr) FIELD_GET(GENMASK(19, 16), addr)
#define VRM_ADDR(addr) FIELD_GET(GENMASK(19, 4), addr)
/**
* struct entry_header: header for each entry in cmddb
@ -147,12 +153,7 @@ static int cmd_db_get_header(const char *id, const struct entry_header **eh,
if (ret)
return ret;
/*
* Pad out query string to same length as in DB. NOTE: the output
* query string is not necessarily '\0' terminated if it bumps up
* against the max size. That's OK and expected.
*/
strncpy(query, id, sizeof(query));
strtomem_pad(query, id, 0);
for (i = 0; i < MAX_SLV_ID; i++) {
rsc_hdr = &cmd_db_header->header[i];
@ -220,6 +221,30 @@ const void *cmd_db_read_aux_data(const char *id, size_t *len)
}
EXPORT_SYMBOL_GPL(cmd_db_read_aux_data);
/**
* cmd_db_match_resource_addr() - Compare if both Resource addresses are same
*
* @addr1: Resource address to compare
* @addr2: Resource address to compare
*
* Return: true if two addresses refer to the same resource, false otherwise
*/
bool cmd_db_match_resource_addr(u32 addr1, u32 addr2)
{
/*
* Each RPMh VRM accelerator resource has 3 or 4 contiguous 4-byte
* aligned addresses associated with it. Ignore the offset to check
* for VRM requests.
*/
if (addr1 == addr2)
return true;
else if (SLAVE_ID(addr1) == CMD_DB_HW_VRM && VRM_ADDR(addr1) == VRM_ADDR(addr2))
return true;
return false;
}
EXPORT_SYMBOL_GPL(cmd_db_match_resource_addr);
/**
* cmd_db_read_slave_id - Get the slave ID for a given resource address
*
@ -362,7 +387,7 @@ static int __init cmd_db_device_init(void)
{
return platform_driver_register(&cmd_db_dev_driver);
}
arch_initcall(cmd_db_device_init);
core_initcall(cmd_db_device_init);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Command DB Driver");
MODULE_LICENSE("GPL v2");

View File

@ -282,7 +282,7 @@ static const struct regmap_config msm8998_bwmon_regmap_cfg = {
* Cache is necessary for using regmap fields with non-readable
* registers.
*/
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config msm8998_bwmon_global_regmap_cfg = {
@ -301,7 +301,7 @@ static const struct regmap_config msm8998_bwmon_global_regmap_cfg = {
* Cache is necessary for using regmap fields with non-readable
* registers.
*/
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
static const struct reg_field sdm845_cpu_bwmon_reg_fields[] = {
@ -369,7 +369,7 @@ static const struct regmap_config sdm845_cpu_bwmon_regmap_cfg = {
* Cache is necessary for using regmap fields with non-readable
* registers.
*/
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
/* BWMON v5 */
@ -446,7 +446,7 @@ static const struct regmap_config sdm845_llcc_bwmon_regmap_cfg = {
* Cache is necessary for using regmap fields with non-readable
* registers.
*/
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
static void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all)

View File

@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/soc/qcom/pdr.h>
#include <linux/soc/qcom/pmic_glink.h>
#include <linux/spinlock.h>
enum {
PMIC_GLINK_CLIENT_BATT = 0,
@ -36,7 +37,7 @@ struct pmic_glink {
unsigned int pdr_state;
/* serializing clients list updates */
struct mutex client_lock;
spinlock_t client_lock;
struct list_head clients;
};
@ -58,10 +59,11 @@ static void _devm_pmic_glink_release_client(struct device *dev, void *res)
{
struct pmic_glink_client *client = (struct pmic_glink_client *)res;
struct pmic_glink *pg = client->pg;
unsigned long flags;
mutex_lock(&pg->client_lock);
spin_lock_irqsave(&pg->client_lock, flags);
list_del(&client->node);
mutex_unlock(&pg->client_lock);
spin_unlock_irqrestore(&pg->client_lock, flags);
}
struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
@ -72,6 +74,7 @@ struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
{
struct pmic_glink_client *client;
struct pmic_glink *pg = dev_get_drvdata(dev->parent);
unsigned long flags;
client = devres_alloc(_devm_pmic_glink_release_client, sizeof(*client), GFP_KERNEL);
if (!client)
@ -83,9 +86,14 @@ struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
client->pdr_notify = pdr;
client->priv = priv;
mutex_lock(&pg->client_lock);
mutex_lock(&pg->state_lock);
spin_lock_irqsave(&pg->client_lock, flags);
list_add(&client->node, &pg->clients);
mutex_unlock(&pg->client_lock);
client->pdr_notify(client->priv, pg->client_state);
spin_unlock_irqrestore(&pg->client_lock, flags);
mutex_unlock(&pg->state_lock);
devres_add(dev, client);
@ -107,6 +115,7 @@ static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
struct pmic_glink_client *client;
struct pmic_glink_hdr *hdr;
struct pmic_glink *pg = dev_get_drvdata(&rpdev->dev);
unsigned long flags;
if (len < sizeof(*hdr)) {
dev_warn(pg->dev, "ignoring truncated message\n");
@ -115,10 +124,12 @@ static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
hdr = data;
spin_lock_irqsave(&pg->client_lock, flags);
list_for_each_entry(client, &pg->clients, node) {
if (client->id == le32_to_cpu(hdr->owner))
client->cb(data, len, client->priv);
}
spin_unlock_irqrestore(&pg->client_lock, flags);
return 0;
}
@ -158,6 +169,7 @@ static void pmic_glink_state_notify_clients(struct pmic_glink *pg)
{
struct pmic_glink_client *client;
unsigned int new_state = pg->client_state;
unsigned long flags;
if (pg->client_state != SERVREG_SERVICE_STATE_UP) {
if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept)
@ -168,8 +180,10 @@ static void pmic_glink_state_notify_clients(struct pmic_glink *pg)
}
if (new_state != pg->client_state) {
spin_lock_irqsave(&pg->client_lock, flags);
list_for_each_entry(client, &pg->clients, node)
client->pdr_notify(client->priv, new_state);
spin_unlock_irqrestore(&pg->client_lock, flags);
pg->client_state = new_state;
}
}
@ -256,7 +270,7 @@ static int pmic_glink_probe(struct platform_device *pdev)
pg->dev = &pdev->dev;
INIT_LIST_HEAD(&pg->clients);
mutex_init(&pg->client_lock);
spin_lock_init(&pg->client_lock);
mutex_init(&pg->state_lock);
match_data = (unsigned long *)of_device_get_match_data(&pdev->dev);

View File

@ -150,6 +150,10 @@ static const struct rpmsg_device_id pmic_pdcharger_ulog_rpmsg_id_match[] = {
{ "PMIC_LOGS_ADSP_APPS" },
{}
};
/*
* No MODULE_DEVICE_TABLE intentionally: that's a debugging module, to be
* loaded manually only.
*/
static struct rpmsg_driver pmic_pdcharger_ulog_rpmsg_driver = {
.probe = pmic_pdcharger_ulog_rpmsg_probe,

View File

@ -35,11 +35,15 @@ static const struct subsystem_data subsystems[] = {
{ "wpss", 605, 13 },
{ "adsp", 606, 2 },
{ "cdsp", 607, 5 },
{ "cdsp1", 607, 12 },
{ "gpdsp0", 607, 17 },
{ "gpdsp1", 607, 18 },
{ "slpi", 608, 3 },
{ "gpu", 609, 0 },
{ "display", 610, 0 },
{ "adsp_island", 613, 2 },
{ "slpi_island", 613, 3 },
{ "apss", 631, QCOM_SMEM_HOST_ANY },
};
struct stats_config {

View File

@ -148,6 +148,10 @@ static const struct of_device_id rpm_master_table[] = {
{ .compatible = "qcom,rpm-master-stats" },
{ },
};
/*
* No MODULE_DEVICE_TABLE intentionally: that's a debugging module, to be
* loaded manually only.
*/
static struct platform_driver master_stats_driver = {
.probe = master_stats_probe,

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME
@ -557,7 +558,7 @@ static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs,
for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) {
addr = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], i, j);
for (k = 0; k < msg->num_cmds; k++) {
if (addr == msg->cmds[k].addr)
if (cmd_db_match_resource_addr(msg->cmds[k].addr, addr))
return -EBUSY;
}
}
@ -1154,7 +1155,7 @@ static int __init rpmh_driver_init(void)
{
return platform_driver_register(&rpmh_driver);
}
arch_initcall(rpmh_driver_init);
core_initcall(rpmh_driver_init);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver");
MODULE_LICENSE("GPL v2");

View File

@ -133,6 +133,7 @@ static const char *const pmic_models[] = {
[72] = "PMR735D",
[73] = "PM8550",
[74] = "PMK8550",
[82] = "SMB2360",
};
struct socinfo_params {
@ -430,6 +431,7 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(QRU1000) },
{ qcom_board_id(SM8475_2) },
{ qcom_board_id(QDU1000) },
{ qcom_board_id(X1E80100) },
{ qcom_board_id(SM8650) },
{ qcom_board_id(SM4450) },
{ qcom_board_id(QDU1010) },

View File

@ -24,6 +24,7 @@ config ARCH_RCAR_GEN2
select RENESAS_IRQC
select RST_RCAR
select SYS_SUPPORTS_SH_CMT
select SYS_SUPPORTS_SH_TMU
config ARCH_RCAR_GEN3
bool
@ -344,6 +345,11 @@ config ARCH_R9A09G011
help
This enables support for the Renesas RZ/V2M SoC.
config ARCH_R9A09G057
bool "ARM64 Platform support for RZ/V2H(P)"
help
This enables support for the Renesas RZ/V2H(P) SoC variants.
endif # ARM64
if RISCV

View File

@ -75,6 +75,10 @@ static const struct renesas_family fam_rzg3s __initconst __maybe_unused = {
.name = "RZ/G3S",
};
static const struct renesas_family fam_rzv2h __initconst __maybe_unused = {
.name = "RZ/V2H",
};
static const struct renesas_family fam_rzv2l __initconst __maybe_unused = {
.name = "RZ/V2L",
};
@ -177,6 +181,11 @@ static const struct renesas_soc soc_rz_g3s __initconst __maybe_unused = {
.id = 0x85e0447,
};
static const struct renesas_soc soc_rz_v2h __initconst __maybe_unused = {
.family = &fam_rzv2h,
.id = 0x847a447,
};
static const struct renesas_soc soc_rz_v2l __initconst __maybe_unused = {
.family = &fam_rzv2l,
.id = 0x8447447,
@ -407,6 +416,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = {
#ifdef CONFIG_ARCH_R9A09G011
{ .compatible = "renesas,r9a09g011", .data = &soc_rz_v2m },
#endif
#ifdef CONFIG_ARCH_R9A09G057
{ .compatible = "renesas,r9a09g057", .data = &soc_rz_v2h },
#endif
#ifdef CONFIG_ARCH_SH73A0
{ .compatible = "renesas,sh73a0", .data = &soc_shmobile_ag5 },
#endif
@ -432,6 +444,11 @@ static const struct renesas_id id_rzg2l __initconst = {
.mask = 0xfffffff,
};
static const struct renesas_id id_rzv2h __initconst = {
.offset = 0x304,
.mask = 0xfffffff,
};
static const struct renesas_id id_rzv2m __initconst = {
.offset = 0x104,
.mask = 0xff,
@ -449,6 +466,7 @@ static const struct of_device_id renesas_ids[] __initconst = {
{ .compatible = "renesas,r9a07g054-sysc", .data = &id_rzg2l },
{ .compatible = "renesas,r9a08g045-sysc", .data = &id_rzg2l },
{ .compatible = "renesas,r9a09g011-sys", .data = &id_rzv2m },
{ .compatible = "renesas,r9a09g057-sys", .data = &id_rzv2h },
{ .compatible = "renesas,prr", .data = &id_prr },
{ /* sentinel */ }
};
@ -513,7 +531,7 @@ static int __init renesas_soc_init(void)
eslo = product & 0xf;
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u",
eshi, eslo);
} else if (id == &id_rzg2l) {
} else if (id == &id_rzg2l || id == &id_rzv2h) {
eshi = ((product >> 28) & 0x0f);
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u",
eshi);

View File

@ -4074,6 +4074,7 @@ static const char * const tegra194_reset_sources[] = {
};
static const struct tegra_wake_event tegra194_wake_events[] = {
TEGRA_WAKE_GPIO("eqos", 20, 0, TEGRA194_MAIN_GPIO(G, 4)),
TEGRA_WAKE_IRQ("pmu", 24, 209),
TEGRA_WAKE_GPIO("power", 29, 1, TEGRA194_AON_GPIO(EE, 4)),
TEGRA_WAKE_IRQ("rtc", 73, 10),
@ -4210,6 +4211,7 @@ static const char * const tegra234_reset_sources[] = {
static const struct tegra_wake_event tegra234_wake_events[] = {
TEGRA_WAKE_GPIO("sd-wake", 8, 0, TEGRA234_MAIN_GPIO(G, 7)),
TEGRA_WAKE_GPIO("eqos", 20, 0, TEGRA234_MAIN_GPIO(G, 4)),
TEGRA_WAKE_IRQ("pmu", 24, 209),
TEGRA_WAKE_GPIO("power", 29, 1, TEGRA234_AON_GPIO(EE, 4)),
TEGRA_WAKE_GPIO("mgbe", 56, 0, TEGRA234_MAIN_GPIO(Y, 3)),

View File

@ -16,7 +16,6 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/omap-mailbox.h>
#include <linux/platform_device.h>
#include <linux/remoteproc.h>
#include <linux/suspend.h>
@ -314,7 +313,6 @@ static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data)
static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc)
{
struct device *dev = m3_ipc->dev;
mbox_msg_t dummy_msg = 0;
int ret;
if (!m3_ipc->mbox) {
@ -330,7 +328,7 @@ static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc)
* the RX callback to avoid multiple interrupts being received
* by the CM3.
*/
ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
ret = mbox_send_message(m3_ipc->mbox, NULL);
if (ret < 0) {
dev_err(dev, "%s: mbox_send_message() failed: %d\n",
__func__, ret);
@ -352,7 +350,6 @@ static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc)
static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc)
{
struct device *dev = m3_ipc->dev;
mbox_msg_t dummy_msg = 0;
int ret;
if (!m3_ipc->mbox) {
@ -361,7 +358,7 @@ static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc)
return -EIO;
}
ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
ret = mbox_send_message(m3_ipc->mbox, NULL);
if (ret < 0) {
dev_err(dev, "%s: mbox_send_message() failed: %d\n",
__func__, ret);

View File

@ -15,5 +15,6 @@ if TEE
source "drivers/tee/optee/Kconfig"
source "drivers/tee/amdtee/Kconfig"
source "drivers/tee/tstee/Kconfig"
endif

View File

@ -5,3 +5,4 @@ tee-objs += tee_shm.o
tee-objs += tee_shm_pool.o
obj-$(CONFIG_OPTEE) += optee/
obj-$(CONFIG_AMDTEE) += amdtee/
obj-$(CONFIG_ARM_TSTEE) += tstee/

View File

@ -9,7 +9,7 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/kref.h>
#include <linux/types.h>
#include "amdtee_if.h"

View File

@ -5,7 +5,7 @@
#include <linux/device.h>
#include <linux/tee.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/psp-tee.h>
#include <linux/slab.h>
#include <linux/psp.h>

View File

@ -9,13 +9,12 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/firmware.h>
#include "amdtee_private.h"
#include "../tee_private.h"
#include <linux/psp-tee.h>
static struct amdtee_driver_data *drv_data;

View File

@ -4,7 +4,7 @@
*/
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/psp.h>
#include "amdtee_private.h"

View File

@ -7,7 +7,7 @@
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/types.h>
#include "optee_private.h"

View File

@ -9,77 +9,13 @@
#include <linux/crash_dump.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/types.h>
#include "optee_private.h"
int optee_pool_op_alloc_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
size_t size, size_t align,
int (*shm_register)(struct tee_context *ctx,
struct tee_shm *shm,
struct page **pages,
size_t num_pages,
unsigned long start))
{
size_t nr_pages = roundup(size, PAGE_SIZE) / PAGE_SIZE;
struct page **pages;
unsigned int i;
int rc = 0;
/*
* Ignore alignment since this is already going to be page aligned
* and there's no need for any larger alignment.
*/
shm->kaddr = alloc_pages_exact(nr_pages * PAGE_SIZE,
GFP_KERNEL | __GFP_ZERO);
if (!shm->kaddr)
return -ENOMEM;
shm->paddr = virt_to_phys(shm->kaddr);
shm->size = nr_pages * PAGE_SIZE;
pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL);
if (!pages) {
rc = -ENOMEM;
goto err;
}
for (i = 0; i < nr_pages; i++)
pages[i] = virt_to_page((u8 *)shm->kaddr + i * PAGE_SIZE);
shm->pages = pages;
shm->num_pages = nr_pages;
if (shm_register) {
rc = shm_register(shm->ctx, shm, pages, nr_pages,
(unsigned long)shm->kaddr);
if (rc)
goto err;
}
return 0;
err:
free_pages_exact(shm->kaddr, shm->size);
shm->kaddr = NULL;
return rc;
}
void optee_pool_op_free_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
int (*shm_unregister)(struct tee_context *ctx,
struct tee_shm *shm))
{
if (shm_unregister)
shm_unregister(shm->ctx, shm);
free_pages_exact(shm->kaddr, shm->size);
shm->kaddr = NULL;
kfree(shm->pages);
shm->pages = NULL;
}
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));

View File

@ -7,7 +7,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/uuid.h>
#include "optee_private.h"

View File

@ -11,7 +11,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include <linux/types.h>
#include "optee_private.h"
#include "optee_ffa.h"
@ -374,14 +374,14 @@ static int optee_ffa_shm_unregister_supp(struct tee_context *ctx,
static int pool_ffa_op_alloc(struct tee_shm_pool *pool,
struct tee_shm *shm, size_t size, size_t align)
{
return optee_pool_op_alloc_helper(pool, shm, size, align,
optee_ffa_shm_register);
return tee_dyn_shm_alloc_helper(shm, size, align,
optee_ffa_shm_register);
}
static void pool_ffa_op_free(struct tee_shm_pool *pool,
struct tee_shm *shm)
{
optee_pool_op_free_helper(pool, shm, optee_ffa_shm_unregister);
tee_dyn_shm_free_helper(shm, optee_ffa_shm_unregister);
}
static void pool_ffa_op_destroy_pool(struct tee_shm_pool *pool)

View File

@ -9,7 +9,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/tee_drv.h>
#include <linux/tee_core.h>
#include "optee_private.h"
struct notif_entry {

Some files were not shown because too many files have changed in this diff Show More