mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
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:
commit
14a60290ed
@ -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";
|
||||
};
|
||||
};
|
96
Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml
Normal file
96
Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml
Normal 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>;
|
||||
};
|
||||
};
|
105
Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml
Normal file
105
Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml
Normal 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>;
|
||||
};
|
||||
};
|
@ -46,6 +46,10 @@ properties:
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -51,6 +51,10 @@ properties:
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -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
|
||||
|
@ -28,6 +28,10 @@ properties:
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -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 {
|
||||
|
@ -127,6 +127,10 @@ properties:
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -93,6 +93,10 @@ properties:
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
|
@ -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
|
||||
|
@ -45,6 +45,10 @@ properties:
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
|
@ -29,6 +29,10 @@ properties:
|
||||
- const: cec
|
||||
- const: hdmi-cec
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -36,6 +36,10 @@ properties:
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
|
@ -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>;
|
||||
};
|
@ -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
|
||||
|
@ -44,6 +44,10 @@ properties:
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
pwm:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
@ -67,6 +67,10 @@ properties:
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
pwm:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
@ -79,6 +79,10 @@ properties:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
power-domains: true
|
||||
|
||||
resets:
|
||||
|
@ -118,6 +118,10 @@ properties:
|
||||
phys:
|
||||
maxItems: 1
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -93,6 +93,10 @@ properties:
|
||||
select RCC clock instead of ETH_REF_CLK.
|
||||
type: boolean
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
|
@ -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:
|
||||
|
@ -30,6 +30,10 @@ properties:
|
||||
vdda-supply:
|
||||
description: phandle to the vdda input analog voltage.
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -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
|
||||
|
@ -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#
|
||||
|
@ -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 {
|
||||
|
@ -65,6 +65,10 @@ properties:
|
||||
$ref: audio-graph-port.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#sound-dai-cells"
|
||||
|
@ -48,6 +48,10 @@ properties:
|
||||
clock-names:
|
||||
maxItems: 3
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -50,6 +50,10 @@ properties:
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#sound-dai-cells"
|
||||
|
@ -46,6 +46,10 @@ properties:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -52,6 +52,10 @@ properties:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -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 ]
|
||||
|
@ -10,6 +10,7 @@ TEE Subsystem
|
||||
tee
|
||||
op-tee
|
||||
amd-tee
|
||||
ts-tee
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
71
Documentation/tee/ts-tee.rst
Normal file
71
Documentation/tee/ts-tee.rst
Normal 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
|
51
MAINTAINERS
51
MAINTAINERS
@ -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>
|
||||
|
@ -12,6 +12,7 @@ menuconfig ARCH_STM32
|
||||
select PINCTRL
|
||||
select RESET_CONTROLLER
|
||||
select STM32_EXTI
|
||||
select STM32_FIREWALL
|
||||
help
|
||||
Support for STMicroelectronics STM32 processors.
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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),)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
141
drivers/bus/stm32_etzpc.c
Normal 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");
|
294
drivers/bus/stm32_firewall.c
Normal file
294
drivers/bus/stm32_firewall.c
Normal 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);
|
83
drivers/bus/stm32_firewall.h
Normal file
83
drivers/bus/stm32_firewall.h
Normal 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
252
drivers/bus/stm32_rifsc.c
Normal 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");
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
916
drivers/firmware/arm_scmi/pinctrl.c
Normal file
916
drivers/firmware/arm_scmi/pinctrl.c
Normal 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)
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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, },
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
571
drivers/pinctrl/pinctrl-scmi.c
Normal file
571
drivers/pinctrl/pinctrl-scmi.c
Normal 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");
|
@ -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
|
||||
|
@ -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/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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 },
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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) },
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)),
|
||||
|
@ -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);
|
||||
|
@ -15,5 +15,6 @@ if TEE
|
||||
|
||||
source "drivers/tee/optee/Kconfig"
|
||||
source "drivers/tee/amdtee/Kconfig"
|
||||
source "drivers/tee/tstee/Kconfig"
|
||||
|
||||
endif
|
||||
|
@ -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/
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user