mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 16:53:49 +00:00
soc: driver updates for 6.12
The driver updates seem larger this time around, with changes is many of the SoC specific drivers, both the custom drivers/soc ones and the closely related subsystems (memory, bus, firmware, reset, ...). The at91 platform gains support for sam9x7 chips in the soc and power management code. This is the latest variant of one of the oldest still supported SoC families, using the ARM9 (ARMv5) core. As usual, the qualcomm snapdragon platform gets a ton of updates in many of their drivers to add more features and additional SoC support. Most of these are somewhat firmware related as the platform has a number of firmware based interfaces to the kernel. A notable addition here is the inclusion of trace events to two of these drivers. Herve Codina and Christophe Leroy are now sending updates for drivers/soc/fsl/ code through the SoC tree, this contains both PowerPC and Arm specific platforms and has previously been problematic to maintain. The first update here contains support for newer PowerPC variants and some cleanups. The turris mox firmware driver has a number of updates, mostly cleanups. The Arm SCMI firmware driver gets a major rework to modularize the existing code into separately loadable drivers for the various transports, the addition of custom NXP i.MX9 interfaces and a number of smaller updates. The Arm FF-A firmware driver gets a feature update to support the v1.2 version of the specification. The reset controller drivers have some smaller cleanups and a newly added driver for the Intel/Mobileye EyeQ5/EyeQ6 MIPS SoCs. The memory controller drivers get some cleanups and refactoring for Tegra, TI, Freescale/NXP and a couple more platforms. Finally there are lots of minor updates to firmware (raspberry pi, tegra, imx), bus (sunxi, omap, tegra) and soc (rockchips, tegra, amlogic, mediatek) drivers and their DT bindings. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmboJicACgkQYKtH/8kJ Uicc+RAAlc6lY0CKqZhAKzyGsjEHzPYcTN5axNf1WrTdgv3assF+yEeiQtkd+Re7 IIbtrWYWmLouOazRm5y7OjOUUy78FRThgRaVlZLPiCZze3rlZjIHj2XJx7R1hdjv aXBSe13i1KfmOsIDVNxAJbFa5UveeAvKKxRb6SJtSwl46KGhhgI3Jwq347sR7bYY QpiVMRVZTkH7++hlE9KE5qv+I6QVlbhneU1ErHjy0g4av+UycQe0eJFOtZV+Y5Aj 7k65D3GC33eOAK56iDCUH9OfYLFu7k3LF7V0zdLIhel1tFuoYUbjkC5iQr4VAMjc Ysw0vwXdnUoG3oDiGLOIK4u56flWqT7I8gSQwRhtp6Ikc47gdlA2zJLE4MNN/4Yh fMP9LUxzr0sJ67LUq4urtnRfVtgKRRZ44v6A+CHY0l3x6rqeNbkJd38mXeVMMIWo gW8cf/fJsEdNE+1neGXq7lRrUmCwvkYUAd7y3gt1BiUq/Abas6ityvzk9Xig87I2 uq3p5nASgrl+o8OlmIBT770BeS2jY8tAGJm1ocCtYN2ZWGh37LeES4Bo/58BPTGE tydBhlBK41h2dUCYQRZFNUt/2/xCELQ2X4mUiOaL2gqmiJq3Q6fdR1d+DAOB+4a5 KL1yt5yIrBl0XMQ76kir3VmqTeKNka7aS23yefgTZfQAVb+JDw4= =EauF -----END PGP SIGNATURE----- Merge tag 'soc-drivers-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull SoC driver updates from Arnd Bergmann: "The driver updates seem larger this time around, with changes is many of the SoC specific drivers, both the custom drivers/soc ones and the closely related subsystems (memory, bus, firmware, reset, ...). The at91 platform gains support for sam9x7 chips in the soc and power management code. This is the latest variant of one of the oldest still supported SoC families, using the ARM9 (ARMv5) core. As usual, the qualcomm snapdragon platform gets a ton of updates in many of their drivers to add more features and additional SoC support. Most of these are somewhat firmware related as the platform has a number of firmware based interfaces to the kernel. A notable addition here is the inclusion of trace events to two of these drivers. Herve Codina and Christophe Leroy are now sending updates for drivers/soc/fsl/ code through the SoC tree, this contains both PowerPC and Arm specific platforms and has previously been problematic to maintain. The first update here contains support for newer PowerPC variants and some cleanups. The turris mox firmware driver has a number of updates, mostly cleanups. The Arm SCMI firmware driver gets a major rework to modularize the existing code into separately loadable drivers for the various transports, the addition of custom NXP i.MX9 interfaces and a number of smaller updates. The Arm FF-A firmware driver gets a feature update to support the v1.2 version of the specification. The reset controller drivers have some smaller cleanups and a newly added driver for the Intel/Mobileye EyeQ5/EyeQ6 MIPS SoCs. The memory controller drivers get some cleanups and refactoring for Tegra, TI, Freescale/NXP and a couple more platforms. Finally there are lots of minor updates to firmware (raspberry pi, tegra, imx), bus (sunxi, omap, tegra) and soc (rockchips, tegra, amlogic, mediatek) drivers and their DT bindings" * tag 'soc-drivers-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (212 commits) firmware: imx: remove duplicate scmi_imx_misc_ctrl_get() platform: cznic: turris-omnia-mcu: Fix error check in omnia_mcu_register_trng() bus: sunxi-rsb: Simplify code with dev_err_probe() soc: fsl: qe: ucc: Export ucc_mux_set_grant_tsa_bkpt soc: fsl: cpm1: qmc: Fix dependency on fsl_soc.h dt-bindings: arm: rockchip: Add rk3576 compatible string to pmu.yaml soc: fsl: qbman: Remove redundant warnings soc: fsl: qbman: Use iommu_paging_domain_alloc() MAINTAINERS: Add QE files related to the Freescale QMC controller soc: fsl: cpm1: qmc: Handle QUICC Engine (QE) soft-qmc firmware soc: fsl: cpm1: qmc: Add support for QUICC Engine (QE) implementation soc: fsl: qe: Add missing PUSHSCHED command soc: fsl: qe: Add resource-managed muram allocators soc: fsl: cpm1: qmc: Introduce qmc_version soc: fsl: cpm1: qmc: Rename SCC_GSMRL_MODE_QMC soc: fsl: cpm1: qmc: Handle RPACK initialization soc: fsl: cpm1: qmc: Rename qmc_chan_command() soc: fsl: cpm1: qmc: Introduce qmc_{init,exit}_xcc() and their CPM1 version soc: fsl: cpm1: qmc: Introduce qmc_init_resource() and its CPM1 version soc: fsl: cpm1: qmc: Re-order probe() operations ...
This commit is contained in:
commit
b8979c6b4d
@ -25,10 +25,18 @@ select:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: amlogic,meson-gx-ao-secure
|
||||
- const: syscon
|
||||
|
||||
oneOf:
|
||||
- items:
|
||||
- const: amlogic,meson-gx-ao-secure
|
||||
- const: syscon
|
||||
- items:
|
||||
- enum:
|
||||
- amlogic,a4-ao-secure
|
||||
- amlogic,c3-ao-secure
|
||||
- amlogic,s4-ao-secure
|
||||
- amlogic,t7-ao-secure
|
||||
- const: amlogic,meson-gx-ao-secure
|
||||
- const: syscon
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
|
@ -11,7 +11,8 @@ PIT Timer required properties:
|
||||
shared across all System Controller members.
|
||||
|
||||
PIT64B Timer required properties:
|
||||
- compatible: Should be "microchip,sam9x60-pit64b"
|
||||
- compatible: Should be "microchip,sam9x60-pit64b" or
|
||||
"microchip,sam9x7-pit64b", "microchip,sam9x60-pit64b"
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain interrupt for PIT64B timer
|
||||
- clocks: Should contain the available clock sources for PIT64B timer.
|
||||
@ -31,7 +32,8 @@ RAMC SDRAM/DDR Controller required properties:
|
||||
"atmel,at91sam9g45-ddramc",
|
||||
"atmel,sama5d3-ddramc",
|
||||
"microchip,sam9x60-ddramc",
|
||||
"microchip,sama7g5-uddrc"
|
||||
"microchip,sama7g5-uddrc",
|
||||
"microchip,sam9x7-ddramc", "atmel,sama5d3-ddramc".
|
||||
- reg: Should contain registers location and length
|
||||
|
||||
Examples:
|
||||
|
@ -26,6 +26,7 @@ select:
|
||||
- rockchip,rk3368-pmu
|
||||
- rockchip,rk3399-pmu
|
||||
- rockchip,rk3568-pmu
|
||||
- rockchip,rk3576-pmu
|
||||
- rockchip,rk3588-pmu
|
||||
- rockchip,rv1126-pmu
|
||||
|
||||
@ -43,6 +44,7 @@ properties:
|
||||
- rockchip,rk3368-pmu
|
||||
- rockchip,rk3399-pmu
|
||||
- rockchip,rk3568-pmu
|
||||
- rockchip,rk3576-pmu
|
||||
- rockchip,rk3588-pmu
|
||||
- rockchip,rv1126-pmu
|
||||
- const: syscon
|
||||
|
@ -139,7 +139,7 @@ examples:
|
||||
- |
|
||||
rpm {
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-msm8916";
|
||||
compatible = "qcom,rpm-msm8916", "qcom,smd-rpm";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
clock-controller {
|
||||
|
@ -22,6 +22,9 @@ description: |
|
||||
|
||||
[0] https://developer.arm.com/documentation/den0056/latest
|
||||
|
||||
anyOf:
|
||||
- $ref: /schemas/firmware/nxp,imx95-scmi.yaml
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
const: scmi
|
||||
@ -121,6 +124,13 @@ properties:
|
||||
atomic mode of operation, even if requested.
|
||||
default: 0
|
||||
|
||||
max-rx-timeout-ms:
|
||||
description:
|
||||
An optional time value, expressed in milliseconds, representing the
|
||||
transport maximum timeout value for the receive channel. The value should
|
||||
be a non-zero value if set.
|
||||
minimum: 1
|
||||
|
||||
arm,smc-id:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
@ -145,6 +155,14 @@ properties:
|
||||
required:
|
||||
- '#power-domain-cells'
|
||||
|
||||
protocol@12:
|
||||
$ref: '#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0x12
|
||||
|
||||
protocol@13:
|
||||
$ref: '#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
@ -284,7 +302,7 @@ properties:
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
$defs:
|
||||
protocol-node:
|
||||
|
@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2024 NXP
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/firmware/nxp,imx95-scmi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: i.MX95 System Control and Management Interface(SCMI) Vendor Protocols Extension
|
||||
|
||||
maintainers:
|
||||
- Peng Fan <peng.fan@nxp.com>
|
||||
|
||||
properties:
|
||||
protocol@81:
|
||||
$ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0x81
|
||||
|
||||
protocol@84:
|
||||
$ref: '/schemas/firmware/arm,scmi.yaml#/$defs/protocol-node'
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
const: 0x84
|
||||
|
||||
nxp,ctrl-ids:
|
||||
description:
|
||||
Each entry consists of 2 integers, represents the ctrl id and the value
|
||||
items:
|
||||
items:
|
||||
- description: the ctrl id index
|
||||
enum: [0, 1, 2, 3, 4, 5, 6, 7, 0x8000, 0x8001, 0x8002, 0x8003,
|
||||
0x8004, 0x8005, 0x8006, 0x8007]
|
||||
- description: the value assigned to the ctrl id
|
||||
minItems: 1
|
||||
maxItems: 16
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
|
||||
additionalProperties: true
|
@ -134,9 +134,8 @@ allOf:
|
||||
properties:
|
||||
fsl,weim-cs-timing:
|
||||
items:
|
||||
items:
|
||||
- description: CSxU
|
||||
- description: CSxL
|
||||
- description: CSxU
|
||||
- description: CSxL
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -151,10 +150,9 @@ allOf:
|
||||
properties:
|
||||
fsl,weim-cs-timing:
|
||||
items:
|
||||
items:
|
||||
- description: CSCRxU
|
||||
- description: CSCRxL
|
||||
- description: CSCRxA
|
||||
- description: CSCRxU
|
||||
- description: CSCRxL
|
||||
- description: CSCRxA
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -171,13 +169,12 @@ allOf:
|
||||
properties:
|
||||
fsl,weim-cs-timing:
|
||||
items:
|
||||
items:
|
||||
- description: CSxGCR1
|
||||
- description: CSxGCR2
|
||||
- description: CSxRCR1
|
||||
- description: CSxRCR2
|
||||
- description: CSxWCR1
|
||||
- description: CSxWCR2
|
||||
- description: CSxGCR1
|
||||
- description: CSxGCR2
|
||||
- description: CSxRCR1
|
||||
- description: CSxRCR2
|
||||
- description: CSxWCR1
|
||||
- description: CSxWCR2
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -67,7 +67,9 @@ properties:
|
||||
- const: dirmap
|
||||
- const: wbuf
|
||||
|
||||
clocks: true
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
@ -50,6 +50,7 @@ properties:
|
||||
- rockchip,rk3188-io-voltage-domain
|
||||
- rockchip,rk3228-io-voltage-domain
|
||||
- rockchip,rk3288-io-voltage-domain
|
||||
- rockchip,rk3308-io-voltage-domain
|
||||
- rockchip,rk3328-io-voltage-domain
|
||||
- rockchip,rk3368-io-voltage-domain
|
||||
- rockchip,rk3368-pmu-io-voltage-domain
|
||||
@ -71,6 +72,7 @@ allOf:
|
||||
- $ref: "#/$defs/rk3188"
|
||||
- $ref: "#/$defs/rk3228"
|
||||
- $ref: "#/$defs/rk3288"
|
||||
- $ref: "#/$defs/rk3308"
|
||||
- $ref: "#/$defs/rk3328"
|
||||
- $ref: "#/$defs/rk3368"
|
||||
- $ref: "#/$defs/rk3368-pmu"
|
||||
@ -194,6 +196,28 @@ $defs:
|
||||
wifi-supply:
|
||||
description: The supply connected to APIO3_VDD. Also known as SDIO0.
|
||||
|
||||
rk3308:
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: rockchip,rk3308-io-voltage-domain
|
||||
|
||||
then:
|
||||
properties:
|
||||
vccio0-supply:
|
||||
description: The supply connected to VCCIO0.
|
||||
vccio1-supply:
|
||||
description: The supply connected to VCCIO1.
|
||||
vccio2-supply:
|
||||
description: The supply connected to VCCIO2.
|
||||
vccio3-supply:
|
||||
description: The supply connected to VCCIO3.
|
||||
vccio4-supply:
|
||||
description: The supply connected to VCCIO4.
|
||||
vccio5-supply:
|
||||
description: The supply connected to VCCIO5.
|
||||
|
||||
rk3328:
|
||||
if:
|
||||
properties:
|
||||
|
@ -90,7 +90,7 @@ examples:
|
||||
qcom,rpm-msg-ram = <&rpm_msg_ram>;
|
||||
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-msm8996";
|
||||
compatible = "qcom,rpm-msm8996", "qcom,glink-smd-rpm";
|
||||
qcom,glink-channels = "rpm_requests";
|
||||
|
||||
/* ... */
|
||||
|
@ -142,7 +142,7 @@ examples:
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-msm8916";
|
||||
compatible = "qcom,rpm-msm8916", "qcom,smd-rpm";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
/* ... */
|
||||
};
|
||||
@ -163,7 +163,7 @@ examples:
|
||||
mboxes = <&apcs_glb 0>;
|
||||
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-qcm2290";
|
||||
compatible = "qcom,rpm-qcm2290", "qcom,glink-smd-rpm";
|
||||
qcom,glink-channels = "rpm_requests";
|
||||
/* ... */
|
||||
};
|
||||
|
@ -19,6 +19,7 @@ properties:
|
||||
- amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs
|
||||
- amlogic,meson-s4-reset # Reset Controller on S4 and compatible SoCs
|
||||
- amlogic,c3-reset # Reset Controller on C3 and compatible SoCs
|
||||
- amlogic,t7-reset
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -1,43 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/reset/mobileye,eyeq5-reset.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Mobileye EyeQ5 reset controller
|
||||
|
||||
description:
|
||||
The EyeQ5 reset driver handles three reset domains. Its registers live in a
|
||||
shared region called OLB.
|
||||
|
||||
maintainers:
|
||||
- Grégory Clement <gregory.clement@bootlin.com>
|
||||
- Théo Lebrun <theo.lebrun@bootlin.com>
|
||||
- Vladimir Kondratiev <vladimir.kondratiev@mobileye.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mobileye,eyeq5-reset
|
||||
|
||||
reg:
|
||||
maxItems: 3
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: d0
|
||||
- const: d1
|
||||
- const: d2
|
||||
|
||||
"#reset-cells":
|
||||
const: 2
|
||||
description:
|
||||
The first cell is the domain (0 to 2 inclusive) and the second one is the
|
||||
reset index inside that domain.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- "#reset-cells"
|
||||
|
||||
additionalProperties: false
|
@ -38,13 +38,17 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names: true
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reset-names: true
|
||||
reset-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
|
210
Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-tsa.yaml
Normal file
210
Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-tsa.yaml
Normal file
@ -0,0 +1,210 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/fsl/cpm_qe/fsl,qe-tsa.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: PowerQUICC QE Time-slot assigner (TSA) controller
|
||||
|
||||
maintainers:
|
||||
- Herve Codina <herve.codina@bootlin.com>
|
||||
|
||||
description:
|
||||
The TSA is the time-slot assigner that can be found on some PowerQUICC SoC.
|
||||
Its purpose is to route some TDM time-slots to other internal serial
|
||||
controllers.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- fsl,mpc8321-tsa
|
||||
- const: fsl,qe-tsa
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: SI (Serial Interface) register base
|
||||
- description: SI RAM base
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: si_regs
|
||||
- const: si_ram
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^tdm@[0-3]$':
|
||||
description:
|
||||
The TDM managed by this controller
|
||||
type: object
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
description:
|
||||
The TDM number for this TDM, 0 for TDMa, 1 for TDMb, 2 for TDMc and 3
|
||||
for TDMd.
|
||||
|
||||
fsl,common-rxtx-pins:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The hardware can use four dedicated pins for Tx clock, Tx sync, Rx
|
||||
clock and Rx sync or use only two pins, Tx/Rx clock and Tx/Rx sync.
|
||||
Without the 'fsl,common-rxtx-pins' property, the four pins are used.
|
||||
With the 'fsl,common-rxtx-pins' property, two pins are used.
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
items:
|
||||
- description: Receive sync clock
|
||||
- description: Receive data clock
|
||||
- description: Transmit sync clock
|
||||
- description: Transmit data clock
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: rsync
|
||||
- const: rclk
|
||||
- const: tsync
|
||||
- const: tclk
|
||||
|
||||
fsl,rx-frame-sync-delay-bits:
|
||||
enum: [0, 1, 2, 3]
|
||||
default: 0
|
||||
description: |
|
||||
Receive frame sync delay in number of bits.
|
||||
Indicates the delay between the Rx sync and the first bit of the Rx
|
||||
frame.
|
||||
|
||||
fsl,tx-frame-sync-delay-bits:
|
||||
enum: [0, 1, 2, 3]
|
||||
default: 0
|
||||
description: |
|
||||
Transmit frame sync delay in number of bits.
|
||||
Indicates the delay between the Tx sync and the first bit of the Tx
|
||||
frame.
|
||||
|
||||
fsl,clock-falling-edge:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Data is sent on falling edge of the clock (and received on the rising
|
||||
edge). If not present, data is sent on the rising edge (and received
|
||||
on the falling edge).
|
||||
|
||||
fsl,fsync-rising-edge:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Frame sync pulses are sampled with the rising edge of the channel
|
||||
clock. If not present, pulses are sampled with the falling edge.
|
||||
|
||||
fsl,fsync-active-low:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Frame sync signals are active on low logic level.
|
||||
If not present, sync signals are active on high level.
|
||||
|
||||
fsl,double-speed-clock:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The channel clock is twice the data rate.
|
||||
|
||||
patternProperties:
|
||||
'^fsl,[rt]x-ts-routes$':
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
description: |
|
||||
A list of tuple that indicates the Tx or Rx time-slots routes.
|
||||
items:
|
||||
items:
|
||||
- description:
|
||||
The number of time-slots
|
||||
minimum: 1
|
||||
maximum: 64
|
||||
- description: |
|
||||
The source (Tx) or destination (Rx) serial interface
|
||||
(dt-bindings/soc/qe-fsl,tsa.h defines these values)
|
||||
- 0: No destination
|
||||
- 1: UCC1
|
||||
- 2: UCC2
|
||||
- 3: UCC3
|
||||
- 4: UCC4
|
||||
- 5: UCC5
|
||||
enum: [0, 1, 2, 3, 4, 5]
|
||||
minItems: 1
|
||||
maxItems: 64
|
||||
|
||||
allOf:
|
||||
# If fsl,common-rxtx-pins is present, only 2 clocks are needed.
|
||||
# Else, the 4 clocks must be present.
|
||||
- if:
|
||||
required:
|
||||
- fsl,common-rxtx-pins
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
maxItems: 2
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
clock-names:
|
||||
minItems: 4
|
||||
|
||||
required:
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/soc/qe-fsl,tsa.h>
|
||||
|
||||
tsa@ae0 {
|
||||
compatible = "fsl,mpc8321-tsa", "fsl,qe-tsa";
|
||||
reg = <0xae0 0x10>,
|
||||
<0xc00 0x200>;
|
||||
reg-names = "si_regs", "si_ram";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
tdm@0 {
|
||||
/* TDMa */
|
||||
reg = <0>;
|
||||
|
||||
clocks = <&clk_l1rsynca>, <&clk_l1rclka>;
|
||||
clock-names = "rsync", "rclk";
|
||||
|
||||
fsl,common-rxtx-pins;
|
||||
fsl,fsync-rising-edge;
|
||||
|
||||
fsl,tx-ts-routes = <2 0>, /* TS 0..1 */
|
||||
<24 FSL_QE_TSA_UCC4>, /* TS 2..25 */
|
||||
<1 0>, /* TS 26 */
|
||||
<5 FSL_QE_TSA_UCC3>; /* TS 27..31 */
|
||||
|
||||
fsl,rx-ts-routes = <2 0>, /* TS 0..1 */
|
||||
<24 FSL_QE_TSA_UCC4>, /* 2..25 */
|
||||
<1 0>, /* TS 26 */
|
||||
<5 FSL_QE_TSA_UCC3>; /* TS 27..31 */
|
||||
};
|
||||
};
|
@ -0,0 +1,197 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/soc/fsl/cpm_qe/fsl,qe-ucc-qmc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: PowerQUICC QE QUICC Multichannel Controller (QMC)
|
||||
|
||||
maintainers:
|
||||
- Herve Codina <herve.codina@bootlin.com>
|
||||
|
||||
description:
|
||||
The QMC (QUICC Multichannel Controller) emulates up to 64 channels within one
|
||||
serial controller using the same TDM physical interface routed from TSA.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- fsl,mpc8321-ucc-qmc
|
||||
- const: fsl,qe-ucc-qmc
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: UCC (Unified communication controller) register base
|
||||
- description: Dual port ram base
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: ucc_regs
|
||||
- const: dpram
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: UCC interrupt line in the QE interrupt controller
|
||||
|
||||
fsl,tsa-serial:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to TSA node
|
||||
- enum: [1, 2, 3, 4, 5]
|
||||
description: |
|
||||
TSA serial interface (dt-bindings/soc/qe-fsl,tsa.h defines these
|
||||
values)
|
||||
- 1: UCC1
|
||||
- 2: UCC2
|
||||
- 3: UCC3
|
||||
- 4: UCC4
|
||||
- 5: UCC5
|
||||
description:
|
||||
Should be a phandle/number pair. The phandle to TSA node and the TSA
|
||||
serial interface to use.
|
||||
|
||||
fsl,soft-qmc:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
Soft QMC firmware name to load. If this property is omitted, no firmware
|
||||
are used.
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^channel@([0-9]|[1-5][0-9]|6[0-3])$':
|
||||
description:
|
||||
A channel managed by this controller
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- fsl,mpc8321-ucc-qmc-hdlc
|
||||
- const: fsl,qe-ucc-qmc-hdlc
|
||||
- const: fsl,qmc-hdlc
|
||||
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 63
|
||||
description:
|
||||
The channel number
|
||||
|
||||
fsl,operational-mode:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [transparent, hdlc]
|
||||
default: transparent
|
||||
description: |
|
||||
The channel operational mode
|
||||
- hdlc: The channel handles HDLC frames
|
||||
- transparent: The channel handles raw data without any processing
|
||||
|
||||
fsl,reverse-data:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The bit order as seen on the channels is reversed,
|
||||
transmitting/receiving the MSB of each octet first.
|
||||
This flag is used only in 'transparent' mode.
|
||||
|
||||
fsl,tx-ts-mask:
|
||||
$ref: /schemas/types.yaml#/definitions/uint64
|
||||
description:
|
||||
Channel assigned Tx time-slots within the Tx time-slots routed by the
|
||||
TSA to this cell.
|
||||
|
||||
fsl,rx-ts-mask:
|
||||
$ref: /schemas/types.yaml#/definitions/uint64
|
||||
description:
|
||||
Channel assigned Rx time-slots within the Rx time-slots routed by the
|
||||
TSA to this cell.
|
||||
|
||||
fsl,framer:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
phandle to the framer node. The framer is in charge of an E1/T1 line
|
||||
interface connected to the TDM bus. It can be used to get the E1/T1 line
|
||||
status such as link up/down.
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
not:
|
||||
contains:
|
||||
const: fsl,qmc-hdlc
|
||||
then:
|
||||
properties:
|
||||
fsl,framer: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
- fsl,tx-ts-mask
|
||||
- fsl,rx-ts-mask
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- fsl,tsa-serial
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/soc/qe-fsl,tsa.h>
|
||||
|
||||
qmc@a60 {
|
||||
compatible = "fsl,mpc8321-ucc-qmc", "fsl,qe-ucc-qmc";
|
||||
reg = <0x3200 0x200>,
|
||||
<0x10000 0x1000>;
|
||||
reg-names = "ucc_regs", "dpram";
|
||||
interrupts = <35>;
|
||||
interrupt-parent = <&qeic>;
|
||||
fsl,soft-qmc = "fsl_qe_ucode_qmc_8321_11.bin";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
fsl,tsa-serial = <&tsa FSL_QE_TSA_UCC4>;
|
||||
|
||||
channel@16 {
|
||||
/* Ch16 : First 4 even TS from all routed from TSA */
|
||||
reg = <16>;
|
||||
fsl,operational-mode = "transparent";
|
||||
fsl,reverse-data;
|
||||
fsl,tx-ts-mask = <0x00000000 0x000000aa>;
|
||||
fsl,rx-ts-mask = <0x00000000 0x000000aa>;
|
||||
};
|
||||
|
||||
channel@17 {
|
||||
/* Ch17 : First 4 odd TS from all routed from TSA */
|
||||
reg = <17>;
|
||||
fsl,operational-mode = "transparent";
|
||||
fsl,reverse-data;
|
||||
fsl,tx-ts-mask = <0x00000000 0x00000055>;
|
||||
fsl,rx-ts-mask = <0x00000000 0x00000055>;
|
||||
};
|
||||
|
||||
channel@19 {
|
||||
/* Ch19 : 8 TS (TS 8..15) from all routed from TSA */
|
||||
compatible = "fsl,mpc8321-ucc-qmc-hdlc",
|
||||
"fsl,qe-ucc-qmc-hdlc",
|
||||
"fsl,qmc-hdlc";
|
||||
reg = <19>;
|
||||
fsl,operational-mode = "hdlc";
|
||||
fsl,tx-ts-mask = <0x00000000 0x0000ff00>;
|
||||
fsl,rx-ts-mask = <0x00000000 0x0000ff00>;
|
||||
fsl,framer = <&framer>;
|
||||
};
|
||||
};
|
@ -30,6 +30,11 @@ properties:
|
||||
- qcom,sm8450-pmic-glink
|
||||
- qcom,sm8550-pmic-glink
|
||||
- const: qcom,pmic-glink
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm7325-pmic-glink
|
||||
- const: qcom,qcm6490-pmic-glink
|
||||
- const: qcom,pmic-glink
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm8650-pmic-glink
|
||||
|
@ -30,31 +30,37 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,rpm-apq8084
|
||||
- qcom,rpm-ipq6018
|
||||
- qcom,rpm-ipq9574
|
||||
- qcom,rpm-mdm9607
|
||||
- qcom,rpm-msm8226
|
||||
- qcom,rpm-msm8610
|
||||
- qcom,rpm-msm8909
|
||||
- qcom,rpm-msm8916
|
||||
- qcom,rpm-msm8917
|
||||
- qcom,rpm-msm8936
|
||||
- qcom,rpm-msm8937
|
||||
- qcom,rpm-msm8952
|
||||
- qcom,rpm-msm8953
|
||||
- qcom,rpm-msm8974
|
||||
- qcom,rpm-msm8976
|
||||
- qcom,rpm-msm8994
|
||||
- qcom,rpm-msm8996
|
||||
- qcom,rpm-msm8998
|
||||
- qcom,rpm-qcm2290
|
||||
- qcom,rpm-qcs404
|
||||
- qcom,rpm-sdm660
|
||||
- qcom,rpm-sm6115
|
||||
- qcom,rpm-sm6125
|
||||
- qcom,rpm-sm6375
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,rpm-apq8084
|
||||
- qcom,rpm-mdm9607
|
||||
- qcom,rpm-msm8226
|
||||
- qcom,rpm-msm8610
|
||||
- qcom,rpm-msm8909
|
||||
- qcom,rpm-msm8916
|
||||
- qcom,rpm-msm8917
|
||||
- qcom,rpm-msm8936
|
||||
- qcom,rpm-msm8937
|
||||
- qcom,rpm-msm8952
|
||||
- qcom,rpm-msm8953
|
||||
- qcom,rpm-msm8974
|
||||
- qcom,rpm-msm8976
|
||||
- qcom,rpm-msm8994
|
||||
- const: qcom,smd-rpm
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,rpm-ipq6018
|
||||
- qcom,rpm-ipq9574
|
||||
- qcom,rpm-msm8996
|
||||
- qcom,rpm-msm8998
|
||||
- qcom,rpm-qcm2290
|
||||
- qcom,rpm-qcs404
|
||||
- qcom,rpm-sdm660
|
||||
- qcom,rpm-sm6115
|
||||
- qcom,rpm-sm6125
|
||||
- qcom,rpm-sm6375
|
||||
- const: qcom,glink-smd-rpm
|
||||
|
||||
clock-controller:
|
||||
$ref: /schemas/clock/qcom,rpmcc.yaml#
|
||||
@ -84,21 +90,7 @@ if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,rpm-apq8084
|
||||
- qcom,rpm-mdm9607
|
||||
- qcom,rpm-msm8226
|
||||
- qcom,rpm-msm8610
|
||||
- qcom,rpm-msm8909
|
||||
- qcom,rpm-msm8916
|
||||
- qcom,rpm-msm8917
|
||||
- qcom,rpm-msm8936
|
||||
- qcom,rpm-msm8937
|
||||
- qcom,rpm-msm8952
|
||||
- qcom,rpm-msm8953
|
||||
- qcom,rpm-msm8974
|
||||
- qcom,rpm-msm8976
|
||||
- qcom,rpm-msm8994
|
||||
const: qcom,smd-rpm
|
||||
then:
|
||||
properties:
|
||||
qcom,glink-channels: false
|
||||
@ -129,7 +121,7 @@ examples:
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-msm8916";
|
||||
compatible = "qcom,rpm-msm8916", "qcom,smd-rpm";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
clock-controller {
|
||||
|
@ -56,7 +56,7 @@ examples:
|
||||
qcom,smd-edge = <15>;
|
||||
|
||||
rpm-requests {
|
||||
compatible = "qcom,rpm-msm8974";
|
||||
compatible = "qcom,rpm-msm8974", "qcom,smd-rpm";
|
||||
qcom,smd-channels = "rpm_requests";
|
||||
|
||||
clock-controller {
|
||||
|
@ -20,6 +20,20 @@ properties:
|
||||
- rockchip,rk3568-pipe-grf
|
||||
- rockchip,rk3568-pipe-phy-grf
|
||||
- rockchip,rk3568-usb2phy-grf
|
||||
- rockchip,rk3576-bigcore-grf
|
||||
- rockchip,rk3576-cci-grf
|
||||
- rockchip,rk3576-gpu-grf
|
||||
- rockchip,rk3576-litcore-grf
|
||||
- rockchip,rk3576-npu-grf
|
||||
- rockchip,rk3576-php-grf
|
||||
- rockchip,rk3576-pipe-phy-grf
|
||||
- rockchip,rk3576-pmu1-grf
|
||||
- rockchip,rk3576-sdgmac-grf
|
||||
- rockchip,rk3576-sys-grf
|
||||
- rockchip,rk3576-usb-grf
|
||||
- rockchip,rk3576-usbdpphy-grf
|
||||
- rockchip,rk3576-vo0-grf
|
||||
- rockchip,rk3576-vop-grf
|
||||
- rockchip,rk3588-bigcore0-grf
|
||||
- rockchip,rk3588-bigcore1-grf
|
||||
- rockchip,rk3588-hdptxphy-grf
|
||||
@ -64,6 +78,8 @@ properties:
|
||||
- rockchip,rk3399-pmugrf
|
||||
- rockchip,rk3568-grf
|
||||
- rockchip,rk3568-pmugrf
|
||||
- rockchip,rk3576-ioc-grf
|
||||
- rockchip,rk3576-pmu0-grf
|
||||
- rockchip,rk3588-usb2phy-grf
|
||||
- rockchip,rv1108-grf
|
||||
- rockchip,rv1108-pmugrf
|
||||
|
@ -32,11 +32,16 @@ properties:
|
||||
- enum:
|
||||
- samsung,exynos850-usi
|
||||
|
||||
reg: true
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks: true
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names: true
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: ipclk
|
||||
|
||||
ranges: true
|
||||
|
||||
@ -113,9 +118,7 @@ then:
|
||||
- description: Operating clock for UART/SPI/I2C protocol
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: ipclk
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
@ -9051,6 +9051,7 @@ M: Herve Codina <herve.codina@bootlin.com>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-scc-qmc.yaml
|
||||
F: Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-ucc-qmc.yaml
|
||||
F: drivers/soc/fsl/qe/qmc.c
|
||||
F: include/soc/fsl/qe/qmc.h
|
||||
|
||||
@ -9066,9 +9067,11 @@ M: Herve Codina <herve.codina@bootlin.com>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml
|
||||
F: Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-tsa.yaml
|
||||
F: drivers/soc/fsl/qe/tsa.c
|
||||
F: drivers/soc/fsl/qe/tsa.h
|
||||
F: include/dt-bindings/soc/cpm1-fsl,tsa.h
|
||||
F: include/dt-bindings/soc/qe-fsl,tsa.h
|
||||
|
||||
FREESCALE QUICC ENGINE UCC ETHERNET DRIVER
|
||||
L: netdev@vger.kernel.org
|
||||
@ -18963,6 +18966,7 @@ L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml
|
||||
F: drivers/soc/qcom/icc-bwmon.c
|
||||
F: drivers/soc/qcom/trace_icc-bwmon.h
|
||||
|
||||
QUALCOMM IOMMU
|
||||
M: Rob Clark <robdclark@gmail.com>
|
||||
|
@ -141,11 +141,27 @@ config SOC_SAM9X60
|
||||
help
|
||||
Select this if you are using Microchip's SAM9X60 SoC
|
||||
|
||||
config SOC_SAM9X7
|
||||
bool "SAM9X7"
|
||||
depends on ARCH_MULTI_V5
|
||||
select ATMEL_AIC5_IRQ
|
||||
select ATMEL_PM if PM
|
||||
select CPU_ARM926T
|
||||
select HAVE_AT91_USB_CLK
|
||||
select HAVE_AT91_GENERATED_CLK
|
||||
select HAVE_AT91_SAM9X60_PLL
|
||||
select MEMORY
|
||||
select PINCTRL_AT91
|
||||
select SOC_SAM_V4_V5
|
||||
select SRAM if PM
|
||||
help
|
||||
Select this if you are using Microchip's SAM9X7 SoC
|
||||
|
||||
comment "Clocksource driver selection"
|
||||
|
||||
config ATMEL_CLOCKSOURCE_PIT
|
||||
bool "Periodic Interval Timer (PIT) support"
|
||||
depends on SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5
|
||||
depends on SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5
|
||||
default SOC_AT91SAM9 || SOC_SAMA5
|
||||
select ATMEL_PIT
|
||||
help
|
||||
@ -155,7 +171,7 @@ config ATMEL_CLOCKSOURCE_PIT
|
||||
|
||||
config ATMEL_CLOCKSOURCE_TCB
|
||||
bool "Timer Counter Blocks (TCB) support"
|
||||
default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5
|
||||
default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5
|
||||
select ATMEL_TCB_CLKSRC
|
||||
help
|
||||
Select this to get a high precision clocksource based on a
|
||||
@ -166,7 +182,7 @@ config ATMEL_CLOCKSOURCE_TCB
|
||||
|
||||
config MICROCHIP_CLOCKSOURCE_PIT64B
|
||||
bool "64-bit Periodic Interval Timer (PIT64B) support"
|
||||
default SOC_SAM9X60 || SOC_SAMA7
|
||||
default SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA7
|
||||
select MICROCHIP_PIT64B
|
||||
help
|
||||
Select this to get a high resolution clockevent (SAM9X60) or
|
||||
|
@ -7,6 +7,7 @@
|
||||
obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o
|
||||
obj-$(CONFIG_SOC_AT91SAM9) += at91sam9.o
|
||||
obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
|
||||
obj-$(CONFIG_SOC_SAM9X7) += sam9x7.o
|
||||
obj-$(CONFIG_SOC_SAMA5) += sama5.o sam_secure.o
|
||||
obj-$(CONFIG_SOC_SAMA7) += sama7.o
|
||||
obj-$(CONFIG_SOC_SAMV7) += samv7.o
|
||||
|
@ -12,6 +12,7 @@
|
||||
extern void __init at91rm9200_pm_init(void);
|
||||
extern void __init at91sam9_pm_init(void);
|
||||
extern void __init sam9x60_pm_init(void);
|
||||
extern void __init sam9x7_pm_init(void);
|
||||
extern void __init sama5_pm_init(void);
|
||||
extern void __init sama5d2_pm_init(void);
|
||||
extern void __init sama7_pm_init(void);
|
||||
@ -19,6 +20,7 @@ extern void __init sama7_pm_init(void);
|
||||
static inline void __init at91rm9200_pm_init(void) { }
|
||||
static inline void __init at91sam9_pm_init(void) { }
|
||||
static inline void __init sam9x60_pm_init(void) { }
|
||||
static inline void __init sam9x7_pm_init(void) { }
|
||||
static inline void __init sama5_pm_init(void) { }
|
||||
static inline void __init sama5d2_pm_init(void) { }
|
||||
static inline void __init sama7_pm_init(void) { }
|
||||
|
@ -233,6 +233,13 @@ static const struct of_device_id sama7g5_ws_ids[] = {
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct of_device_id sam9x7_ws_ids[] = {
|
||||
{ .compatible = "microchip,sam9x7-rtc", .data = &ws_info[1] },
|
||||
{ .compatible = "microchip,sam9x7-rtt", .data = &ws_info[4] },
|
||||
{ .compatible = "microchip,sam9x7-gem", .data = &ws_info[5] },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int at91_pm_config_ws(unsigned int pm_mode, bool set)
|
||||
{
|
||||
const struct wakeup_source_info *wsi;
|
||||
@ -1361,6 +1368,7 @@ static const struct of_device_id atmel_pmc_ids[] __initconst = {
|
||||
{ .compatible = "atmel,sama5d4-pmc", .data = &pmc_infos[1] },
|
||||
{ .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] },
|
||||
{ .compatible = "microchip,sam9x60-pmc", .data = &pmc_infos[4] },
|
||||
{ .compatible = "microchip,sam9x7-pmc", .data = &pmc_infos[4] },
|
||||
{ .compatible = "microchip,sama7g5-pmc", .data = &pmc_infos[5] },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
@ -1499,6 +1507,27 @@ void __init sam9x60_pm_init(void)
|
||||
soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws;
|
||||
}
|
||||
|
||||
void __init sam9x7_pm_init(void)
|
||||
{
|
||||
static const int modes[] __initconst = {
|
||||
AT91_PM_STANDBY, AT91_PM_ULP0,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SOC_SAM9X7))
|
||||
return;
|
||||
|
||||
at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
|
||||
ret = at91_dt_ramc(false);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
at91_pm_init(NULL);
|
||||
|
||||
soc_pm.ws_ids = sam9x7_ws_ids;
|
||||
soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws;
|
||||
}
|
||||
|
||||
void __init at91sam9_pm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
33
arch/arm/mach-at91/sam9x7.c
Normal file
33
arch/arm/mach-at91/sam9x7.c
Normal file
@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Setup code for SAM9X7.
|
||||
*
|
||||
* Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries
|
||||
*
|
||||
* Author: Varshini Rajendran <varshini.rajendran@microchip.com>
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
|
||||
#include "generic.h"
|
||||
|
||||
static void __init sam9x7_init(void)
|
||||
{
|
||||
of_platform_default_populate(NULL, NULL, NULL);
|
||||
|
||||
sam9x7_pm_init();
|
||||
}
|
||||
|
||||
static const char * const sam9x7_dt_board_compat[] __initconst = {
|
||||
"microchip,sam9x7",
|
||||
NULL
|
||||
};
|
||||
|
||||
DT_MACHINE_START(sam9x7_dt, "Microchip SAM9X7")
|
||||
/* Maintainer: Microchip */
|
||||
.init_machine = sam9x7_init,
|
||||
.dt_compat = sam9x7_dt_board_compat,
|
||||
MACHINE_END
|
@ -70,6 +70,7 @@ static void __init realview_smp_prepare_cpus(unsigned int max_cpus)
|
||||
return;
|
||||
}
|
||||
map = syscon_node_to_regmap(np);
|
||||
of_node_put(np);
|
||||
if (IS_ERR(map)) {
|
||||
pr_err("PLATSMP: No syscon regmap\n");
|
||||
return;
|
||||
|
@ -89,7 +89,7 @@ config HISILICON_LPC
|
||||
|
||||
config IMX_WEIM
|
||||
bool "Freescale EIM DRIVER"
|
||||
depends on ARCH_MXC
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
help
|
||||
Driver for i.MX WEIM controller.
|
||||
The WEIM(Wireless External Interface Module) works like a bus.
|
||||
|
@ -85,6 +85,7 @@ static int integrator_ap_lm_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
map = syscon_node_to_regmap(syscon);
|
||||
of_node_put(syscon);
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(dev,
|
||||
"could not find Integrator/AP system controller\n");
|
||||
|
@ -282,22 +282,18 @@ static int weim_probe(struct platform_device *pdev)
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
/* get the clock */
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* parse the device node */
|
||||
ret = weim_parse_dt(pdev);
|
||||
if (ret)
|
||||
clk_disable_unprepare(clk);
|
||||
else
|
||||
dev_info(&pdev->dev, "Driver registered.\n");
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
dev_info(&pdev->dev, "Driver registered.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
|
||||
|
@ -751,12 +751,10 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
||||
int irq, ret;
|
||||
|
||||
of_property_read_u32(np, "clock-frequency", &clk_freq);
|
||||
if (clk_freq > RSB_MAX_FREQ) {
|
||||
dev_err(dev,
|
||||
"clock-frequency (%u Hz) is too high (max = 20MHz)\n",
|
||||
clk_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (clk_freq > RSB_MAX_FREQ)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"clock-frequency (%u Hz) is too high (max = 20MHz)\n",
|
||||
clk_freq);
|
||||
|
||||
rsb = devm_kzalloc(dev, sizeof(*rsb), GFP_KERNEL);
|
||||
if (!rsb)
|
||||
@ -774,28 +772,22 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
||||
return irq;
|
||||
|
||||
rsb->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(rsb->clk)) {
|
||||
ret = PTR_ERR(rsb->clk);
|
||||
dev_err(dev, "failed to retrieve clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(rsb->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(rsb->clk),
|
||||
"failed to retrieve clk\n");
|
||||
|
||||
rsb->rstc = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(rsb->rstc)) {
|
||||
ret = PTR_ERR(rsb->rstc);
|
||||
dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(rsb->rstc))
|
||||
return dev_err_probe(dev, PTR_ERR(rsb->rstc),
|
||||
"failed to retrieve reset controller\n");
|
||||
|
||||
init_completion(&rsb->complete);
|
||||
mutex_init(&rsb->lock);
|
||||
|
||||
ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't register interrupt handler irq %d: %d\n",
|
||||
irq, ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"can't register interrupt handler irq %d\n", irq);
|
||||
|
||||
ret = sunxi_rsb_hw_init(rsb);
|
||||
if (ret)
|
||||
|
@ -126,7 +126,6 @@ static const char * const clock_names[SYSC_MAX_CLOCKS] = {
|
||||
* @enabled: sysc runtime enabled status
|
||||
* @needs_resume: runtime resume needed on resume from suspend
|
||||
* @child_needs_resume: runtime resume needed for child on resume from suspend
|
||||
* @disable_on_idle: status flag used for disabling modules with resets
|
||||
* @idle_work: work structure used to perform delayed idle on a module
|
||||
* @pre_reset_quirk: module specific pre-reset quirk
|
||||
* @post_reset_quirk: module specific post-reset quirk
|
||||
@ -2569,14 +2568,12 @@ static const struct sysc_dts_quirk sysc_dts_quirks[] = {
|
||||
static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
|
||||
bool is_child)
|
||||
{
|
||||
const struct property *prop;
|
||||
int i, len;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sysc_dts_quirks); i++) {
|
||||
const char *name = sysc_dts_quirks[i].name;
|
||||
|
||||
prop = of_get_property(np, name, &len);
|
||||
if (!prop)
|
||||
if (!of_property_present(np, name))
|
||||
continue;
|
||||
|
||||
ddata->cfg.quirks |= sysc_dts_quirks[i].mask;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/arm_ffa.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -53,11 +54,8 @@
|
||||
#define PACK_TARGET_INFO(s, r) \
|
||||
(FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r)))
|
||||
|
||||
/*
|
||||
* Keeping RX TX buffer size as 4K for now
|
||||
* 64K may be preferred to keep it min a page in 64K PAGE_SIZE config
|
||||
*/
|
||||
#define RXTX_BUFFER_SIZE SZ_4K
|
||||
#define RXTX_MAP_MIN_BUFSZ_MASK GENMASK(1, 0)
|
||||
#define RXTX_MAP_MIN_BUFSZ(x) ((x) & RXTX_MAP_MIN_BUFSZ_MASK)
|
||||
|
||||
#define FFA_MAX_NOTIFICATIONS 64
|
||||
|
||||
@ -75,6 +73,7 @@ static const int ffa_linux_errmap[] = {
|
||||
-EAGAIN, /* FFA_RET_RETRY */
|
||||
-ECANCELED, /* FFA_RET_ABORTED */
|
||||
-ENODATA, /* FFA_RET_NO_DATA */
|
||||
-EAGAIN, /* FFA_RET_NOT_READY */
|
||||
};
|
||||
|
||||
static inline int ffa_to_linux_errno(int errno)
|
||||
@ -97,7 +96,9 @@ struct ffa_drv_info {
|
||||
struct mutex tx_lock; /* lock to protect Tx buffer */
|
||||
void *rx_buffer;
|
||||
void *tx_buffer;
|
||||
size_t rxtx_bufsz;
|
||||
bool mem_ops_native;
|
||||
bool msg_direct_req2_supp;
|
||||
bool bitmap_created;
|
||||
bool notif_enabled;
|
||||
unsigned int sched_recv_irq;
|
||||
@ -211,6 +212,32 @@ static int ffa_rxtx_unmap(u16 vm_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffa_features(u32 func_feat_id, u32 input_props,
|
||||
u32 *if_props_1, u32 *if_props_2)
|
||||
{
|
||||
ffa_value_t id;
|
||||
|
||||
if (!ARM_SMCCC_IS_FAST_CALL(func_feat_id) && input_props) {
|
||||
pr_err("%s: Invalid Parameters: %x, %x", __func__,
|
||||
func_feat_id, input_props);
|
||||
return ffa_to_linux_errno(FFA_RET_INVALID_PARAMETERS);
|
||||
}
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_FEATURES, .a1 = func_feat_id, .a2 = input_props,
|
||||
}, &id);
|
||||
|
||||
if (id.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)id.a2);
|
||||
|
||||
if (if_props_1)
|
||||
*if_props_1 = id.a2;
|
||||
if (if_props_2)
|
||||
*if_props_2 = id.a3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PARTITION_INFO_GET_RETURN_COUNT_ONLY BIT(0)
|
||||
|
||||
/* buffer must be sizeof(struct ffa_partition_info) * num_partitions */
|
||||
@ -260,17 +287,75 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
||||
return count;
|
||||
}
|
||||
|
||||
#define LAST_INDEX_MASK GENMASK(15, 0)
|
||||
#define CURRENT_INDEX_MASK GENMASK(31, 16)
|
||||
#define UUID_INFO_TAG_MASK GENMASK(47, 32)
|
||||
#define PARTITION_INFO_SZ_MASK GENMASK(63, 48)
|
||||
#define PARTITION_COUNT(x) ((u16)(FIELD_GET(LAST_INDEX_MASK, (x))) + 1)
|
||||
#define CURRENT_INDEX(x) ((u16)(FIELD_GET(CURRENT_INDEX_MASK, (x))))
|
||||
#define UUID_INFO_TAG(x) ((u16)(FIELD_GET(UUID_INFO_TAG_MASK, (x))))
|
||||
#define PARTITION_INFO_SZ(x) ((u16)(FIELD_GET(PARTITION_INFO_SZ_MASK, (x))))
|
||||
static int
|
||||
__ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
||||
struct ffa_partition_info *buffer, int num_parts)
|
||||
{
|
||||
u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0;
|
||||
ffa_value_t partition_info;
|
||||
|
||||
do {
|
||||
start_idx = prev_idx ? prev_idx + 1 : 0;
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_PARTITION_INFO_GET_REGS,
|
||||
.a1 = (u64)uuid1 << 32 | uuid0,
|
||||
.a2 = (u64)uuid3 << 32 | uuid2,
|
||||
.a3 = start_idx | tag << 16,
|
||||
}, &partition_info);
|
||||
|
||||
if (partition_info.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)partition_info.a2);
|
||||
|
||||
if (!count)
|
||||
count = PARTITION_COUNT(partition_info.a2);
|
||||
if (!buffer || !num_parts) /* count only */
|
||||
return count;
|
||||
|
||||
cur_idx = CURRENT_INDEX(partition_info.a2);
|
||||
tag = UUID_INFO_TAG(partition_info.a2);
|
||||
buf_sz = PARTITION_INFO_SZ(partition_info.a2);
|
||||
if (buf_sz > sizeof(*buffer))
|
||||
buf_sz = sizeof(*buffer);
|
||||
|
||||
memcpy(buffer + prev_idx * buf_sz, &partition_info.a3,
|
||||
(cur_idx - start_idx + 1) * buf_sz);
|
||||
prev_idx = cur_idx;
|
||||
|
||||
} while (cur_idx < (count - 1));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* buffer is allocated and caller must free the same if returned count > 0 */
|
||||
static int
|
||||
ffa_partition_probe(const uuid_t *uuid, struct ffa_partition_info **buffer)
|
||||
{
|
||||
int count;
|
||||
u32 uuid0_4[4];
|
||||
bool reg_mode = false;
|
||||
struct ffa_partition_info *pbuf;
|
||||
|
||||
if (!ffa_features(FFA_PARTITION_INFO_GET_REGS, 0, NULL, NULL))
|
||||
reg_mode = true;
|
||||
|
||||
export_uuid((u8 *)uuid0_4, uuid);
|
||||
count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1], uuid0_4[2],
|
||||
uuid0_4[3], NULL, 0);
|
||||
if (reg_mode)
|
||||
count = __ffa_partition_info_get_regs(uuid0_4[0], uuid0_4[1],
|
||||
uuid0_4[2], uuid0_4[3],
|
||||
NULL, 0);
|
||||
else
|
||||
count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1],
|
||||
uuid0_4[2], uuid0_4[3],
|
||||
NULL, 0);
|
||||
if (count <= 0)
|
||||
return count;
|
||||
|
||||
@ -278,8 +363,14 @@ ffa_partition_probe(const uuid_t *uuid, struct ffa_partition_info **buffer)
|
||||
if (!pbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1], uuid0_4[2],
|
||||
uuid0_4[3], pbuf, count);
|
||||
if (reg_mode)
|
||||
count = __ffa_partition_info_get_regs(uuid0_4[0], uuid0_4[1],
|
||||
uuid0_4[2], uuid0_4[3],
|
||||
pbuf, count);
|
||||
else
|
||||
count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1],
|
||||
uuid0_4[2], uuid0_4[3],
|
||||
pbuf, count);
|
||||
if (count <= 0)
|
||||
kfree(pbuf);
|
||||
else
|
||||
@ -305,6 +396,18 @@ static int ffa_id_get(u16 *vm_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ffa_msg_send_wait_for_completion(ffa_value_t *ret)
|
||||
{
|
||||
while (ret->a0 == FFA_INTERRUPT || ret->a0 == FFA_YIELD) {
|
||||
if (ret->a0 == FFA_YIELD)
|
||||
fsleep(1000);
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_RUN, .a1 = ret->a1,
|
||||
}, ret);
|
||||
}
|
||||
}
|
||||
|
||||
static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
|
||||
struct ffa_send_direct_data *data)
|
||||
{
|
||||
@ -325,10 +428,7 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
|
||||
.a6 = data->data3, .a7 = data->data4,
|
||||
}, &ret);
|
||||
|
||||
while (ret.a0 == FFA_INTERRUPT)
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_RUN, .a1 = ret.a1,
|
||||
}, &ret);
|
||||
ffa_msg_send_wait_for_completion(&ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
@ -352,7 +452,7 @@ static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
|
||||
ffa_value_t ret;
|
||||
int retval = 0;
|
||||
|
||||
if (sz > (RXTX_BUFFER_SIZE - sizeof(*msg)))
|
||||
if (sz > (drv_info->rxtx_bufsz - sizeof(*msg)))
|
||||
return -ERANGE;
|
||||
|
||||
mutex_lock(&drv_info->tx_lock);
|
||||
@ -377,6 +477,32 @@ static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int ffa_msg_send_direct_req2(u16 src_id, u16 dst_id, const uuid_t *uuid,
|
||||
struct ffa_send_direct_data2 *data)
|
||||
{
|
||||
u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
|
||||
ffa_value_t ret, args = {
|
||||
.a0 = FFA_MSG_SEND_DIRECT_REQ2, .a1 = src_dst_ids,
|
||||
};
|
||||
|
||||
export_uuid((u8 *)&args.a2, uuid);
|
||||
memcpy((void *)&args + offsetof(ffa_value_t, a4), data, sizeof(*data));
|
||||
|
||||
invoke_ffa_fn(args, &ret);
|
||||
|
||||
ffa_msg_send_wait_for_completion(&ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
|
||||
if (ret.a0 == FFA_MSG_SEND_DIRECT_RESP2) {
|
||||
memcpy(data, &ret.a4, sizeof(*data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
|
||||
u32 frag_len, u32 len, u64 *handle)
|
||||
{
|
||||
@ -561,9 +687,10 @@ static int ffa_memory_ops(u32 func_id, struct ffa_mem_ops_args *args)
|
||||
{
|
||||
int ret;
|
||||
void *buffer;
|
||||
size_t rxtx_bufsz = drv_info->rxtx_bufsz;
|
||||
|
||||
if (!args->use_txbuf) {
|
||||
buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL);
|
||||
buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
@ -571,12 +698,12 @@ static int ffa_memory_ops(u32 func_id, struct ffa_mem_ops_args *args)
|
||||
mutex_lock(&drv_info->tx_lock);
|
||||
}
|
||||
|
||||
ret = ffa_setup_and_transmit(func_id, buffer, RXTX_BUFFER_SIZE, args);
|
||||
ret = ffa_setup_and_transmit(func_id, buffer, rxtx_bufsz, args);
|
||||
|
||||
if (args->use_txbuf)
|
||||
mutex_unlock(&drv_info->tx_lock);
|
||||
else
|
||||
free_pages_exact(buffer, RXTX_BUFFER_SIZE);
|
||||
free_pages_exact(buffer, rxtx_bufsz);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
@ -597,32 +724,6 @@ static int ffa_memory_reclaim(u64 g_handle, u32 flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffa_features(u32 func_feat_id, u32 input_props,
|
||||
u32 *if_props_1, u32 *if_props_2)
|
||||
{
|
||||
ffa_value_t id;
|
||||
|
||||
if (!ARM_SMCCC_IS_FAST_CALL(func_feat_id) && input_props) {
|
||||
pr_err("%s: Invalid Parameters: %x, %x", __func__,
|
||||
func_feat_id, input_props);
|
||||
return ffa_to_linux_errno(FFA_RET_INVALID_PARAMETERS);
|
||||
}
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_FEATURES, .a1 = func_feat_id, .a2 = input_props,
|
||||
}, &id);
|
||||
|
||||
if (id.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)id.a2);
|
||||
|
||||
if (if_props_1)
|
||||
*if_props_1 = id.a2;
|
||||
if (if_props_2)
|
||||
*if_props_2 = id.a3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffa_notification_bitmap_create(void)
|
||||
{
|
||||
ffa_value_t ret;
|
||||
@ -858,11 +959,15 @@ static int ffa_run(struct ffa_device *dev, u16 vcpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ffa_set_up_mem_ops_native_flag(void)
|
||||
static void ffa_drvinfo_flags_init(void)
|
||||
{
|
||||
if (!ffa_features(FFA_FN_NATIVE(MEM_LEND), 0, NULL, NULL) ||
|
||||
!ffa_features(FFA_FN_NATIVE(MEM_SHARE), 0, NULL, NULL))
|
||||
drv_info->mem_ops_native = true;
|
||||
|
||||
if (!ffa_features(FFA_MSG_SEND_DIRECT_REQ2, 0, NULL, NULL) ||
|
||||
!ffa_features(FFA_MSG_SEND_DIRECT_RESP2, 0, NULL, NULL))
|
||||
drv_info->msg_direct_req2_supp = true;
|
||||
}
|
||||
|
||||
static u32 ffa_api_version_get(void)
|
||||
@ -908,6 +1013,16 @@ 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_sync_send_receive2(struct ffa_device *dev, const uuid_t *uuid,
|
||||
struct ffa_send_direct_data2 *data)
|
||||
{
|
||||
if (!drv_info->msg_direct_req2_supp)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ffa_msg_send_direct_req2(drv_info->vm_id, dev->vm_id,
|
||||
uuid, data);
|
||||
}
|
||||
|
||||
static int ffa_memory_share(struct ffa_mem_ops_args *args)
|
||||
{
|
||||
if (drv_info->mem_ops_native)
|
||||
@ -1191,6 +1306,7 @@ 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,
|
||||
.sync_send_receive2 = ffa_sync_send_receive2,
|
||||
};
|
||||
|
||||
static const struct ffa_mem_ops ffa_drv_mem_ops = {
|
||||
@ -1242,7 +1358,7 @@ ffa_bus_notifier(struct notifier_block *nb, unsigned long action, void *data)
|
||||
|
||||
if (action == BUS_NOTIFY_BIND_DRIVER) {
|
||||
struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
|
||||
const struct ffa_device_id *id_table= ffa_drv->id_table;
|
||||
const struct ffa_device_id *id_table = ffa_drv->id_table;
|
||||
|
||||
/*
|
||||
* FF-A v1.1 provides UUID for each partition as part of the
|
||||
@ -1327,8 +1443,6 @@ static int ffa_setup_partitions(void)
|
||||
/* Allocate for the host */
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
pr_err("%s: failed to alloc Host partition ID 0x%x. Abort.\n",
|
||||
__func__, drv_info->vm_id);
|
||||
/* Already registered devices are freed on bus_exit */
|
||||
ffa_partitions_cleanup();
|
||||
return -ENOMEM;
|
||||
@ -1603,15 +1717,16 @@ static void ffa_notifications_setup(void)
|
||||
static int __init ffa_init(void)
|
||||
{
|
||||
int ret;
|
||||
u32 buf_sz;
|
||||
size_t rxtx_bufsz = SZ_4K;
|
||||
|
||||
ret = ffa_transport_init(&invoke_ffa_fn);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drv_info = kzalloc(sizeof(*drv_info), GFP_KERNEL);
|
||||
if (!drv_info) {
|
||||
if (!drv_info)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = ffa_version_check(&drv_info->version);
|
||||
if (ret)
|
||||
@ -1623,13 +1738,24 @@ static int __init ffa_init(void)
|
||||
goto free_drv_info;
|
||||
}
|
||||
|
||||
drv_info->rx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL);
|
||||
ret = ffa_features(FFA_FN_NATIVE(RXTX_MAP), 0, &buf_sz, NULL);
|
||||
if (!ret) {
|
||||
if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 1)
|
||||
rxtx_bufsz = SZ_64K;
|
||||
else if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 2)
|
||||
rxtx_bufsz = SZ_16K;
|
||||
else
|
||||
rxtx_bufsz = SZ_4K;
|
||||
}
|
||||
|
||||
drv_info->rxtx_bufsz = rxtx_bufsz;
|
||||
drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
|
||||
if (!drv_info->rx_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto free_pages;
|
||||
}
|
||||
|
||||
drv_info->tx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL);
|
||||
drv_info->tx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
|
||||
if (!drv_info->tx_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto free_pages;
|
||||
@ -1637,7 +1763,7 @@ static int __init ffa_init(void)
|
||||
|
||||
ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
|
||||
virt_to_phys(drv_info->rx_buffer),
|
||||
RXTX_BUFFER_SIZE / FFA_PAGE_SIZE);
|
||||
rxtx_bufsz / FFA_PAGE_SIZE);
|
||||
if (ret) {
|
||||
pr_err("failed to register FFA RxTx buffers\n");
|
||||
goto free_pages;
|
||||
@ -1646,7 +1772,7 @@ static int __init ffa_init(void)
|
||||
mutex_init(&drv_info->rx_lock);
|
||||
mutex_init(&drv_info->tx_lock);
|
||||
|
||||
ffa_set_up_mem_ops_native_flag();
|
||||
ffa_drvinfo_flags_init();
|
||||
|
||||
ffa_notifications_setup();
|
||||
|
||||
@ -1667,8 +1793,8 @@ static int __init ffa_init(void)
|
||||
ffa_notifications_cleanup();
|
||||
free_pages:
|
||||
if (drv_info->tx_buffer)
|
||||
free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);
|
||||
free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE);
|
||||
free_pages_exact(drv_info->tx_buffer, rxtx_bufsz);
|
||||
free_pages_exact(drv_info->rx_buffer, rxtx_bufsz);
|
||||
free_drv_info:
|
||||
kfree(drv_info);
|
||||
return ret;
|
||||
@ -1680,8 +1806,8 @@ static void __exit ffa_exit(void)
|
||||
ffa_notifications_cleanup();
|
||||
ffa_partitions_cleanup();
|
||||
ffa_rxtx_unmap(drv_info->vm_id);
|
||||
free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);
|
||||
free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE);
|
||||
free_pages_exact(drv_info->tx_buffer, drv_info->rxtx_bufsz);
|
||||
free_pages_exact(drv_info->rx_buffer, drv_info->rxtx_bufsz);
|
||||
kfree(drv_info);
|
||||
}
|
||||
module_exit(ffa_exit);
|
||||
|
@ -55,116 +55,22 @@ config ARM_SCMI_RAW_MODE_SUPPORT_COEX
|
||||
operate normally, thing which could make an SCMI test suite using the
|
||||
SCMI Raw mode support unreliable. If unsure, say N.
|
||||
|
||||
config ARM_SCMI_HAVE_TRANSPORT
|
||||
bool
|
||||
config ARM_SCMI_DEBUG_COUNTERS
|
||||
bool "Enable SCMI communication debug metrics tracking"
|
||||
select ARM_SCMI_NEED_DEBUGFS
|
||||
depends on DEBUG_FS
|
||||
default n
|
||||
help
|
||||
This declares whether at least one SCMI transport has been configured.
|
||||
Used to trigger a build bug when trying to build SCMI without any
|
||||
configured transport.
|
||||
Enables tracking of some key communication metrics for debug
|
||||
purposes. It may track metrics like how many messages were sent
|
||||
or received, were there any failures, what kind of failures, ..etc.
|
||||
|
||||
config ARM_SCMI_HAVE_SHMEM
|
||||
bool
|
||||
help
|
||||
This declares whether a shared memory based transport for SCMI is
|
||||
available.
|
||||
Enable this option to create a new debugfs directory which contains
|
||||
such useful debug counters. This can be helpful for debugging and
|
||||
SCMI monitoring.
|
||||
|
||||
config ARM_SCMI_HAVE_MSG
|
||||
bool
|
||||
help
|
||||
This declares whether a message passing based transport for SCMI is
|
||||
available.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_MAILBOX
|
||||
bool "SCMI transport based on Mailbox"
|
||||
depends on MAILBOX
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_SHMEM
|
||||
default y
|
||||
help
|
||||
Enable mailbox based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on mailboxes, answer Y.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_OPTEE
|
||||
bool "SCMI transport based on OP-TEE service"
|
||||
depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_SHMEM
|
||||
select ARM_SCMI_HAVE_MSG
|
||||
default y
|
||||
help
|
||||
This enables the OP-TEE service based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on OP-TEE SCMI service, answer Y.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_SMC
|
||||
bool "SCMI transport based on SMC"
|
||||
depends on HAVE_ARM_SMCCC_DISCOVERY
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_SHMEM
|
||||
default y
|
||||
help
|
||||
Enable SMC based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on SMC, answer Y.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE
|
||||
bool "Enable atomic mode support for SCMI SMC transport"
|
||||
depends on ARM_SCMI_TRANSPORT_SMC
|
||||
help
|
||||
Enable support of atomic operation for SCMI SMC based transport.
|
||||
|
||||
If you want the SCMI SMC based transport to operate in atomic
|
||||
mode, avoiding any kind of sleeping behaviour for selected
|
||||
transactions on the TX path, answer Y.
|
||||
Enabling atomic mode operations allows any SCMI driver using this
|
||||
transport to optionally ask for atomic SCMI transactions and operate
|
||||
in atomic context too, at the price of using a number of busy-waiting
|
||||
primitives all over instead. If unsure say N.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_VIRTIO
|
||||
bool "SCMI transport based on VirtIO"
|
||||
depends on VIRTIO=y || VIRTIO=ARM_SCMI_PROTOCOL
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_MSG
|
||||
help
|
||||
This enables the virtio based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on VirtIO, answer Y.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_VIRTIO_VERSION1_COMPLIANCE
|
||||
bool "SCMI VirtIO transport Version 1 compliance"
|
||||
depends on ARM_SCMI_TRANSPORT_VIRTIO
|
||||
default y
|
||||
help
|
||||
This enforces strict compliance with VirtIO Version 1 specification.
|
||||
|
||||
If you want the ARM SCMI VirtIO transport layer to refuse to work
|
||||
with Legacy VirtIO backends and instead support only VirtIO Version 1
|
||||
devices (or above), answer Y.
|
||||
|
||||
If you want instead to support also old Legacy VirtIO backends (like
|
||||
the ones implemented by kvmtool) and let the core Kernel VirtIO layer
|
||||
take care of the needed conversions, say N.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_VIRTIO_ATOMIC_ENABLE
|
||||
bool "Enable atomic mode for SCMI VirtIO transport"
|
||||
depends on ARM_SCMI_TRANSPORT_VIRTIO
|
||||
help
|
||||
Enable support of atomic operation for SCMI VirtIO based transport.
|
||||
|
||||
If you want the SCMI VirtIO based transport to operate in atomic
|
||||
mode, avoiding any kind of sleeping behaviour for selected
|
||||
transactions on the TX path, answer Y.
|
||||
|
||||
Enabling atomic mode operations allows any SCMI driver using this
|
||||
transport to optionally ask for atomic SCMI transactions and operate
|
||||
in atomic context too, at the price of using a number of busy-waiting
|
||||
primitives all over instead. If unsure say N.
|
||||
source "drivers/firmware/arm_scmi/transports/Kconfig"
|
||||
source "drivers/firmware/arm_scmi/vendors/imx/Kconfig"
|
||||
|
||||
endif #ARM_SCMI_PROTOCOL
|
||||
|
||||
|
@ -5,23 +5,15 @@ scmi-core-objs := $(scmi-bus-y)
|
||||
scmi-driver-y = driver.o notify.o
|
||||
scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += mailbox.o
|
||||
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 += pinctrl.o
|
||||
scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += transports/
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += vendors/imx/
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o
|
||||
|
||||
ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy)
|
||||
# The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame
|
||||
# pointer in Thumb2 mode, which is forcibly enabled by Clang when profiling
|
||||
# hooks are inserted via the -pg switch.
|
||||
CFLAGS_REMOVE_smc.o += $(CC_FLAGS_FTRACE)
|
||||
endif
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "notify.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001
|
||||
|
||||
#define SCMI_BASE_NUM_SOURCES 1
|
||||
#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
|
||||
@ -42,7 +42,6 @@ struct scmi_msg_resp_base_discover_agent {
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
};
|
||||
|
||||
|
||||
struct scmi_msg_base_error_notify {
|
||||
__le32 event_control;
|
||||
#define BASE_TP_NOTIFY_ALL BIT(0)
|
||||
@ -105,7 +104,6 @@ scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor)
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_revision_info *rev = ph->get_priv(ph);
|
||||
|
||||
|
||||
if (sub_vendor) {
|
||||
cmd = BASE_DISCOVER_SUB_VENDOR;
|
||||
vendor_id = rev->sub_vendor_id;
|
||||
@ -386,7 +384,7 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rev->major_ver = PROTOCOL_REV_MAJOR(version),
|
||||
rev->major_ver = PROTOCOL_REV_MAJOR(version);
|
||||
rev->minor_ver = PROTOCOL_REV_MINOR(version);
|
||||
ph->set_priv(ph, rev, version);
|
||||
|
||||
|
@ -365,6 +365,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
u32 latency = 0;
|
||||
|
||||
attributes = le32_to_cpu(attr->attributes);
|
||||
strscpy(clk->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
|
||||
/* clock_enable_latency field is present only since SCMI v3.1 */
|
||||
|
@ -4,7 +4,7 @@
|
||||
* driver common header file containing some definitions, structures
|
||||
* and function prototypes used in all the different SCMI protocols.
|
||||
*
|
||||
* Copyright (C) 2018-2022 ARM Ltd.
|
||||
* Copyright (C) 2018-2024 ARM Ltd.
|
||||
*/
|
||||
#ifndef _SCMI_COMMON_H
|
||||
#define _SCMI_COMMON_H
|
||||
@ -183,7 +183,6 @@ struct scmi_chan_info {
|
||||
/**
|
||||
* struct scmi_transport_ops - Structure representing a SCMI transport ops
|
||||
*
|
||||
* @link_supplier: Optional callback to add link to a supplier device
|
||||
* @chan_available: Callback to check if channel is available or not
|
||||
* @chan_setup: Callback to allocate and setup a channel
|
||||
* @chan_free: Callback to free a channel
|
||||
@ -198,7 +197,6 @@ struct scmi_chan_info {
|
||||
* @poll_done: Callback to poll transfer status
|
||||
*/
|
||||
struct scmi_transport_ops {
|
||||
int (*link_supplier)(struct device *dev);
|
||||
bool (*chan_available)(struct device_node *of_node, int idx);
|
||||
int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
bool tx);
|
||||
@ -219,12 +217,6 @@ struct scmi_transport_ops {
|
||||
/**
|
||||
* struct scmi_desc - Description of SoC integration
|
||||
*
|
||||
* @transport_init: An optional function that a transport can provide to
|
||||
* initialize some transport-specific setup during SCMI core
|
||||
* initialization, so ahead of SCMI core probing.
|
||||
* @transport_exit: An optional function that a transport can provide to
|
||||
* de-initialize some transport-specific setup during SCMI core
|
||||
* de-initialization, so after SCMI core removal.
|
||||
* @ops: Pointer to the transport specific ops structure
|
||||
* @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
|
||||
* @max_msg: Maximum number of messages for a channel type (tx or rx) that can
|
||||
@ -245,8 +237,6 @@ struct scmi_transport_ops {
|
||||
* when requested.
|
||||
*/
|
||||
struct scmi_desc {
|
||||
int (*transport_init)(void);
|
||||
void (*transport_exit)(void);
|
||||
const struct scmi_transport_ops *ops;
|
||||
int max_rx_timeout_ms;
|
||||
int max_msg;
|
||||
@ -286,20 +276,30 @@ int scmi_xfer_raw_inflight_register(const struct scmi_handle *handle,
|
||||
int scmi_xfer_raw_wait_for_message_response(struct scmi_chan_info *cinfo,
|
||||
struct scmi_xfer *xfer,
|
||||
unsigned int timeout_ms);
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
|
||||
extern const struct scmi_desc scmi_mailbox_desc;
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
|
||||
extern const struct scmi_desc scmi_smc_desc;
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
|
||||
extern const struct scmi_desc scmi_virtio_desc;
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE
|
||||
extern const struct scmi_desc scmi_optee_desc;
|
||||
#endif
|
||||
|
||||
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);
|
||||
enum debug_counters {
|
||||
SENT_OK,
|
||||
SENT_FAIL,
|
||||
SENT_FAIL_POLLING_UNSUPPORTED,
|
||||
SENT_FAIL_CHANNEL_NOT_FOUND,
|
||||
RESPONSE_OK,
|
||||
NOTIFICATION_OK,
|
||||
DELAYED_RESPONSE_OK,
|
||||
XFERS_RESPONSE_TIMEOUT,
|
||||
XFERS_RESPONSE_POLLED_TIMEOUT,
|
||||
RESPONSE_POLLED_OK,
|
||||
ERR_MSG_UNEXPECTED,
|
||||
ERR_MSG_INVALID,
|
||||
ERR_MSG_NOMEM,
|
||||
ERR_PROTOCOL,
|
||||
SCMI_DEBUG_COUNTERS_LAST
|
||||
};
|
||||
|
||||
static inline void scmi_inc_count(atomic_t *arr, int stat)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS))
|
||||
atomic_inc(&arr[stat]);
|
||||
}
|
||||
|
||||
enum scmi_bad_msg {
|
||||
MSG_UNEXPECTED = -1,
|
||||
@ -309,24 +309,44 @@ enum scmi_bad_msg {
|
||||
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;
|
||||
|
||||
void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer, struct scmi_chan_info *cinfo);
|
||||
u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
|
||||
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
|
||||
/**
|
||||
* struct scmi_shared_mem_operations - Transport core operations for
|
||||
* Shared Memory
|
||||
*
|
||||
* @tx_prepare: Prepare the @xfer message for transmission on the chosen @shmem
|
||||
* @read_header: Read header of the message currently hold in @shmem
|
||||
* @fetch_response: Copy the message response from @shmem into @xfer
|
||||
* @fetch_notification: Copy the message notification from @shmem into @xfer
|
||||
* @clear_channel: Clear the @shmem channel busy flag
|
||||
* @poll_done: Check if poll has completed for @xfer on @shmem
|
||||
* @channel_free: Check if @shmem channel is marked as free
|
||||
* @channel_intr_enabled: Check is @shmem channel has requested a completion irq
|
||||
* @setup_iomap: Setup IO shared memory for channel @cinfo
|
||||
*/
|
||||
struct scmi_shared_mem_operations {
|
||||
void (*tx_prepare)(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer,
|
||||
struct scmi_chan_info *cinfo);
|
||||
u32 (*read_header)(struct scmi_shared_mem __iomem *shmem);
|
||||
|
||||
void (*fetch_response)(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer);
|
||||
void (*fetch_notification)(struct scmi_shared_mem __iomem *shmem,
|
||||
size_t max_len, struct scmi_xfer *xfer);
|
||||
void (*clear_channel)(struct scmi_shared_mem __iomem *shmem);
|
||||
bool (*poll_done)(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer);
|
||||
void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
|
||||
size_t max_len, struct scmi_xfer *xfer);
|
||||
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
|
||||
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer);
|
||||
bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem);
|
||||
bool shmem_channel_intr_enabled(struct scmi_shared_mem __iomem *shmem);
|
||||
bool (*channel_free)(struct scmi_shared_mem __iomem *shmem);
|
||||
bool (*channel_intr_enabled)(struct scmi_shared_mem __iomem *shmem);
|
||||
void __iomem *(*setup_iomap)(struct scmi_chan_info *cinfo,
|
||||
struct device *dev,
|
||||
bool tx, struct resource *res);
|
||||
};
|
||||
|
||||
const struct scmi_shared_mem_operations *scmi_shared_mem_operations_get(void);
|
||||
|
||||
/* declarations for message passing transports */
|
||||
struct scmi_msg_payld;
|
||||
@ -334,14 +354,108 @@ struct scmi_msg_payld;
|
||||
/* Maximum overhead of message w.r.t. struct scmi_desc.max_msg_size */
|
||||
#define SCMI_MSG_MAX_PROT_OVERHEAD (2 * sizeof(__le32))
|
||||
|
||||
size_t msg_response_size(struct scmi_xfer *xfer);
|
||||
size_t msg_command_size(struct scmi_xfer *xfer);
|
||||
void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer);
|
||||
u32 msg_read_header(struct scmi_msg_payld *msg);
|
||||
void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
|
||||
struct scmi_xfer *xfer);
|
||||
void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
|
||||
size_t max_len, struct scmi_xfer *xfer);
|
||||
/**
|
||||
* struct scmi_message_operations - Transport core operations for Message
|
||||
*
|
||||
* @response_size: Get calculated response size for @xfer
|
||||
* @command_size: Get calculated command size for @xfer
|
||||
* @tx_prepare: Prepare the @xfer message for transmission on the provided @msg
|
||||
* @read_header: Read header of the message currently hold in @msg
|
||||
* @fetch_response: Copy the message response from @msg into @xfer
|
||||
* @fetch_notification: Copy the message notification from @msg into @xfer
|
||||
*/
|
||||
struct scmi_message_operations {
|
||||
size_t (*response_size)(struct scmi_xfer *xfer);
|
||||
size_t (*command_size)(struct scmi_xfer *xfer);
|
||||
void (*tx_prepare)(struct scmi_msg_payld *msg, struct scmi_xfer *xfer);
|
||||
u32 (*read_header)(struct scmi_msg_payld *msg);
|
||||
void (*fetch_response)(struct scmi_msg_payld *msg, size_t len,
|
||||
struct scmi_xfer *xfer);
|
||||
void (*fetch_notification)(struct scmi_msg_payld *msg, size_t len,
|
||||
size_t max_len, struct scmi_xfer *xfer);
|
||||
};
|
||||
|
||||
const struct scmi_message_operations *scmi_message_operations_get(void);
|
||||
|
||||
/**
|
||||
* struct scmi_transport_core_operations - Transpoert core operations
|
||||
*
|
||||
* @bad_message_trace: An helper to report a malformed/unexpected message
|
||||
* @rx_callback: Callback to report received messages
|
||||
* @shmem: Datagram operations for shared memory based transports
|
||||
* @msg: Datagram operations for message based transports
|
||||
*/
|
||||
struct scmi_transport_core_operations {
|
||||
void (*bad_message_trace)(struct scmi_chan_info *cinfo,
|
||||
u32 msg_hdr, enum scmi_bad_msg err);
|
||||
void (*rx_callback)(struct scmi_chan_info *cinfo, u32 msg_hdr,
|
||||
void *priv);
|
||||
const struct scmi_shared_mem_operations *shmem;
|
||||
const struct scmi_message_operations *msg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_transport - A structure representing a configured transport
|
||||
*
|
||||
* @supplier: Device representing the transport and acting as a supplier for
|
||||
* the core SCMI stack
|
||||
* @desc: Transport descriptor
|
||||
* @core_ops: A pointer to a pointer used by the core SCMI stack to make the
|
||||
* core transport operations accessible to the transports.
|
||||
*/
|
||||
struct scmi_transport {
|
||||
struct device *supplier;
|
||||
struct scmi_desc *desc;
|
||||
struct scmi_transport_core_operations **core_ops;
|
||||
};
|
||||
|
||||
#define DEFINE_SCMI_TRANSPORT_DRIVER(__tag, __drv, __desc, __match, __core_ops)\
|
||||
static void __tag##_dev_free(void *data) \
|
||||
{ \
|
||||
struct platform_device *spdev = data; \
|
||||
\
|
||||
platform_device_unregister(spdev); \
|
||||
} \
|
||||
\
|
||||
static int __tag##_probe(struct platform_device *pdev) \
|
||||
{ \
|
||||
struct device *dev = &pdev->dev; \
|
||||
struct platform_device *spdev; \
|
||||
struct scmi_transport strans; \
|
||||
int ret; \
|
||||
\
|
||||
spdev = platform_device_alloc("arm-scmi", PLATFORM_DEVID_AUTO); \
|
||||
if (!spdev) \
|
||||
return -ENOMEM; \
|
||||
\
|
||||
device_set_of_node_from_dev(&spdev->dev, dev); \
|
||||
\
|
||||
strans.supplier = dev; \
|
||||
strans.desc = &(__desc); \
|
||||
strans.core_ops = &(__core_ops); \
|
||||
\
|
||||
ret = platform_device_add_data(spdev, &strans, sizeof(strans)); \
|
||||
if (ret) \
|
||||
goto err; \
|
||||
\
|
||||
ret = platform_device_add(spdev); \
|
||||
if (ret) \
|
||||
goto err; \
|
||||
\
|
||||
return devm_add_action_or_reset(dev, __tag##_dev_free, spdev); \
|
||||
\
|
||||
err: \
|
||||
platform_device_put(spdev); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct platform_driver __drv = { \
|
||||
.driver = { \
|
||||
.name = #__tag "_transport", \
|
||||
.of_match_table = __match, \
|
||||
}, \
|
||||
.probe = __tag##_probe, \
|
||||
}
|
||||
|
||||
void scmi_notification_instance_data_set(const struct scmi_handle *handle,
|
||||
void *priv);
|
||||
|
@ -11,7 +11,7 @@
|
||||
* various power domain DVFS including the core/cluster, certain system
|
||||
* clocks configuration, thermal sensors and many others.
|
||||
*
|
||||
* Copyright (C) 2018-2021 ARM Ltd.
|
||||
* Copyright (C) 2018-2024 ARM Ltd.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@ -117,12 +117,14 @@ struct scmi_protocol_instance {
|
||||
* @name: Name of this SCMI instance
|
||||
* @type: Type of this SCMI instance
|
||||
* @is_atomic: Flag to state if the transport of this instance is atomic
|
||||
* @counters: An array of atomic_c's used for tracking statistics (if enabled)
|
||||
*/
|
||||
struct scmi_debug_info {
|
||||
struct dentry *top_dentry;
|
||||
const char *name;
|
||||
const char *type;
|
||||
bool is_atomic;
|
||||
atomic_t counters[SCMI_DEBUG_COUNTERS_LAST];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -194,6 +196,16 @@ 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 void scmi_rx_callback(struct scmi_chan_info *cinfo,
|
||||
u32 msg_hdr, void *priv);
|
||||
static void scmi_bad_message_trace(struct scmi_chan_info *cinfo,
|
||||
u32 msg_hdr, enum scmi_bad_msg err);
|
||||
|
||||
static struct scmi_transport_core_operations scmi_trans_core_ops = {
|
||||
.bad_message_trace = scmi_bad_message_trace,
|
||||
.rx_callback = scmi_rx_callback,
|
||||
};
|
||||
|
||||
static unsigned long
|
||||
scmi_vendor_protocol_signature(unsigned int protocol_id, char *vendor_id,
|
||||
char *sub_vendor_id, u32 impl_ver)
|
||||
@ -833,8 +845,8 @@ scmi_xfer_lookup_unlocked(struct scmi_xfers_info *minfo, u16 xfer_id)
|
||||
* 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)
|
||||
static 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);
|
||||
@ -988,6 +1000,7 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
|
||||
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
|
||||
|
||||
scmi_bad_message_trace(cinfo, msg_hdr, MSG_UNEXPECTED);
|
||||
scmi_inc_count(info->dbg->counters, ERR_MSG_UNEXPECTED);
|
||||
|
||||
return xfer;
|
||||
}
|
||||
@ -1015,6 +1028,7 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
|
||||
msg_type, xfer_id, msg_hdr, xfer->state);
|
||||
|
||||
scmi_bad_message_trace(cinfo, msg_hdr, MSG_INVALID);
|
||||
scmi_inc_count(info->dbg->counters, ERR_MSG_INVALID);
|
||||
|
||||
/* On error the refcount incremented above has to be dropped */
|
||||
__scmi_xfer_put(minfo, xfer);
|
||||
@ -1054,6 +1068,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
|
||||
PTR_ERR(xfer));
|
||||
|
||||
scmi_bad_message_trace(cinfo, msg_hdr, MSG_NOMEM);
|
||||
scmi_inc_count(info->dbg->counters, ERR_MSG_NOMEM);
|
||||
|
||||
scmi_clear_channel(info, cinfo);
|
||||
return;
|
||||
@ -1069,6 +1084,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
|
||||
trace_scmi_msg_dump(info->id, cinfo->id, xfer->hdr.protocol_id,
|
||||
xfer->hdr.id, "NOTI", xfer->hdr.seq,
|
||||
xfer->hdr.status, xfer->rx.buf, xfer->rx.len);
|
||||
scmi_inc_count(info->dbg->counters, NOTIFICATION_OK);
|
||||
|
||||
scmi_notify(cinfo->handle, xfer->hdr.protocol_id,
|
||||
xfer->hdr.id, xfer->rx.buf, xfer->rx.len, ts);
|
||||
@ -1128,8 +1144,10 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
|
||||
if (xfer->hdr.type == MSG_TYPE_DELAYED_RESP) {
|
||||
scmi_clear_channel(info, cinfo);
|
||||
complete(xfer->async_done);
|
||||
scmi_inc_count(info->dbg->counters, DELAYED_RESPONSE_OK);
|
||||
} else {
|
||||
complete(&xfer->done);
|
||||
scmi_inc_count(info->dbg->counters, RESPONSE_OK);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
|
||||
@ -1160,7 +1178,8 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
|
||||
* NOTE: This function will be invoked in IRQ context, hence should be
|
||||
* as optimal as possible.
|
||||
*/
|
||||
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv)
|
||||
static void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr,
|
||||
void *priv)
|
||||
{
|
||||
u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
|
||||
|
||||
@ -1213,6 +1232,7 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
|
||||
struct scmi_xfer *xfer, unsigned int timeout_ms)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
|
||||
|
||||
if (xfer->hdr.poll_completion) {
|
||||
/*
|
||||
@ -1233,13 +1253,12 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
|
||||
"timed out in resp(caller: %pS) - polling\n",
|
||||
(void *)_RET_IP_);
|
||||
ret = -ETIMEDOUT;
|
||||
scmi_inc_count(info->dbg->counters, XFERS_RESPONSE_POLLED_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
unsigned long flags;
|
||||
struct scmi_info *info =
|
||||
handle_to_scmi_info(cinfo->handle);
|
||||
|
||||
/*
|
||||
* Do not fetch_response if an out-of-order delayed
|
||||
@ -1259,11 +1278,9 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
|
||||
"RESP" : "resp",
|
||||
xfer->hdr.seq, xfer->hdr.status,
|
||||
xfer->rx.buf, xfer->rx.len);
|
||||
scmi_inc_count(info->dbg->counters, RESPONSE_POLLED_OK);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
|
||||
struct scmi_info *info =
|
||||
handle_to_scmi_info(cinfo->handle);
|
||||
|
||||
scmi_raw_message_report(info->raw, xfer,
|
||||
SCMI_RAW_REPLY_QUEUE,
|
||||
cinfo->id);
|
||||
@ -1276,6 +1293,7 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
|
||||
dev_err(dev, "timed out in resp(caller: %pS)\n",
|
||||
(void *)_RET_IP_);
|
||||
ret = -ETIMEDOUT;
|
||||
scmi_inc_count(info->dbg->counters, XFERS_RESPONSE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1359,13 +1377,15 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
|
||||
!is_transport_polling_capable(info->desc)) {
|
||||
dev_warn_once(dev,
|
||||
"Polling mode is not supported by transport.\n");
|
||||
scmi_inc_count(info->dbg->counters, SENT_FAIL_POLLING_UNSUPPORTED);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cinfo = idr_find(&info->tx_idr, pi->proto->id);
|
||||
if (unlikely(!cinfo))
|
||||
if (unlikely(!cinfo)) {
|
||||
scmi_inc_count(info->dbg->counters, SENT_FAIL_CHANNEL_NOT_FOUND);
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
/* True ONLY if also supported by transport. */
|
||||
if (is_polling_enabled(cinfo, info->desc))
|
||||
xfer->hdr.poll_completion = true;
|
||||
@ -1397,16 +1417,20 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
|
||||
ret = info->desc->ops->send_message(cinfo, xfer);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "Failed to send message %d\n", ret);
|
||||
scmi_inc_count(info->dbg->counters, SENT_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
trace_scmi_msg_dump(info->id, cinfo->id, xfer->hdr.protocol_id,
|
||||
xfer->hdr.id, "CMND", xfer->hdr.seq,
|
||||
xfer->hdr.status, xfer->tx.buf, xfer->tx.len);
|
||||
scmi_inc_count(info->dbg->counters, SENT_OK);
|
||||
|
||||
ret = scmi_wait_for_message_response(cinfo, xfer);
|
||||
if (!ret && xfer->hdr.status)
|
||||
if (!ret && xfer->hdr.status) {
|
||||
ret = scmi_to_linux_errno(xfer->hdr.status);
|
||||
scmi_inc_count(info->dbg->counters, ERR_PROTOCOL);
|
||||
}
|
||||
|
||||
if (info->desc->ops->mark_txdone)
|
||||
info->desc->ops->mark_txdone(cinfo, ret, xfer);
|
||||
@ -2708,14 +2732,14 @@ scmi_txrx_setup(struct scmi_info *info, struct device_node *of_node,
|
||||
static int scmi_channels_setup(struct scmi_info *info)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *child, *top_np = info->dev->of_node;
|
||||
struct device_node *top_np = info->dev->of_node;
|
||||
|
||||
/* Initialize a common generic channel at first */
|
||||
ret = scmi_txrx_setup(info, top_np, SCMI_PROTOCOL_BASE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_available_child_of_node(top_np, child) {
|
||||
for_each_available_child_of_node_scoped(top_np, child) {
|
||||
u32 prot_id;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &prot_id))
|
||||
@ -2726,10 +2750,8 @@ static int scmi_channels_setup(struct scmi_info *info)
|
||||
"Out of range protocol %d\n", prot_id);
|
||||
|
||||
ret = scmi_txrx_setup(info, child, prot_id);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -2833,6 +2855,56 @@ static int scmi_device_request_notifier(struct notifier_block *nb,
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static const char * const dbg_counter_strs[] = {
|
||||
"sent_ok",
|
||||
"sent_fail",
|
||||
"sent_fail_polling_unsupported",
|
||||
"sent_fail_channel_not_found",
|
||||
"response_ok",
|
||||
"notification_ok",
|
||||
"delayed_response_ok",
|
||||
"xfers_response_timeout",
|
||||
"xfers_response_polled_timeout",
|
||||
"response_polled_ok",
|
||||
"err_msg_unexpected",
|
||||
"err_msg_invalid",
|
||||
"err_msg_nomem",
|
||||
"err_protocol",
|
||||
};
|
||||
|
||||
static ssize_t reset_all_on_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct scmi_debug_info *dbg = filp->private_data;
|
||||
|
||||
for (int i = 0; i < SCMI_DEBUG_COUNTERS_LAST; i++)
|
||||
atomic_set(&dbg->counters[i], 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_reset_counts = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.llseek = no_llseek,
|
||||
.write = reset_all_on_write,
|
||||
};
|
||||
|
||||
static void scmi_debugfs_counters_setup(struct scmi_debug_info *dbg,
|
||||
struct dentry *trans)
|
||||
{
|
||||
struct dentry *counters;
|
||||
int idx;
|
||||
|
||||
counters = debugfs_create_dir("counters", trans);
|
||||
|
||||
for (idx = 0; idx < SCMI_DEBUG_COUNTERS_LAST; idx++)
|
||||
debugfs_create_atomic_t(dbg_counter_strs[idx], 0600, counters,
|
||||
&dbg->counters[idx]);
|
||||
|
||||
debugfs_create_file("reset", 0200, counters, dbg, &fops_reset_counts);
|
||||
}
|
||||
|
||||
static void scmi_debugfs_common_cleanup(void *d)
|
||||
{
|
||||
struct scmi_debug_info *dbg = d;
|
||||
@ -2899,6 +2971,9 @@ static struct scmi_debug_info *scmi_debugfs_common_setup(struct scmi_info *info)
|
||||
debugfs_create_u32("rx_max_msg", 0400, trans,
|
||||
(u32 *)&info->rx_minfo.max_msg);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS))
|
||||
scmi_debugfs_counters_setup(dbg, trans);
|
||||
|
||||
dbg->top_dentry = top_dentry;
|
||||
|
||||
if (devm_add_action_or_reset(info->dev,
|
||||
@ -2950,6 +3025,37 @@ static int scmi_debugfs_raw_mode_setup(struct scmi_info *info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_desc *scmi_transport_setup(struct device *dev)
|
||||
{
|
||||
struct scmi_transport *trans;
|
||||
int ret;
|
||||
|
||||
trans = dev_get_platdata(dev);
|
||||
if (!trans || !trans->desc || !trans->supplier || !trans->core_ops)
|
||||
return NULL;
|
||||
|
||||
if (!device_link_add(dev, trans->supplier, DL_FLAG_AUTOREMOVE_CONSUMER)) {
|
||||
dev_err(dev,
|
||||
"Adding link to supplier transport device failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Provide core transport ops */
|
||||
*trans->core_ops = &scmi_trans_core_ops;
|
||||
|
||||
dev_info(dev, "Using %s\n", dev_driver_string(trans->supplier));
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "max-rx-timeout-ms",
|
||||
&trans->desc->max_rx_timeout_ms);
|
||||
if (ret && ret != -EINVAL)
|
||||
dev_err(dev, "Malformed max-rx-timeout-ms DT property.\n");
|
||||
|
||||
dev_info(dev, "SCMI max-rx-timeout: %dms\n",
|
||||
trans->desc->max_rx_timeout_ms);
|
||||
|
||||
return trans->desc;
|
||||
}
|
||||
|
||||
static int scmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@ -2961,9 +3067,12 @@ static int scmi_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child, *np = dev->of_node;
|
||||
|
||||
desc = of_device_get_match_data(dev);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
desc = scmi_transport_setup(dev);
|
||||
if (!desc) {
|
||||
err_str = "transport invalid\n";
|
||||
ret = -EINVAL;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
@ -3002,14 +3111,6 @@ static int scmi_probe(struct platform_device *pdev)
|
||||
info->atomic_threshold);
|
||||
handle->is_transport_atomic = scmi_is_transport_atomic;
|
||||
|
||||
if (desc->ops->link_supplier) {
|
||||
ret = desc->ops->link_supplier(dev);
|
||||
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) {
|
||||
@ -3130,6 +3231,7 @@ static int scmi_probe(struct platform_device *pdev)
|
||||
clear_ida:
|
||||
ida_free(&scmi_id, info->id);
|
||||
|
||||
out_err:
|
||||
return dev_err_probe(dev, ret, "%s", err_str);
|
||||
}
|
||||
|
||||
@ -3215,86 +3317,16 @@ static struct attribute *versions_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(versions);
|
||||
|
||||
/* Each compatible listed below must have descriptor associated with it */
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
|
||||
{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE
|
||||
{ .compatible = "linaro,scmi-optee", .data = &scmi_optee_desc },
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
|
||||
{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
|
||||
{ .compatible = "arm,scmi-smc-param", .data = &scmi_smc_desc},
|
||||
{ .compatible = "qcom,scmi-smc", .data = &scmi_smc_desc},
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
|
||||
{ .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc},
|
||||
#endif
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, scmi_of_match);
|
||||
|
||||
static struct platform_driver scmi_driver = {
|
||||
.driver = {
|
||||
.name = "arm-scmi",
|
||||
.suppress_bind_attrs = true,
|
||||
.of_match_table = scmi_of_match,
|
||||
.dev_groups = versions_groups,
|
||||
},
|
||||
.probe = scmi_probe,
|
||||
.remove_new = scmi_remove,
|
||||
};
|
||||
|
||||
/**
|
||||
* __scmi_transports_setup - Common helper to call transport-specific
|
||||
* .init/.exit code if provided.
|
||||
*
|
||||
* @init: A flag to distinguish between init and exit.
|
||||
*
|
||||
* Note that, if provided, we invoke .init/.exit functions for all the
|
||||
* transports currently compiled in.
|
||||
*
|
||||
* Return: 0 on Success.
|
||||
*/
|
||||
static inline int __scmi_transports_setup(bool init)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct of_device_id *trans;
|
||||
|
||||
for (trans = scmi_of_match; trans->data; trans++) {
|
||||
const struct scmi_desc *tdesc = trans->data;
|
||||
|
||||
if ((init && !tdesc->transport_init) ||
|
||||
(!init && !tdesc->transport_exit))
|
||||
continue;
|
||||
|
||||
if (init)
|
||||
ret = tdesc->transport_init();
|
||||
else
|
||||
tdesc->transport_exit();
|
||||
|
||||
if (ret) {
|
||||
pr_err("SCMI transport %s FAILED initialization!\n",
|
||||
trans->compatible);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init scmi_transports_init(void)
|
||||
{
|
||||
return __scmi_transports_setup(true);
|
||||
}
|
||||
|
||||
static void __exit scmi_transports_exit(void)
|
||||
{
|
||||
__scmi_transports_setup(false);
|
||||
}
|
||||
|
||||
static struct dentry *scmi_debugfs_init(void)
|
||||
{
|
||||
struct dentry *d;
|
||||
@ -3310,16 +3342,15 @@ static struct dentry *scmi_debugfs_init(void)
|
||||
|
||||
static int __init scmi_driver_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Bail out if no SCMI transport was configured */
|
||||
if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Initialize any compiled-in transport which provided an init/exit */
|
||||
ret = scmi_transports_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_HAVE_SHMEM))
|
||||
scmi_trans_core_ops.shmem = scmi_shared_mem_operations_get();
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_HAVE_MSG))
|
||||
scmi_trans_core_ops.msg = scmi_message_operations_get();
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_NEED_DEBUGFS))
|
||||
scmi_top_dentry = scmi_debugfs_init();
|
||||
@ -3354,8 +3385,6 @@ static void __exit scmi_driver_exit(void)
|
||||
scmi_powercap_unregister();
|
||||
scmi_pinctrl_unregister();
|
||||
|
||||
scmi_transports_exit();
|
||||
|
||||
platform_driver_unregister(&scmi_driver);
|
||||
|
||||
debugfs_remove_recursive(scmi_top_dentry);
|
||||
|
@ -4,7 +4,7 @@
|
||||
*
|
||||
* Derived from shm.c.
|
||||
*
|
||||
* Copyright (C) 2019-2021 ARM Ltd.
|
||||
* Copyright (C) 2019-2024 ARM Ltd.
|
||||
* Copyright (C) 2020-2021 OpenSynergy GmbH
|
||||
*/
|
||||
|
||||
@ -30,7 +30,7 @@ struct scmi_msg_payld {
|
||||
*
|
||||
* Return: transport SDU size.
|
||||
*/
|
||||
size_t msg_command_size(struct scmi_xfer *xfer)
|
||||
static size_t msg_command_size(struct scmi_xfer *xfer)
|
||||
{
|
||||
return sizeof(struct scmi_msg_payld) + xfer->tx.len;
|
||||
}
|
||||
@ -42,7 +42,7 @@ size_t msg_command_size(struct scmi_xfer *xfer)
|
||||
*
|
||||
* Return: transport SDU size.
|
||||
*/
|
||||
size_t msg_response_size(struct scmi_xfer *xfer)
|
||||
static size_t msg_response_size(struct scmi_xfer *xfer)
|
||||
{
|
||||
return sizeof(struct scmi_msg_payld) + sizeof(__le32) + xfer->rx.len;
|
||||
}
|
||||
@ -53,7 +53,7 @@ size_t msg_response_size(struct scmi_xfer *xfer)
|
||||
* @msg: transport SDU for command
|
||||
* @xfer: message which is being sent
|
||||
*/
|
||||
void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer)
|
||||
static void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer)
|
||||
{
|
||||
msg->msg_header = cpu_to_le32(pack_scmi_header(&xfer->hdr));
|
||||
if (xfer->tx.buf)
|
||||
@ -67,7 +67,7 @@ void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer)
|
||||
*
|
||||
* Return: SCMI header
|
||||
*/
|
||||
u32 msg_read_header(struct scmi_msg_payld *msg)
|
||||
static u32 msg_read_header(struct scmi_msg_payld *msg)
|
||||
{
|
||||
return le32_to_cpu(msg->msg_header);
|
||||
}
|
||||
@ -79,8 +79,8 @@ u32 msg_read_header(struct scmi_msg_payld *msg)
|
||||
* @len: transport SDU size
|
||||
* @xfer: message being responded to
|
||||
*/
|
||||
void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
|
||||
struct scmi_xfer *xfer)
|
||||
static void msg_fetch_response(struct scmi_msg_payld *msg,
|
||||
size_t len, struct scmi_xfer *xfer)
|
||||
{
|
||||
size_t prefix_len = sizeof(*msg) + sizeof(msg->msg_payload[0]);
|
||||
|
||||
@ -100,8 +100,8 @@ void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
|
||||
* @max_len: maximum SCMI payload size to fetch
|
||||
* @xfer: notification message
|
||||
*/
|
||||
void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
|
||||
size_t max_len, struct scmi_xfer *xfer)
|
||||
static void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
|
||||
size_t max_len, struct scmi_xfer *xfer)
|
||||
{
|
||||
xfer->rx.len = min_t(size_t, max_len,
|
||||
len >= sizeof(*msg) ? len - sizeof(*msg) : 0);
|
||||
@ -109,3 +109,17 @@ void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
|
||||
/* Take a copy to the rx buffer.. */
|
||||
memcpy(xfer->rx.buf, msg->msg_payload, xfer->rx.len);
|
||||
}
|
||||
|
||||
static const struct scmi_message_operations scmi_msg_ops = {
|
||||
.tx_prepare = msg_tx_prepare,
|
||||
.command_size = msg_command_size,
|
||||
.response_size = msg_response_size,
|
||||
.read_header = msg_read_header,
|
||||
.fetch_response = msg_fetch_response,
|
||||
.fetch_notification = msg_fetch_notification,
|
||||
};
|
||||
|
||||
const struct scmi_message_operations *scmi_message_operations_get(void)
|
||||
{
|
||||
return &scmi_msg_ops;
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
if (!dom_info->mult_factor)
|
||||
dev_warn(ph->dev,
|
||||
"Wrong sustained perf/frequency(domain %d)\n",
|
||||
"Wrong sustained perf/frequency(domain %d)\n",
|
||||
dom_info->id);
|
||||
|
||||
strscpy(dom_info->info.name, attr->name,
|
||||
|
@ -913,4 +913,5 @@ static const struct scmi_protocol scmi_pinctrl = {
|
||||
.ops = &pinctrl_proto_ops,
|
||||
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
|
||||
};
|
||||
|
||||
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(pinctrl, scmi_pinctrl)
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "notify.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30001
|
||||
|
||||
enum scmi_power_protocol_cmd {
|
||||
POWER_DOMAIN_ATTRIBUTES = 0x3,
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "notify.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30001
|
||||
|
||||
enum scmi_reset_protocol_cmd {
|
||||
RESET_DOMAIN_ATTRIBUTES = 0x3,
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "notify.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30001
|
||||
|
||||
#define SCMI_MAX_NUM_SENSOR_AXIS 63
|
||||
#define SCMIv2_SENSOR_PROTOCOL 0x10000
|
||||
|
@ -2,11 +2,13 @@
|
||||
/*
|
||||
* For transport using shared mem structure.
|
||||
*
|
||||
* Copyright (C) 2019 ARM Ltd.
|
||||
* Copyright (C) 2019-2024 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/processor.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
@ -32,8 +34,9 @@ struct scmi_shared_mem {
|
||||
u8 msg_payload[];
|
||||
};
|
||||
|
||||
void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer, struct scmi_chan_info *cinfo)
|
||||
static void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer,
|
||||
struct scmi_chan_info *cinfo)
|
||||
{
|
||||
ktime_t stop;
|
||||
|
||||
@ -73,13 +76,13 @@ void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
|
||||
memcpy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len);
|
||||
}
|
||||
|
||||
u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem)
|
||||
static u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem)
|
||||
{
|
||||
return ioread32(&shmem->msg_header);
|
||||
}
|
||||
|
||||
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer)
|
||||
static void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer)
|
||||
{
|
||||
size_t len = ioread32(&shmem->length);
|
||||
|
||||
@ -91,8 +94,8 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
|
||||
memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len);
|
||||
}
|
||||
|
||||
void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
|
||||
size_t max_len, struct scmi_xfer *xfer)
|
||||
static void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
|
||||
size_t max_len, struct scmi_xfer *xfer)
|
||||
{
|
||||
size_t len = ioread32(&shmem->length);
|
||||
|
||||
@ -103,13 +106,13 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
|
||||
memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len);
|
||||
}
|
||||
|
||||
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem)
|
||||
static void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem)
|
||||
{
|
||||
iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status);
|
||||
}
|
||||
|
||||
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer)
|
||||
static bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer)
|
||||
{
|
||||
u16 xfer_id;
|
||||
|
||||
@ -123,13 +126,69 @@ bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
|
||||
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
|
||||
}
|
||||
|
||||
bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem)
|
||||
static bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem)
|
||||
{
|
||||
return (ioread32(&shmem->channel_status) &
|
||||
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
|
||||
}
|
||||
|
||||
bool shmem_channel_intr_enabled(struct scmi_shared_mem __iomem *shmem)
|
||||
static bool shmem_channel_intr_enabled(struct scmi_shared_mem __iomem *shmem)
|
||||
{
|
||||
return ioread32(&shmem->flags) & SCMI_SHMEM_FLAG_INTR_ENABLED;
|
||||
}
|
||||
|
||||
static void __iomem *shmem_setup_iomap(struct scmi_chan_info *cinfo,
|
||||
struct device *dev, bool tx,
|
||||
struct resource *res)
|
||||
{
|
||||
struct device_node *shmem __free(device_node);
|
||||
const char *desc = tx ? "Tx" : "Rx";
|
||||
int ret, idx = tx ? 0 : 1;
|
||||
struct device *cdev = cinfo->dev;
|
||||
struct resource lres = {};
|
||||
resource_size_t size;
|
||||
void __iomem *addr;
|
||||
|
||||
shmem = of_parse_phandle(cdev->of_node, "shmem", idx);
|
||||
if (!shmem)
|
||||
return IOMEM_ERR_PTR(-ENODEV);
|
||||
|
||||
if (!of_device_is_compatible(shmem, "arm,scmi-shmem"))
|
||||
return IOMEM_ERR_PTR(-ENXIO);
|
||||
|
||||
/* Use a local on-stack as a working area when not provided */
|
||||
if (!res)
|
||||
res = &lres;
|
||||
|
||||
ret = of_address_to_resource(shmem, 0, res);
|
||||
if (ret) {
|
||||
dev_err(cdev, "failed to get SCMI %s shared memory\n", desc);
|
||||
return IOMEM_ERR_PTR(ret);
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
addr = devm_ioremap(dev, res->start, size);
|
||||
if (!addr) {
|
||||
dev_err(dev, "failed to ioremap SCMI %s shared memory\n", desc);
|
||||
return IOMEM_ERR_PTR(-EADDRNOTAVAIL);
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static const struct scmi_shared_mem_operations scmi_shmem_ops = {
|
||||
.tx_prepare = shmem_tx_prepare,
|
||||
.read_header = shmem_read_header,
|
||||
.fetch_response = shmem_fetch_response,
|
||||
.fetch_notification = shmem_fetch_notification,
|
||||
.clear_channel = shmem_clear_channel,
|
||||
.poll_done = shmem_poll_done,
|
||||
.channel_free = shmem_channel_free,
|
||||
.channel_intr_enabled = shmem_channel_intr_enabled,
|
||||
.setup_iomap = shmem_setup_iomap,
|
||||
};
|
||||
|
||||
const struct scmi_shared_mem_operations *scmi_shared_mem_operations_get(void)
|
||||
{
|
||||
return &scmi_shmem_ops;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "notify.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001
|
||||
|
||||
#define SCMI_SYSTEM_NUM_SOURCES 1
|
||||
|
||||
|
123
drivers/firmware/arm_scmi/transports/Kconfig
Normal file
123
drivers/firmware/arm_scmi/transports/Kconfig
Normal file
@ -0,0 +1,123 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menu "SCMI Transport Drivers"
|
||||
|
||||
config ARM_SCMI_HAVE_TRANSPORT
|
||||
bool
|
||||
help
|
||||
This declares whether at least one SCMI transport has been configured.
|
||||
Used to trigger a build bug when trying to build SCMI without any
|
||||
configured transport.
|
||||
|
||||
config ARM_SCMI_HAVE_SHMEM
|
||||
bool
|
||||
help
|
||||
This declares whether a shared memory based transport for SCMI is
|
||||
available.
|
||||
|
||||
config ARM_SCMI_HAVE_MSG
|
||||
bool
|
||||
help
|
||||
This declares whether a message passing based transport for SCMI is
|
||||
available.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_MAILBOX
|
||||
tristate "SCMI transport based on Mailbox"
|
||||
depends on MAILBOX
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_SHMEM
|
||||
default y
|
||||
help
|
||||
Enable mailbox based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on mailboxes, answer Y.
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called scmi_transport_mailbox.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_SMC
|
||||
tristate "SCMI transport based on SMC"
|
||||
depends on HAVE_ARM_SMCCC_DISCOVERY
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_SHMEM
|
||||
default y
|
||||
help
|
||||
Enable SMC based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on SMC, answer Y.
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called scmi_transport_smc.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE
|
||||
bool "Enable atomic mode support for SCMI SMC transport"
|
||||
depends on ARM_SCMI_TRANSPORT_SMC
|
||||
help
|
||||
Enable support of atomic operation for SCMI SMC based transport.
|
||||
|
||||
If you want the SCMI SMC based transport to operate in atomic
|
||||
mode, avoiding any kind of sleeping behaviour for selected
|
||||
transactions on the TX path, answer Y.
|
||||
Enabling atomic mode operations allows any SCMI driver using this
|
||||
transport to optionally ask for atomic SCMI transactions and operate
|
||||
in atomic context too, at the price of using a number of busy-waiting
|
||||
primitives all over instead. If unsure say N.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_OPTEE
|
||||
tristate "SCMI transport based on OP-TEE service"
|
||||
depends on OPTEE
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_SHMEM
|
||||
select ARM_SCMI_HAVE_MSG
|
||||
default y
|
||||
help
|
||||
This enables the OP-TEE service based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on OP-TEE SCMI service, answer Y.
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called scmi_transport_optee.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_VIRTIO
|
||||
tristate "SCMI transport based on VirtIO"
|
||||
depends on VIRTIO
|
||||
select ARM_SCMI_HAVE_TRANSPORT
|
||||
select ARM_SCMI_HAVE_MSG
|
||||
help
|
||||
This enables the virtio based transport for SCMI.
|
||||
|
||||
If you want the ARM SCMI PROTOCOL stack to include support for a
|
||||
transport based on VirtIO, answer Y.
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called scmi_transport_virtio.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_VIRTIO_VERSION1_COMPLIANCE
|
||||
bool "SCMI VirtIO transport Version 1 compliance"
|
||||
depends on ARM_SCMI_TRANSPORT_VIRTIO
|
||||
default y
|
||||
help
|
||||
This enforces strict compliance with VirtIO Version 1 specification.
|
||||
|
||||
If you want the ARM SCMI VirtIO transport layer to refuse to work
|
||||
with Legacy VirtIO backends and instead support only VirtIO Version 1
|
||||
devices (or above), answer Y.
|
||||
|
||||
If you want instead to support also old Legacy VirtIO backends (like
|
||||
the ones implemented by kvmtool) and let the core Kernel VirtIO layer
|
||||
take care of the needed conversions, say N.
|
||||
|
||||
config ARM_SCMI_TRANSPORT_VIRTIO_ATOMIC_ENABLE
|
||||
bool "Enable atomic mode for SCMI VirtIO transport"
|
||||
depends on ARM_SCMI_TRANSPORT_VIRTIO
|
||||
help
|
||||
Enable support of atomic operation for SCMI VirtIO based transport.
|
||||
|
||||
If you want the SCMI VirtIO based transport to operate in atomic
|
||||
mode, avoiding any kind of sleeping behaviour for selected
|
||||
transactions on the TX path, answer Y.
|
||||
|
||||
Enabling atomic mode operations allows any SCMI driver using this
|
||||
transport to optionally ask for atomic SCMI transactions and operate
|
||||
in atomic context too, at the price of using a number of busy-waiting
|
||||
primitives all over instead. If unsure say N.
|
||||
|
||||
endmenu
|
16
drivers/firmware/arm_scmi/transports/Makefile
Normal file
16
drivers/firmware/arm_scmi/transports/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
scmi_transport_mailbox-objs := mailbox.o
|
||||
obj-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += scmi_transport_mailbox.o
|
||||
scmi_transport_smc-objs := smc.o
|
||||
obj-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += scmi_transport_smc.o
|
||||
scmi_transport_optee-objs := optee.o
|
||||
obj-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += scmi_transport_optee.o
|
||||
scmi_transport_virtio-objs := virtio.o
|
||||
obj-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += scmi_transport_virtio.o
|
||||
|
||||
ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy)
|
||||
# The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame
|
||||
# pointer in Thumb2 mode, which is forcibly enabled by Clang when profiling
|
||||
# hooks are inserted via the -pg switch.
|
||||
CFLAGS_REMOVE_smc.o += $(CC_FLAGS_FTRACE)
|
||||
endif
|
@ -3,7 +3,7 @@
|
||||
* System Control and Management Interface (SCMI) Message Mailbox Transport
|
||||
* driver.
|
||||
*
|
||||
* Copyright (C) 2019 ARM Ltd.
|
||||
* Copyright (C) 2019-2024 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
@ -11,9 +11,10 @@
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "../common.h"
|
||||
|
||||
/**
|
||||
* struct scmi_mailbox - Structure representing a SCMI mailbox transport
|
||||
@ -36,11 +37,13 @@ struct scmi_mailbox {
|
||||
|
||||
#define client_to_scmi_mailbox(c) container_of(c, struct scmi_mailbox, cl)
|
||||
|
||||
static struct scmi_transport_core_operations *core;
|
||||
|
||||
static void tx_prepare(struct mbox_client *cl, void *m)
|
||||
{
|
||||
struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);
|
||||
|
||||
shmem_tx_prepare(smbox->shmem, m, smbox->cinfo);
|
||||
core->shmem->tx_prepare(smbox->shmem, m, smbox->cinfo);
|
||||
}
|
||||
|
||||
static void rx_callback(struct mbox_client *cl, void *m)
|
||||
@ -56,15 +59,17 @@ static void rx_callback(struct mbox_client *cl, void *m)
|
||||
* a previous timed-out reply which arrived late could be wrongly
|
||||
* associated with the next pending transaction.
|
||||
*/
|
||||
if (cl->knows_txdone && !shmem_channel_free(smbox->shmem)) {
|
||||
if (cl->knows_txdone &&
|
||||
!core->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);
|
||||
core->bad_message_trace(smbox->cinfo,
|
||||
core->shmem->read_header(smbox->shmem),
|
||||
MSG_MBOX_SPURIOUS);
|
||||
return;
|
||||
}
|
||||
|
||||
scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL);
|
||||
core->rx_callback(smbox->cinfo,
|
||||
core->shmem->read_header(smbox->shmem), NULL);
|
||||
}
|
||||
|
||||
static bool mailbox_chan_available(struct device_node *of_node, int idx)
|
||||
@ -124,18 +129,16 @@ static int mailbox_chan_validate(struct device *cdev, int *a2p_rx_chan,
|
||||
|
||||
/* Bail out if provided shmem descriptors do not refer distinct areas */
|
||||
if (num_sh > 1) {
|
||||
struct device_node *np_tx, *np_rx;
|
||||
struct device_node *np_tx __free(device_node) =
|
||||
of_parse_phandle(np, "shmem", 0);
|
||||
struct device_node *np_rx __free(device_node) =
|
||||
of_parse_phandle(np, "shmem", 1);
|
||||
|
||||
np_tx = of_parse_phandle(np, "shmem", 0);
|
||||
np_rx = of_parse_phandle(np, "shmem", 1);
|
||||
if (!np_tx || !np_rx || np_tx == np_rx) {
|
||||
dev_warn(cdev, "Invalid shmem descriptor for '%s'\n",
|
||||
of_node_full_name(np));
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
of_node_put(np_tx);
|
||||
of_node_put(np_rx);
|
||||
}
|
||||
|
||||
/* Calculate channels IDs to use depending on mboxes/shmem layout */
|
||||
@ -178,11 +181,8 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
const char *desc = tx ? "Tx" : "Rx";
|
||||
struct device *cdev = cinfo->dev;
|
||||
struct scmi_mailbox *smbox;
|
||||
struct device_node *shmem;
|
||||
int ret, a2p_rx_chan, p2a_chan, p2a_rx_chan, idx = tx ? 0 : 1;
|
||||
int ret, a2p_rx_chan, p2a_chan, p2a_rx_chan;
|
||||
struct mbox_client *cl;
|
||||
resource_size_t size;
|
||||
struct resource res;
|
||||
|
||||
ret = mailbox_chan_validate(cdev, &a2p_rx_chan, &p2a_chan, &p2a_rx_chan);
|
||||
if (ret)
|
||||
@ -195,25 +195,9 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
if (!smbox)
|
||||
return -ENOMEM;
|
||||
|
||||
shmem = of_parse_phandle(cdev->of_node, "shmem", idx);
|
||||
if (!of_device_is_compatible(shmem, "arm,scmi-shmem")) {
|
||||
of_node_put(shmem);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(shmem, 0, &res);
|
||||
of_node_put(shmem);
|
||||
if (ret) {
|
||||
dev_err(cdev, "failed to get SCMI %s shared memory\n", desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size = resource_size(&res);
|
||||
smbox->shmem = devm_ioremap(dev, res.start, size);
|
||||
if (!smbox->shmem) {
|
||||
dev_err(dev, "failed to ioremap SCMI %s shared memory\n", desc);
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
smbox->shmem = core->shmem->setup_iomap(cinfo, dev, tx, NULL);
|
||||
if (IS_ERR(smbox->shmem))
|
||||
return PTR_ERR(smbox->shmem);
|
||||
|
||||
cl = &smbox->cl;
|
||||
cl->dev = cdev;
|
||||
@ -252,7 +236,6 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cinfo->transport_info = smbox;
|
||||
smbox->cinfo = cinfo;
|
||||
|
||||
@ -312,7 +295,7 @@ static void mailbox_fetch_response(struct scmi_chan_info *cinfo,
|
||||
{
|
||||
struct scmi_mailbox *smbox = cinfo->transport_info;
|
||||
|
||||
shmem_fetch_response(smbox->shmem, xfer);
|
||||
core->shmem->fetch_response(smbox->shmem, xfer);
|
||||
}
|
||||
|
||||
static void mailbox_fetch_notification(struct scmi_chan_info *cinfo,
|
||||
@ -320,7 +303,7 @@ static void mailbox_fetch_notification(struct scmi_chan_info *cinfo,
|
||||
{
|
||||
struct scmi_mailbox *smbox = cinfo->transport_info;
|
||||
|
||||
shmem_fetch_notification(smbox->shmem, max_len, xfer);
|
||||
core->shmem->fetch_notification(smbox->shmem, max_len, xfer);
|
||||
}
|
||||
|
||||
static void mailbox_clear_channel(struct scmi_chan_info *cinfo)
|
||||
@ -329,9 +312,9 @@ static void mailbox_clear_channel(struct scmi_chan_info *cinfo)
|
||||
struct mbox_chan *intr_chan;
|
||||
int ret;
|
||||
|
||||
shmem_clear_channel(smbox->shmem);
|
||||
core->shmem->clear_channel(smbox->shmem);
|
||||
|
||||
if (!shmem_channel_intr_enabled(smbox->shmem))
|
||||
if (!core->shmem->channel_intr_enabled(smbox->shmem))
|
||||
return;
|
||||
|
||||
if (smbox->chan_platform_receiver)
|
||||
@ -354,7 +337,7 @@ mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
|
||||
{
|
||||
struct scmi_mailbox *smbox = cinfo->transport_info;
|
||||
|
||||
return shmem_poll_done(smbox->shmem, xfer);
|
||||
return core->shmem->poll_done(smbox->shmem, xfer);
|
||||
}
|
||||
|
||||
static const struct scmi_transport_ops scmi_mailbox_ops = {
|
||||
@ -369,9 +352,22 @@ static const struct scmi_transport_ops scmi_mailbox_ops = {
|
||||
.poll_done = mailbox_poll_done,
|
||||
};
|
||||
|
||||
const struct scmi_desc scmi_mailbox_desc = {
|
||||
static struct scmi_desc scmi_mailbox_desc = {
|
||||
.ops = &scmi_mailbox_ops,
|
||||
.max_rx_timeout_ms = 30, /* We may increase this if required */
|
||||
.max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */
|
||||
.max_msg_size = 128,
|
||||
};
|
||||
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "arm,scmi" },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
DEFINE_SCMI_TRANSPORT_DRIVER(scmi_mailbox, scmi_mailbox_driver,
|
||||
scmi_mailbox_desc, scmi_of_match, core);
|
||||
module_platform_driver(scmi_mailbox_driver);
|
||||
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("SCMI Mailbox Transport driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -9,12 +9,13 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tee_drv.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <uapi/linux/tee.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "../common.h"
|
||||
|
||||
#define SCMI_OPTEE_MAX_MSG_SIZE 128
|
||||
|
||||
@ -148,12 +149,11 @@ struct scmi_optee_agent {
|
||||
struct list_head channel_list;
|
||||
};
|
||||
|
||||
static struct scmi_transport_core_operations *core;
|
||||
|
||||
/* There can be only 1 SCMI service in OP-TEE we connect to */
|
||||
static struct scmi_optee_agent *scmi_optee_private;
|
||||
|
||||
/* Forward reference to scmi_optee transport initialization */
|
||||
static int scmi_optee_init(void);
|
||||
|
||||
/* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */
|
||||
static int open_session(struct scmi_optee_agent *agent, u32 *tee_session)
|
||||
{
|
||||
@ -312,24 +312,6 @@ static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_optee_link_supplier(struct device *dev)
|
||||
{
|
||||
if (!scmi_optee_private) {
|
||||
if (scmi_optee_init())
|
||||
dev_dbg(dev, "Optee bus not yet ready\n");
|
||||
|
||||
/* Wait for optee bus */
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
if (!device_link_add(dev, scmi_optee_private->dev, DL_FLAG_AUTOREMOVE_CONSUMER)) {
|
||||
dev_err(dev, "Adding link to supplier optee device failed\n");
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool scmi_optee_chan_available(struct device_node *of_node, int idx)
|
||||
{
|
||||
u32 channel_id;
|
||||
@ -343,7 +325,7 @@ static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo)
|
||||
struct scmi_optee_channel *channel = cinfo->transport_info;
|
||||
|
||||
if (!channel->tee_shm)
|
||||
shmem_clear_channel(channel->req.shmem);
|
||||
core->shmem->clear_channel(channel->req.shmem);
|
||||
}
|
||||
|
||||
static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel)
|
||||
@ -368,38 +350,11 @@ static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *ch
|
||||
static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo,
|
||||
struct scmi_optee_channel *channel)
|
||||
{
|
||||
struct device_node *np;
|
||||
resource_size_t size;
|
||||
struct resource res;
|
||||
int ret;
|
||||
channel->req.shmem = core->shmem->setup_iomap(cinfo, dev, true, NULL);
|
||||
if (IS_ERR(channel->req.shmem))
|
||||
return PTR_ERR(channel->req.shmem);
|
||||
|
||||
np = of_parse_phandle(cinfo->dev->of_node, "shmem", 0);
|
||||
if (!of_device_is_compatible(np, "arm,scmi-shmem")) {
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(np, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get SCMI Tx shared memory\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = resource_size(&res);
|
||||
|
||||
channel->req.shmem = devm_ioremap(dev, res.start, size);
|
||||
if (!channel->req.shmem) {
|
||||
dev_err(dev, "Failed to ioremap SCMI Tx shared memory\n");
|
||||
ret = -EADDRNOTAVAIL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
of_node_put(np);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo,
|
||||
@ -473,6 +428,13 @@ static int scmi_optee_chan_free(int id, void *p, void *data)
|
||||
struct scmi_chan_info *cinfo = p;
|
||||
struct scmi_optee_channel *channel = cinfo->transport_info;
|
||||
|
||||
/*
|
||||
* Different protocols might share the same chan info, so a previous
|
||||
* call might have already freed the structure.
|
||||
*/
|
||||
if (!channel)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&scmi_optee_private->mu);
|
||||
list_del(&channel->link);
|
||||
mutex_unlock(&scmi_optee_private->mu);
|
||||
@ -499,10 +461,11 @@ static int scmi_optee_send_message(struct scmi_chan_info *cinfo,
|
||||
mutex_lock(&channel->mu);
|
||||
|
||||
if (channel->tee_shm) {
|
||||
msg_tx_prepare(channel->req.msg, xfer);
|
||||
ret = invoke_process_msg_channel(channel, msg_command_size(xfer));
|
||||
core->msg->tx_prepare(channel->req.msg, xfer);
|
||||
ret = invoke_process_msg_channel(channel,
|
||||
core->msg->command_size(xfer));
|
||||
} else {
|
||||
shmem_tx_prepare(channel->req.shmem, xfer, cinfo);
|
||||
core->shmem->tx_prepare(channel->req.shmem, xfer, cinfo);
|
||||
ret = invoke_process_smt_channel(channel);
|
||||
}
|
||||
|
||||
@ -518,9 +481,10 @@ static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo,
|
||||
struct scmi_optee_channel *channel = cinfo->transport_info;
|
||||
|
||||
if (channel->tee_shm)
|
||||
msg_fetch_response(channel->req.msg, channel->rx_len, xfer);
|
||||
core->msg->fetch_response(channel->req.msg,
|
||||
channel->rx_len, xfer);
|
||||
else
|
||||
shmem_fetch_response(channel->req.shmem, xfer);
|
||||
core->shmem->fetch_response(channel->req.shmem, xfer);
|
||||
}
|
||||
|
||||
static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret,
|
||||
@ -532,7 +496,6 @@ static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret,
|
||||
}
|
||||
|
||||
static struct scmi_transport_ops scmi_optee_ops = {
|
||||
.link_supplier = scmi_optee_link_supplier,
|
||||
.chan_available = scmi_optee_chan_available,
|
||||
.chan_setup = scmi_optee_chan_setup,
|
||||
.chan_free = scmi_optee_chan_free,
|
||||
@ -547,6 +510,22 @@ static int scmi_optee_ctx_match(struct tee_ioctl_version_data *ver, const void *
|
||||
return ver->impl_id == TEE_IMPL_ID_OPTEE;
|
||||
}
|
||||
|
||||
static struct scmi_desc scmi_optee_desc = {
|
||||
.ops = &scmi_optee_ops,
|
||||
.max_rx_timeout_ms = 30,
|
||||
.max_msg = 20,
|
||||
.max_msg_size = SCMI_OPTEE_MAX_MSG_SIZE,
|
||||
.sync_cmds_completed_on_ret = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "linaro,scmi-optee" },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
DEFINE_SCMI_TRANSPORT_DRIVER(scmi_optee, scmi_optee_driver, scmi_optee_desc,
|
||||
scmi_of_match, core);
|
||||
|
||||
static int scmi_optee_service_probe(struct device *dev)
|
||||
{
|
||||
struct scmi_optee_agent *agent;
|
||||
@ -582,6 +561,12 @@ static int scmi_optee_service_probe(struct device *dev)
|
||||
smp_mb();
|
||||
scmi_optee_private = agent;
|
||||
|
||||
ret = platform_driver_register(&scmi_optee_driver);
|
||||
if (ret) {
|
||||
scmi_optee_private = NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -597,6 +582,8 @@ static int scmi_optee_service_remove(struct device *dev)
|
||||
if (!scmi_optee_private)
|
||||
return -EINVAL;
|
||||
|
||||
platform_driver_unregister(&scmi_optee_driver);
|
||||
|
||||
if (!list_empty(&scmi_optee_private->channel_list))
|
||||
return -EBUSY;
|
||||
|
||||
@ -618,7 +605,7 @@ static const struct tee_client_device_id scmi_optee_service_id[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(tee, scmi_optee_service_id);
|
||||
|
||||
static struct tee_client_driver scmi_optee_driver = {
|
||||
static struct tee_client_driver scmi_optee_service_driver = {
|
||||
.id_table = scmi_optee_service_id,
|
||||
.driver = {
|
||||
.name = "scmi-optee",
|
||||
@ -628,22 +615,18 @@ static struct tee_client_driver scmi_optee_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int scmi_optee_init(void)
|
||||
static int __init scmi_transport_optee_init(void)
|
||||
{
|
||||
return driver_register(&scmi_optee_driver.driver);
|
||||
return driver_register(&scmi_optee_service_driver.driver);
|
||||
}
|
||||
module_init(scmi_transport_optee_init);
|
||||
|
||||
static void scmi_optee_exit(void)
|
||||
static void __exit scmi_transport_optee_exit(void)
|
||||
{
|
||||
if (scmi_optee_private)
|
||||
driver_unregister(&scmi_optee_driver.driver);
|
||||
driver_unregister(&scmi_optee_service_driver.driver);
|
||||
}
|
||||
module_exit(scmi_transport_optee_exit);
|
||||
|
||||
const struct scmi_desc scmi_optee_desc = {
|
||||
.transport_exit = scmi_optee_exit,
|
||||
.ops = &scmi_optee_ops,
|
||||
.max_rx_timeout_ms = 30,
|
||||
.max_msg = 20,
|
||||
.max_msg_size = SCMI_OPTEE_MAX_MSG_SIZE,
|
||||
.sync_cmds_completed_on_ret = true,
|
||||
};
|
||||
MODULE_AUTHOR("Etienne Carriere <etienne.carriere@foss.st.com>");
|
||||
MODULE_DESCRIPTION("SCMI OPTEE Transport driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -16,10 +16,11 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/processor.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "../common.h"
|
||||
|
||||
/*
|
||||
* The shmem address is split into 4K page and offset.
|
||||
@ -69,23 +70,25 @@ struct scmi_smc {
|
||||
unsigned long cap_id;
|
||||
};
|
||||
|
||||
static struct scmi_transport_core_operations *core;
|
||||
|
||||
static irqreturn_t smc_msg_done_isr(int irq, void *data)
|
||||
{
|
||||
struct scmi_smc *scmi_info = data;
|
||||
|
||||
scmi_rx_callback(scmi_info->cinfo,
|
||||
shmem_read_header(scmi_info->shmem), NULL);
|
||||
core->rx_callback(scmi_info->cinfo,
|
||||
core->shmem->read_header(scmi_info->shmem), NULL);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static bool smc_chan_available(struct device_node *of_node, int idx)
|
||||
{
|
||||
struct device_node *np = of_parse_phandle(of_node, "shmem", 0);
|
||||
struct device_node *np __free(device_node) =
|
||||
of_parse_phandle(of_node, "shmem", 0);
|
||||
if (!np)
|
||||
return false;
|
||||
|
||||
of_node_put(np);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -130,9 +133,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
struct device *cdev = cinfo->dev;
|
||||
unsigned long cap_id = ULONG_MAX;
|
||||
struct scmi_smc *scmi_info;
|
||||
resource_size_t size;
|
||||
struct resource res;
|
||||
struct device_node *np;
|
||||
struct resource res = {};
|
||||
u32 func_id;
|
||||
int ret;
|
||||
|
||||
@ -143,31 +144,16 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
if (!scmi_info)
|
||||
return -ENOMEM;
|
||||
|
||||
np = of_parse_phandle(cdev->of_node, "shmem", 0);
|
||||
if (!of_device_is_compatible(np, "arm,scmi-shmem")) {
|
||||
of_node_put(np);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(np, 0, &res);
|
||||
of_node_put(np);
|
||||
if (ret) {
|
||||
dev_err(cdev, "failed to get SCMI Tx shared memory\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
size = resource_size(&res);
|
||||
scmi_info->shmem = devm_ioremap(dev, res.start, size);
|
||||
if (!scmi_info->shmem) {
|
||||
dev_err(dev, "failed to ioremap SCMI Tx shared memory\n");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
scmi_info->shmem = core->shmem->setup_iomap(cinfo, dev, tx, &res);
|
||||
if (IS_ERR(scmi_info->shmem))
|
||||
return PTR_ERR(scmi_info->shmem);
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "arm,smc-id", &func_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,scmi-smc")) {
|
||||
resource_size_t size = resource_size(&res);
|
||||
void __iomem *ptr = (void __iomem *)scmi_info->shmem + size - 8;
|
||||
/* The capability-id is kept in last 8 bytes of shmem.
|
||||
* +-------+ <-- 0
|
||||
@ -243,7 +229,7 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
|
||||
*/
|
||||
smc_channel_lock_acquire(scmi_info, xfer);
|
||||
|
||||
shmem_tx_prepare(scmi_info->shmem, xfer, cinfo);
|
||||
core->shmem->tx_prepare(scmi_info->shmem, xfer, cinfo);
|
||||
|
||||
if (scmi_info->cap_id != ULONG_MAX)
|
||||
arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->cap_id, 0,
|
||||
@ -267,7 +253,7 @@ static void smc_fetch_response(struct scmi_chan_info *cinfo,
|
||||
{
|
||||
struct scmi_smc *scmi_info = cinfo->transport_info;
|
||||
|
||||
shmem_fetch_response(scmi_info->shmem, xfer);
|
||||
core->shmem->fetch_response(scmi_info->shmem, xfer);
|
||||
}
|
||||
|
||||
static void smc_mark_txdone(struct scmi_chan_info *cinfo, int ret,
|
||||
@ -287,7 +273,7 @@ static const struct scmi_transport_ops scmi_smc_ops = {
|
||||
.fetch_response = smc_fetch_response,
|
||||
};
|
||||
|
||||
const struct scmi_desc scmi_smc_desc = {
|
||||
static struct scmi_desc scmi_smc_desc = {
|
||||
.ops = &scmi_smc_ops,
|
||||
.max_rx_timeout_ms = 30,
|
||||
.max_msg = 20,
|
||||
@ -303,3 +289,19 @@ const struct scmi_desc scmi_smc_desc = {
|
||||
.sync_cmds_completed_on_ret = true,
|
||||
.atomic_enabled = IS_ENABLED(CONFIG_ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE),
|
||||
};
|
||||
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "arm,scmi-smc" },
|
||||
{ .compatible = "arm,scmi-smc-param" },
|
||||
{ .compatible = "qcom,scmi-smc" },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
DEFINE_SCMI_TRANSPORT_DRIVER(scmi_smc, scmi_smc_driver, scmi_smc_desc,
|
||||
scmi_of_match, core);
|
||||
module_platform_driver(scmi_smc_driver);
|
||||
|
||||
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
|
||||
MODULE_AUTHOR("Nikunj Kela <quic_nkela@quicinc.com>");
|
||||
MODULE_DESCRIPTION("SCMI SMC Transport driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -4,7 +4,7 @@
|
||||
* (SCMI).
|
||||
*
|
||||
* Copyright (C) 2020-2022 OpenSynergy.
|
||||
* Copyright (C) 2021-2022 ARM Ltd.
|
||||
* Copyright (C) 2021-2024 ARM Ltd.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/virtio.h>
|
||||
@ -27,7 +28,7 @@
|
||||
#include <uapi/linux/virtio_ids.h>
|
||||
#include <uapi/linux/virtio_scmi.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "../common.h"
|
||||
|
||||
#define VIRTIO_MAX_RX_TIMEOUT_MS 60000
|
||||
#define VIRTIO_SCMI_MAX_MSG_SIZE 128 /* Value may be increased. */
|
||||
@ -108,6 +109,8 @@ struct scmi_vio_msg {
|
||||
refcount_t users;
|
||||
};
|
||||
|
||||
static struct scmi_transport_core_operations *core;
|
||||
|
||||
/* Only one SCMI VirtIO device can possibly exist */
|
||||
static struct virtio_device *scmi_vdev;
|
||||
|
||||
@ -294,8 +297,9 @@ static void scmi_vio_complete_cb(struct virtqueue *vqueue)
|
||||
|
||||
if (msg) {
|
||||
msg->rx_len = length;
|
||||
scmi_rx_callback(vioch->cinfo,
|
||||
msg_read_header(msg->input), msg);
|
||||
core->rx_callback(vioch->cinfo,
|
||||
core->msg->read_header(msg->input),
|
||||
msg);
|
||||
|
||||
scmi_finalize_message(vioch, msg);
|
||||
}
|
||||
@ -339,8 +343,9 @@ static void scmi_vio_deferred_tx_worker(struct work_struct *work)
|
||||
* is no more processed elsewhere so no poll_lock needed.
|
||||
*/
|
||||
if (msg->poll_status == VIO_MSG_NOT_POLLED)
|
||||
scmi_rx_callback(vioch->cinfo,
|
||||
msg_read_header(msg->input), msg);
|
||||
core->rx_callback(vioch->cinfo,
|
||||
core->msg->read_header(msg->input),
|
||||
msg);
|
||||
|
||||
/* Free the processed message once done */
|
||||
scmi_vio_msg_release(vioch, msg);
|
||||
@ -366,23 +371,6 @@ static unsigned int virtio_get_max_msg(struct scmi_chan_info *base_cinfo)
|
||||
return vioch->max_msg;
|
||||
}
|
||||
|
||||
static int virtio_link_supplier(struct device *dev)
|
||||
{
|
||||
if (!scmi_vdev) {
|
||||
dev_notice(dev,
|
||||
"Deferring probe after not finding a bound scmi-virtio device\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
if (!device_link_add(dev, &scmi_vdev->dev,
|
||||
DL_FLAG_AUTOREMOVE_CONSUMER)) {
|
||||
dev_err(dev, "Adding link to supplier virtio device failed\n");
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool virtio_chan_available(struct device_node *of_node, int idx)
|
||||
{
|
||||
struct scmi_vio_channel *channels, *vioch = NULL;
|
||||
@ -510,10 +498,10 @@ static int virtio_send_message(struct scmi_chan_info *cinfo,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
msg_tx_prepare(msg->request, xfer);
|
||||
core->msg->tx_prepare(msg->request, xfer);
|
||||
|
||||
sg_init_one(&sg_out, msg->request, msg_command_size(xfer));
|
||||
sg_init_one(&sg_in, msg->input, msg_response_size(xfer));
|
||||
sg_init_one(&sg_out, msg->request, core->msg->command_size(xfer));
|
||||
sg_init_one(&sg_in, msg->input, core->msg->response_size(xfer));
|
||||
|
||||
spin_lock_irqsave(&vioch->lock, flags);
|
||||
|
||||
@ -560,7 +548,7 @@ static void virtio_fetch_response(struct scmi_chan_info *cinfo,
|
||||
struct scmi_vio_msg *msg = xfer->priv;
|
||||
|
||||
if (msg)
|
||||
msg_fetch_response(msg->input, msg->rx_len, xfer);
|
||||
core->msg->fetch_response(msg->input, msg->rx_len, xfer);
|
||||
}
|
||||
|
||||
static void virtio_fetch_notification(struct scmi_chan_info *cinfo,
|
||||
@ -569,7 +557,8 @@ static void virtio_fetch_notification(struct scmi_chan_info *cinfo,
|
||||
struct scmi_vio_msg *msg = xfer->priv;
|
||||
|
||||
if (msg)
|
||||
msg_fetch_notification(msg->input, msg->rx_len, max_len, xfer);
|
||||
core->msg->fetch_notification(msg->input, msg->rx_len,
|
||||
max_len, xfer);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -669,7 +658,7 @@ static void virtio_mark_txdone(struct scmi_chan_info *cinfo, int ret,
|
||||
* the message we are polling for could be alternatively delivered via usual
|
||||
* IRQs callbacks on another core which happened to have IRQs enabled while we
|
||||
* are actively polling for it here: in such a case it will be handled as such
|
||||
* by scmi_rx_callback() and the polling loop in the SCMI Core TX path will be
|
||||
* by rx_callback() and the polling loop in the SCMI Core TX path will be
|
||||
* transparently terminated anyway.
|
||||
*
|
||||
* Return: True once polling has successfully completed.
|
||||
@ -790,7 +779,6 @@ static bool virtio_poll_done(struct scmi_chan_info *cinfo,
|
||||
}
|
||||
|
||||
static const struct scmi_transport_ops scmi_virtio_ops = {
|
||||
.link_supplier = virtio_link_supplier,
|
||||
.chan_available = virtio_chan_available,
|
||||
.chan_setup = virtio_chan_setup,
|
||||
.chan_free = virtio_chan_free,
|
||||
@ -802,6 +790,23 @@ static const struct scmi_transport_ops scmi_virtio_ops = {
|
||||
.poll_done = virtio_poll_done,
|
||||
};
|
||||
|
||||
static struct scmi_desc scmi_virtio_desc = {
|
||||
.ops = &scmi_virtio_ops,
|
||||
/* for non-realtime virtio devices */
|
||||
.max_rx_timeout_ms = VIRTIO_MAX_RX_TIMEOUT_MS,
|
||||
.max_msg = 0, /* overridden by virtio_get_max_msg() */
|
||||
.max_msg_size = VIRTIO_SCMI_MAX_MSG_SIZE,
|
||||
.atomic_enabled = IS_ENABLED(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO_ATOMIC_ENABLE),
|
||||
};
|
||||
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "arm,scmi-virtio" },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
DEFINE_SCMI_TRANSPORT_DRIVER(scmi_virtio, scmi_virtio_driver, scmi_virtio_desc,
|
||||
scmi_of_match, core);
|
||||
|
||||
static int scmi_vio_probe(struct virtio_device *vdev)
|
||||
{
|
||||
struct device *dev = &vdev->dev;
|
||||
@ -861,14 +866,27 @@ static int scmi_vio_probe(struct virtio_device *vdev)
|
||||
}
|
||||
|
||||
vdev->priv = channels;
|
||||
|
||||
/* Ensure initialized scmi_vdev is visible */
|
||||
smp_store_mb(scmi_vdev, vdev);
|
||||
|
||||
ret = platform_driver_register(&scmi_virtio_driver);
|
||||
if (ret) {
|
||||
vdev->priv = NULL;
|
||||
vdev->config->del_vqs(vdev);
|
||||
/* Ensure NULLified scmi_vdev is visible */
|
||||
smp_store_mb(scmi_vdev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scmi_vio_remove(struct virtio_device *vdev)
|
||||
{
|
||||
platform_driver_unregister(&scmi_virtio_driver);
|
||||
|
||||
/*
|
||||
* Once we get here, virtio_chan_free() will have already been called by
|
||||
* the SCMI core for any existing channel and, as a consequence, all the
|
||||
@ -913,23 +931,10 @@ static struct virtio_driver virtio_scmi_driver = {
|
||||
.validate = scmi_vio_validate,
|
||||
};
|
||||
|
||||
static int __init virtio_scmi_init(void)
|
||||
{
|
||||
return register_virtio_driver(&virtio_scmi_driver);
|
||||
}
|
||||
module_virtio_driver(virtio_scmi_driver);
|
||||
|
||||
static void virtio_scmi_exit(void)
|
||||
{
|
||||
unregister_virtio_driver(&virtio_scmi_driver);
|
||||
}
|
||||
|
||||
const struct scmi_desc scmi_virtio_desc = {
|
||||
.transport_init = virtio_scmi_init,
|
||||
.transport_exit = virtio_scmi_exit,
|
||||
.ops = &scmi_virtio_ops,
|
||||
/* for non-realtime virtio devices */
|
||||
.max_rx_timeout_ms = VIRTIO_MAX_RX_TIMEOUT_MS,
|
||||
.max_msg = 0, /* overridden by virtio_get_max_msg() */
|
||||
.max_msg_size = VIRTIO_SCMI_MAX_MSG_SIZE,
|
||||
.atomic_enabled = IS_ENABLED(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO_ATOMIC_ENABLE),
|
||||
};
|
||||
MODULE_AUTHOR("Igor Skalkin <igor.skalkin@opensynergy.com>");
|
||||
MODULE_AUTHOR("Peter Hilber <peter.hilber@opensynergy.com>");
|
||||
MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
|
||||
MODULE_DESCRIPTION("SCMI VirtIO Transport driver");
|
||||
MODULE_LICENSE("GPL");
|
25
drivers/firmware/arm_scmi/vendors/imx/Kconfig
vendored
Normal file
25
drivers/firmware/arm_scmi/vendors/imx/Kconfig
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menu "ARM SCMI NXP i.MX Vendor Protocols"
|
||||
|
||||
config IMX_SCMI_BBM_EXT
|
||||
tristate "i.MX SCMI BBM EXTENSION"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
This enables i.MX System BBM control logic which supports RTC
|
||||
and BUTTON.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx-sm-bbm.
|
||||
|
||||
config IMX_SCMI_MISC_EXT
|
||||
tristate "i.MX SCMI MISC EXTENSION"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
This enables i.MX System MISC control logic such as gpio expander
|
||||
wakeup
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx-sm-misc.
|
||||
endmenu
|
3
drivers/firmware/arm_scmi/vendors/imx/Makefile
vendored
Normal file
3
drivers/firmware/arm_scmi/vendors/imx/Makefile
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o
|
||||
obj-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o
|
383
drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c
vendored
Normal file
383
drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) NXP BBM Protocol
|
||||
*
|
||||
* Copyright 2024 NXP
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications BBM - " fmt
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
#include "../../protocols.h"
|
||||
#include "../../notify.h"
|
||||
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000
|
||||
|
||||
enum scmi_imx_bbm_protocol_cmd {
|
||||
IMX_BBM_GPR_SET = 0x3,
|
||||
IMX_BBM_GPR_GET = 0x4,
|
||||
IMX_BBM_RTC_ATTRIBUTES = 0x5,
|
||||
IMX_BBM_RTC_TIME_SET = 0x6,
|
||||
IMX_BBM_RTC_TIME_GET = 0x7,
|
||||
IMX_BBM_RTC_ALARM_SET = 0x8,
|
||||
IMX_BBM_BUTTON_GET = 0x9,
|
||||
IMX_BBM_RTC_NOTIFY = 0xA,
|
||||
IMX_BBM_BUTTON_NOTIFY = 0xB,
|
||||
};
|
||||
|
||||
#define GET_RTCS_NR(x) le32_get_bits((x), GENMASK(23, 16))
|
||||
#define GET_GPRS_NR(x) le32_get_bits((x), GENMASK(15, 0))
|
||||
|
||||
#define SCMI_IMX_BBM_NOTIFY_RTC_UPDATED BIT(2)
|
||||
#define SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER BIT(1)
|
||||
#define SCMI_IMX_BBM_NOTIFY_RTC_ALARM BIT(0)
|
||||
|
||||
#define SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG BIT(0)
|
||||
|
||||
#define SCMI_IMX_BBM_NOTIFY_RTC_FLAG \
|
||||
(SCMI_IMX_BBM_NOTIFY_RTC_UPDATED | SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER | \
|
||||
SCMI_IMX_BBM_NOTIFY_RTC_ALARM)
|
||||
|
||||
#define SCMI_IMX_BBM_EVENT_RTC_MASK GENMASK(31, 24)
|
||||
|
||||
struct scmi_imx_bbm_info {
|
||||
u32 version;
|
||||
int nr_rtc;
|
||||
int nr_gpr;
|
||||
};
|
||||
|
||||
struct scmi_msg_imx_bbm_protocol_attributes {
|
||||
__le32 attributes;
|
||||
};
|
||||
|
||||
struct scmi_imx_bbm_set_time {
|
||||
__le32 id;
|
||||
__le32 flags;
|
||||
__le32 value_low;
|
||||
__le32 value_high;
|
||||
};
|
||||
|
||||
struct scmi_imx_bbm_get_time {
|
||||
__le32 id;
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
struct scmi_imx_bbm_alarm_time {
|
||||
__le32 id;
|
||||
__le32 flags;
|
||||
__le32 value_low;
|
||||
__le32 value_high;
|
||||
};
|
||||
|
||||
struct scmi_msg_imx_bbm_rtc_notify {
|
||||
__le32 rtc_id;
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
struct scmi_msg_imx_bbm_button_notify {
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
struct scmi_imx_bbm_notify_payld {
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
static int scmi_imx_bbm_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_imx_bbm_info *pi)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_imx_bbm_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_rtc = GET_RTCS_NR(attr->attributes);
|
||||
pi->nr_gpr = GET_GPRS_NR(attr->attributes);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_notify(const struct scmi_protocol_handle *ph,
|
||||
u32 src_id, int message_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
if (message_id == IMX_BBM_RTC_NOTIFY) {
|
||||
struct scmi_msg_imx_bbm_rtc_notify *rtc_notify;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, message_id,
|
||||
sizeof(*rtc_notify), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc_notify = t->tx.buf;
|
||||
rtc_notify->rtc_id = cpu_to_le32(0);
|
||||
rtc_notify->flags =
|
||||
cpu_to_le32(enable ? SCMI_IMX_BBM_NOTIFY_RTC_FLAG : 0);
|
||||
} else if (message_id == IMX_BBM_BUTTON_NOTIFY) {
|
||||
struct scmi_msg_imx_bbm_button_notify *button_notify;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, message_id,
|
||||
sizeof(*button_notify), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
button_notify = t->tx.buf;
|
||||
button_notify->flags = cpu_to_le32(enable ? 1 : 0);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum scmi_imx_bbm_protocol_cmd evt_2_cmd[] = {
|
||||
IMX_BBM_RTC_NOTIFY,
|
||||
IMX_BBM_BUTTON_NOTIFY
|
||||
};
|
||||
|
||||
static int scmi_imx_bbm_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret, cmd_id;
|
||||
|
||||
if (evt_id >= ARRAY_SIZE(evt_2_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
cmd_id = evt_2_cmd[evt_id];
|
||||
ret = scmi_imx_bbm_notify(ph, src_id, cmd_id, enable);
|
||||
if (ret)
|
||||
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
|
||||
evt_id, src_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *scmi_imx_bbm_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
const struct scmi_imx_bbm_notify_payld *p = payld;
|
||||
struct scmi_imx_bbm_notif_report *r = report;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
return NULL;
|
||||
|
||||
if (evt_id == SCMI_EVENT_IMX_BBM_RTC) {
|
||||
r->is_rtc = true;
|
||||
r->is_button = false;
|
||||
r->timestamp = timestamp;
|
||||
r->rtc_id = le32_get_bits(p->flags, SCMI_IMX_BBM_EVENT_RTC_MASK);
|
||||
r->rtc_evt = le32_get_bits(p->flags, SCMI_IMX_BBM_NOTIFY_RTC_FLAG);
|
||||
dev_dbg(ph->dev, "RTC: %d evt: %x\n", r->rtc_id, r->rtc_evt);
|
||||
*src_id = r->rtc_evt;
|
||||
} else if (evt_id == SCMI_EVENT_IMX_BBM_BUTTON) {
|
||||
r->is_rtc = false;
|
||||
r->is_button = true;
|
||||
r->timestamp = timestamp;
|
||||
dev_dbg(ph->dev, "BBM Button\n");
|
||||
*src_id = 0;
|
||||
} else {
|
||||
WARN_ON_ONCE(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct scmi_event scmi_imx_bbm_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_IMX_BBM_RTC,
|
||||
.max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_imx_bbm_notif_report),
|
||||
},
|
||||
{
|
||||
.id = SCMI_EVENT_IMX_BBM_BUTTON,
|
||||
.max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_imx_bbm_notif_report),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct scmi_event_ops scmi_imx_bbm_event_ops = {
|
||||
.set_notify_enabled = scmi_imx_bbm_set_notify_enabled,
|
||||
.fill_custom_report = scmi_imx_bbm_fill_custom_report,
|
||||
};
|
||||
|
||||
static const struct scmi_protocol_events scmi_imx_bbm_protocol_events = {
|
||||
.queue_sz = SCMI_PROTO_QUEUE_SZ,
|
||||
.ops = &scmi_imx_bbm_event_ops,
|
||||
.evts = scmi_imx_bbm_events,
|
||||
.num_events = ARRAY_SIZE(scmi_imx_bbm_events),
|
||||
.num_sources = 1,
|
||||
};
|
||||
|
||||
static int scmi_imx_bbm_rtc_time_set(const struct scmi_protocol_handle *ph,
|
||||
u32 rtc_id, u64 sec)
|
||||
{
|
||||
struct scmi_imx_bbm_info *pi = ph->get_priv(ph);
|
||||
struct scmi_imx_bbm_set_time *cfg;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
if (rtc_id >= pi->nr_rtc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_SET, sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(rtc_id);
|
||||
cfg->flags = 0;
|
||||
cfg->value_low = cpu_to_le32(lower_32_bits(sec));
|
||||
cfg->value_high = cpu_to_le32(upper_32_bits(sec));
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_rtc_time_get(const struct scmi_protocol_handle *ph,
|
||||
u32 rtc_id, u64 *value)
|
||||
{
|
||||
struct scmi_imx_bbm_info *pi = ph->get_priv(ph);
|
||||
struct scmi_imx_bbm_get_time *cfg;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
if (rtc_id >= pi->nr_rtc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_GET, sizeof(*cfg),
|
||||
sizeof(u64), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(rtc_id);
|
||||
cfg->flags = 0;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret)
|
||||
*value = get_unaligned_le64(t->rx.buf);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_rtc_alarm_set(const struct scmi_protocol_handle *ph,
|
||||
u32 rtc_id, bool enable, u64 sec)
|
||||
{
|
||||
struct scmi_imx_bbm_info *pi = ph->get_priv(ph);
|
||||
struct scmi_imx_bbm_alarm_time *cfg;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
if (rtc_id >= pi->nr_rtc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_ALARM_SET, sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(rtc_id);
|
||||
cfg->flags = enable ?
|
||||
cpu_to_le32(SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG) : 0;
|
||||
cfg->value_low = cpu_to_le32(lower_32_bits(sec));
|
||||
cfg->value_high = cpu_to_le32(upper_32_bits(sec));
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_button_get(const struct scmi_protocol_handle *ph, u32 *state)
|
||||
{
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, IMX_BBM_BUTTON_GET, 0, sizeof(u32), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret)
|
||||
*state = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_imx_bbm_proto_ops scmi_imx_bbm_proto_ops = {
|
||||
.rtc_time_get = scmi_imx_bbm_rtc_time_get,
|
||||
.rtc_time_set = scmi_imx_bbm_rtc_time_set,
|
||||
.rtc_alarm_set = scmi_imx_bbm_rtc_alarm_set,
|
||||
.button_get = scmi_imx_bbm_button_get,
|
||||
};
|
||||
|
||||
static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
u32 version;
|
||||
int ret;
|
||||
struct scmi_imx_bbm_info *binfo;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM BBM Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
binfo = devm_kzalloc(ph->dev, sizeof(*binfo), GFP_KERNEL);
|
||||
if (!binfo)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scmi_imx_bbm_attributes_get(ph, binfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, binfo, version);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_bbm = {
|
||||
.id = SCMI_PROTOCOL_IMX_BBM,
|
||||
.owner = THIS_MODULE,
|
||||
.instance_init = &scmi_imx_bbm_protocol_init,
|
||||
.ops = &scmi_imx_bbm_proto_ops,
|
||||
.events = &scmi_imx_bbm_protocol_events,
|
||||
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
|
||||
.vendor_id = "NXP",
|
||||
.sub_vendor_id = "IMX",
|
||||
};
|
||||
module_scmi_protocol(scmi_imx_bbm);
|
||||
|
||||
MODULE_DESCRIPTION("i.MX SCMI BBM driver");
|
||||
MODULE_LICENSE("GPL");
|
318
drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c
vendored
Normal file
318
drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c
vendored
Normal file
@ -0,0 +1,318 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System control and Management Interface (SCMI) NXP MISC Protocol
|
||||
*
|
||||
* Copyright 2024 NXP
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SCMI Notifications MISC - " fmt
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
#include "../../protocols.h"
|
||||
#include "../../notify.h"
|
||||
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000
|
||||
|
||||
#define MAX_MISC_CTRL_SOURCES GENMASK(15, 0)
|
||||
|
||||
enum scmi_imx_misc_protocol_cmd {
|
||||
SCMI_IMX_MISC_CTRL_SET = 0x3,
|
||||
SCMI_IMX_MISC_CTRL_GET = 0x4,
|
||||
SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_info {
|
||||
u32 version;
|
||||
u32 nr_dev_ctrl;
|
||||
u32 nr_brd_ctrl;
|
||||
u32 nr_reason;
|
||||
};
|
||||
|
||||
struct scmi_msg_imx_misc_protocol_attributes {
|
||||
__le32 attributes;
|
||||
};
|
||||
|
||||
#define GET_BRD_CTRLS_NR(x) le32_get_bits((x), GENMASK(31, 24))
|
||||
#define GET_REASONS_NR(x) le32_get_bits((x), GENMASK(23, 16))
|
||||
#define GET_DEV_CTRLS_NR(x) le32_get_bits((x), GENMASK(15, 0))
|
||||
#define BRD_CTRL_START_ID BIT(15)
|
||||
|
||||
struct scmi_imx_misc_ctrl_set_in {
|
||||
__le32 id;
|
||||
__le32 num;
|
||||
__le32 value[];
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_ctrl_notify_in {
|
||||
__le32 ctrl_id;
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_ctrl_notify_payld {
|
||||
__le32 ctrl_id;
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_ctrl_get_out {
|
||||
__le32 num;
|
||||
__le32 val[];
|
||||
};
|
||||
|
||||
static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_imx_misc_info *mi)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_imx_misc_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) {
|
||||
mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
|
||||
mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
|
||||
mi->nr_reason = GET_REASONS_NR(attr->attributes);
|
||||
dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n",
|
||||
mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph,
|
||||
u32 ctrl_id)
|
||||
{
|
||||
struct scmi_imx_misc_info *mi = ph->get_priv(ph);
|
||||
|
||||
/*
|
||||
* [0, BRD_CTRL_START_ID) is for Dev Ctrl which is SOC related
|
||||
* [BRD_CTRL_START_ID, 0xffff) is for Board Ctrl which is board related
|
||||
*/
|
||||
if (ctrl_id < BRD_CTRL_START_ID && ctrl_id > mi->nr_dev_ctrl)
|
||||
return -EINVAL;
|
||||
if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph,
|
||||
u32 ctrl_id, u32 evt_id, u32 flags)
|
||||
{
|
||||
struct scmi_imx_misc_ctrl_notify_in *in;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
|
||||
sizeof(*in), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in = t->tx.buf;
|
||||
in->ctrl_id = cpu_to_le32(ctrl_id);
|
||||
in->flags = cpu_to_le32(flags);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, u32 src_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* misc_ctrl_req_notify is for enablement */
|
||||
if (enable)
|
||||
return 0;
|
||||
|
||||
ret = scmi_imx_misc_ctrl_notify(ph, src_id, evt_id, 0);
|
||||
if (ret)
|
||||
dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n",
|
||||
evt_id, src_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *
|
||||
scmi_imx_misc_ctrl_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
u8 evt_id, ktime_t timestamp,
|
||||
const void *payld, size_t payld_sz,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
|
||||
struct scmi_imx_misc_ctrl_notify_report *r = report;
|
||||
|
||||
if (sizeof(*p) != payld_sz)
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->ctrl_id = le32_to_cpu(p->ctrl_id);
|
||||
r->flags = le32_to_cpu(p->flags);
|
||||
if (src_id)
|
||||
*src_id = r->ctrl_id;
|
||||
dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
|
||||
r->ctrl_id, r->flags);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct scmi_event_ops scmi_imx_misc_event_ops = {
|
||||
.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
|
||||
.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
|
||||
};
|
||||
|
||||
static const struct scmi_event scmi_imx_misc_events[] = {
|
||||
{
|
||||
.id = SCMI_EVENT_IMX_MISC_CONTROL,
|
||||
.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
|
||||
.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
|
||||
},
|
||||
};
|
||||
|
||||
static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
|
||||
.queue_sz = SCMI_PROTO_QUEUE_SZ,
|
||||
.ops = &scmi_imx_misc_event_ops,
|
||||
.evts = scmi_imx_misc_events,
|
||||
.num_events = ARRAY_SIZE(scmi_imx_misc_events),
|
||||
.num_sources = MAX_MISC_CTRL_SOURCES,
|
||||
};
|
||||
|
||||
static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
|
||||
u32 ctrl_id, u32 *num, u32 *val)
|
||||
{
|
||||
struct scmi_imx_misc_ctrl_get_out *out;
|
||||
struct scmi_xfer *t;
|
||||
int ret, i;
|
||||
int max_msg_size = ph->hops->get_max_msg_size(ph);
|
||||
int max_num = (max_msg_size - sizeof(*out)) / sizeof(__le32);
|
||||
|
||||
ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32),
|
||||
0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(ctrl_id, t->tx.buf);
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
out = t->rx.buf;
|
||||
*num = le32_to_cpu(out->num);
|
||||
|
||||
if (*num >= max_num ||
|
||||
*num * sizeof(__le32) > t->rx.len - sizeof(__le32)) {
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < *num; i++)
|
||||
val[i] = le32_to_cpu(out->val[i]);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
|
||||
u32 ctrl_id, u32 num, u32 *val)
|
||||
{
|
||||
struct scmi_imx_misc_ctrl_set_in *in;
|
||||
struct scmi_xfer *t;
|
||||
int ret, i;
|
||||
int max_msg_size = ph->hops->get_max_msg_size(ph);
|
||||
int max_num = (max_msg_size - sizeof(*in)) / sizeof(__le32);
|
||||
|
||||
ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (num > max_num)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET, sizeof(*in),
|
||||
0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in = t->tx.buf;
|
||||
in->id = cpu_to_le32(ctrl_id);
|
||||
in->num = cpu_to_le32(num);
|
||||
for (i = 0; i < num; i++)
|
||||
in->value[i] = cpu_to_le32(val[i]);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
|
||||
.misc_ctrl_set = scmi_imx_misc_ctrl_set,
|
||||
.misc_ctrl_get = scmi_imx_misc_ctrl_get,
|
||||
.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
|
||||
};
|
||||
|
||||
static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct scmi_imx_misc_info *minfo;
|
||||
u32 version;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->version_get(ph, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
|
||||
if (!minfo)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scmi_imx_misc_attributes_get(ph, minfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ph->set_priv(ph, minfo, version);
|
||||
}
|
||||
|
||||
static const struct scmi_protocol scmi_imx_misc = {
|
||||
.id = SCMI_PROTOCOL_IMX_MISC,
|
||||
.owner = THIS_MODULE,
|
||||
.instance_init = &scmi_imx_misc_protocol_init,
|
||||
.ops = &scmi_imx_misc_proto_ops,
|
||||
.events = &scmi_imx_misc_protocol_events,
|
||||
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
|
||||
.vendor_id = "NXP",
|
||||
.sub_vendor_id = "IMX",
|
||||
};
|
||||
module_scmi_protocol(scmi_imx_misc);
|
||||
|
||||
MODULE_DESCRIPTION("i.MX SCMI MISC driver");
|
||||
MODULE_LICENSE("GPL");
|
886
drivers/firmware/arm_scmi/vendors/imx/imx95.rst
vendored
Normal file
886
drivers/firmware/arm_scmi/vendors/imx/imx95.rst
vendored
Normal file
@ -0,0 +1,886 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
.. include:: <isonum.txt>
|
||||
|
||||
===============================================================================
|
||||
i.MX95 System Control and Management Interface(SCMI) Vendor Protocols Extension
|
||||
===============================================================================
|
||||
|
||||
:Copyright: |copy| 2024 NXP
|
||||
|
||||
:Author: Peng Fan <peng.fan@nxp.com>
|
||||
|
||||
The System Manager (SM) is a low-level system function which runs on a System
|
||||
Control Processor (SCP) to support isolation and management of power domains,
|
||||
clocks, resets, sensors, pins, etc. on complex application processors. It often
|
||||
runs on a Cortex-M processor and provides an abstraction to many of the
|
||||
underlying features of the hardware. The primary purpose of the SM is to allow
|
||||
isolation between software running on different cores in the SoC. It does this
|
||||
by having exclusive access to critical resources such as those controlling
|
||||
power, clocks, reset, PMIC, etc. and then providing an RPC interface to those
|
||||
clients. This allows the SM to provide access control, arbitration, and
|
||||
aggregation policies for those shared critical resources.
|
||||
|
||||
SM introduces a concept Logic Machine(LM) which is analogous to VM and each has
|
||||
its own instance of SCMI. All normal SCMI calls only apply to that LM. That
|
||||
includes boot, shutdown, reset, suspend, wake, etc. Each LM (e.g. A55 and M7)
|
||||
are completely isolated from the others and each LM has its own communication
|
||||
channels talking to the same SCMI server.
|
||||
|
||||
This document covers all the information necessary to understand, maintain,
|
||||
port, and deploy the SM on supported processors.
|
||||
|
||||
The SM implements an interface compliant with the Arm SCMI Specification
|
||||
with additional vendor specific extensions.
|
||||
|
||||
SCMI_BBM: System Control and Management BBM Vendor Protocol
|
||||
==============================================================
|
||||
|
||||
This protocol is intended provide access to the battery-backed module. This
|
||||
contains persistent storage (GPR), an RTC, and the ON/OFF button. The protocol
|
||||
can also provide access to similar functions implemented via external board
|
||||
components. The BBM protocol provides functions to:
|
||||
|
||||
- Describe the protocol version.
|
||||
- Discover implementation attributes.
|
||||
- Read/write GPR
|
||||
- Discover the RTCs available in the system.
|
||||
- Read/write the RTC time in seconds and ticks
|
||||
- Set an alarm (per LM) in seconds
|
||||
- Get notifications on RTC update, alarm, or rollover.
|
||||
- Get notification on ON/OFF button activity.
|
||||
|
||||
For most SoC, there is one on-chip RTC (e.g. in BBNSM) and this is RTC ID 0.
|
||||
Board code can add additional GPR and RTC.
|
||||
|
||||
GPR are not aggregated. The RTC time is also not aggregated. Setting these
|
||||
sets for all so normally exclusive access would be granted to one agent for
|
||||
each. However, RTC alarms are maintained for each LM and the hardware is
|
||||
programmed with the next nearest alarm time. So only one agent in an LM should
|
||||
be given access rights to set an RTC alarm.
|
||||
|
||||
Commands:
|
||||
_________
|
||||
|
||||
PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x0
|
||||
protocol_id: 0x81
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|uint32 version | For this revision of the specification, this value must be |
|
||||
| | 0x10000. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|
||||
PROTOCOL_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x1
|
||||
protocol_id: 0x81
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes | Bits[31:8] Number of RTCs. |
|
||||
| | Bits[15:0] Number of persistent storage (GPR) words. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
PROTOCOL_MESSAGE_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x2
|
||||
protocol_id: 0x81
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: in case the message is implemented and available |
|
||||
| |to use. |
|
||||
| |NOT_FOUND: if the message identified by message_id is |
|
||||
| |invalid or not implemented |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Flags that are associated with a specific function in the |
|
||||
| |protocol. For all functions in this protocol, this |
|
||||
| |parameter has a value of 0 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_GPR_SET
|
||||
~~~~~~~~~~~
|
||||
|
||||
message_id: 0x3
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of GPR to write |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 value |32-bit value to write to the GPR |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the GPR was successfully written. |
|
||||
| |NOT_FOUND: if the index is not valid. |
|
||||
| |DENIED: if the agent does not have permission to write |
|
||||
| |the specified GPR |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_GPR_GET
|
||||
~~~~~~~~~~~
|
||||
|
||||
message_id: 0x4
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of GPR to read |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the GPR was successfully read. |
|
||||
| |NOT_FOUND: if the index is not valid. |
|
||||
| |DENIED: if the agent does not have permission to read |
|
||||
| |the specified GPR. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 value |32-bit value read from the GPR |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_RTC_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x5
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of RTC |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: returned the attributes. |
|
||||
| |NOT_FOUND: Index is invalid. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Bit[31:24] Bit width of RTC seconds. |
|
||||
| |Bit[23:16] Bit width of RTC ticks. |
|
||||
| |Bits[15:0] RTC ticks per second |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint8 name[16] |Null-terminated ASCII string of up to 16 bytes in length |
|
||||
| |describing the RTC name |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_RTC_TIME_SET
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x6
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of RTC |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] RTC time format: |
|
||||
| |Set to 1 if the time is in ticks. |
|
||||
| |Set to 0 if the time is in seconds |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 time[2] |Lower word: Lower 32 bits of the time in seconds/ticks. |
|
||||
| |Upper word: Upper 32 bits of the time in seconds/ticks. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: RTC time was successfully set. |
|
||||
| |NOT_FOUND: rtcId pertains to a non-existent RTC. |
|
||||
| |INVALID_PARAMETERS: time is not valid |
|
||||
| |(beyond the range of the RTC). |
|
||||
| |DENIED: the agent does not have permission to set the RTC. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_RTC_TIME_GET
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x7
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of RTC |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] RTC time format: |
|
||||
| |Set to 1 if the time is in ticks. |
|
||||
| |Set to 0 if the time is in seconds |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: RTC time was successfully get. |
|
||||
| |NOT_FOUND: rtcId pertains to a non-existent RTC. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 time[2] |Lower word: Lower 32 bits of the time in seconds/ticks. |
|
||||
| |Upper word: Upper 32 bits of the time in seconds/ticks. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_RTC_ALARM_SET
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x8
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of RTC |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] RTC enable flag: |
|
||||
| |Set to 1 if the RTC alarm should be enabled. |
|
||||
| |Set to 0 if the RTC alarm should be disabled |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 time[2] |Lower word: Lower 32 bits of the time in seconds. |
|
||||
| |Upper word: Upper 32 bits of the time in seconds. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: RTC time was successfully set. |
|
||||
| |NOT_FOUND: rtcId pertains to a non-existent RTC. |
|
||||
| |INVALID_PARAMETERS: time is not valid |
|
||||
| |(beyond the range of the RTC). |
|
||||
| |DENIED: the agent does not have permission to set the RTC |
|
||||
| |alarm |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_BUTTON_GET
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x9
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the button status was read. |
|
||||
| |Other value: ARM SCMI Specification status code definitions|
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 state |State of the ON/OFF button. 1: ON, 0: OFF |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_RTC_NOTIFY
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xA
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of RTC |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Notification flags |
|
||||
| |Bits[31:3] Reserved, must be zero. |
|
||||
| |Bit[2] Update enable: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification. |
|
||||
| |Bit[1] Rollover enable: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification. |
|
||||
| |Bit[0] Alarm enable: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: notification configuration was successfully |
|
||||
| |updated. |
|
||||
| |NOT_FOUND: rtcId pertains to a non-existent RTC. |
|
||||
| |DENIED: the agent does not have permission to request RTC |
|
||||
| |notifications. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_BUTTON_NOTIFY
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xB
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Notification flags |
|
||||
| |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] Enable button: |
|
||||
| |Set to 1 to send notification. |
|
||||
| |Set to 0 if no notification |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: notification configuration was successfully |
|
||||
| |updated. |
|
||||
| |DENIED: the agent does not have permission to request |
|
||||
| |button notifications. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
NEGOTIATE_PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x10
|
||||
protocol_id: 0x81
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Parameters |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 version |The negotiated protocol version the agent intends to use |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the negotiated protocol version is supported |
|
||||
| |by the platform. All commands, responses, and |
|
||||
| |notifications post successful return of this command must|
|
||||
| |comply with the negotiated version. |
|
||||
| |NOT_SUPPORTED: if the protocol version is not supported. |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
Notifications
|
||||
_____________
|
||||
|
||||
BBM_RTC_EVENT
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x0
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |RTC events: |
|
||||
| |Bits[31:2] Reserved, must be zero. |
|
||||
| |Bit[1] RTC rollover notification: |
|
||||
| |1 RTC rollover detected. |
|
||||
| |0 no RTC rollover detected. |
|
||||
| |Bit[0] RTC alarm notification: |
|
||||
| |1 RTC alarm generated. |
|
||||
| |0 no RTC alarm generated. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
BBM_BUTTON_EVENT
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x1
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |RTC events: |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
| |Button events: |
|
||||
| |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] Button notification: |
|
||||
| |1 button change detected. |
|
||||
| |0 no button change detected. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
SCMI_MISC: System Control and Management MISC Vendor Protocol
|
||||
================================================================
|
||||
|
||||
Provides miscellaneous functions. This includes controls that are miscellaneous
|
||||
settings/actions that must be exposed from the SM to agents. They are device
|
||||
specific and are usually define to access bit fields in various mix block
|
||||
control modules, IOMUX_GPR, and other GPR/CSR owned by the SM. This protocol
|
||||
supports the following functions:
|
||||
|
||||
- Describe the protocol version.
|
||||
- Discover implementation attributes.
|
||||
- Set/Get a control.
|
||||
- Initiate an action on a control.
|
||||
- Obtain platform (i.e. SM) build information.
|
||||
- Obtain ROM passover data.
|
||||
- Read boot/shutdown/reset information for the LM or the system.
|
||||
|
||||
Commands:
|
||||
_________
|
||||
|
||||
PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x0
|
||||
protocol_id: 0x84
|
||||
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Return values |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|uint32 version | For this revision of the specification, this value must be |
|
||||
| | 0x10000. |
|
||||
+---------------+--------------------------------------------------------------+
|
||||
|
||||
PROTOCOL_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x1
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status | See ARM SCMI Specification for status code definitions. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Protocol attributes: |
|
||||
| |Bits[31:24] Reserved, must be zero. |
|
||||
| |Bits[23:16] Number of reset reasons. |
|
||||
| |Bits[15:0] Number of controls |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
PROTOCOL_MESSAGE_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x2
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: in case the message is implemented and available |
|
||||
| |to use. |
|
||||
| |NOT_FOUND: if the message identified by message_id is |
|
||||
| |invalid or not implemented |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Flags that are associated with a specific function in the |
|
||||
| |protocol. For all functions in this protocol, this |
|
||||
| |parameter has a value of 0 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_CONTROL_SET
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x3
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of the control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 num |Size of the value data in words |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 val[8] |value data array |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the control was set successfully. |
|
||||
| |NOT_FOUND: if the index is not valid. |
|
||||
| |DENIED: if the agent does not have permission to set the |
|
||||
| |control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_CONTROL_GET
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x4
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of the control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the control was get successfully. |
|
||||
| |NOT_FOUND: if the index is not valid. |
|
||||
| |DENIED: if the agent does not have permission to get the |
|
||||
| |control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 num |Size of the return data in words, max 8 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 | |
|
||||
|val[0, num - 1] |value data array |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_CONTROL_ACTION
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x5
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of the control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 action |Action for the control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 numarg |Size of the argument data, max 8 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 | |
|
||||
|arg[0, numarg -1] |Argument data array |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the action was set successfully. |
|
||||
| |NOT_FOUND: if the index is not valid. |
|
||||
| |DENIED: if the agent does not have permission to get the |
|
||||
| |control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 num |Size of the return data in words, max 8 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 | |
|
||||
|val[0, num - 1] |value data array |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_DISCOVER_BUILD_INFO
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This function is used to obtain the build commit, data, time, number.
|
||||
|
||||
message_id: 0x6
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the build info was got successfully. |
|
||||
| |NOT_SUPPORTED: if the data is not available. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 buildnum |Build number |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 buildcommit|Most significant 32 bits of the git commit hash |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint8 date[16] |Date of build. Null terminated ASCII string of up to 16 |
|
||||
| |bytes in length |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint8 time[16] |Time of build. Null terminated ASCII string of up to 16 |
|
||||
| |bytes in length |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_ROM_PASSOVER_GET
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ROM passover data is information exported by ROM and could be used by others.
|
||||
It includes boot device, instance, type, mode and etc. This function is used
|
||||
to obtain the ROM passover data. The returned block of words is structured as
|
||||
defined in the ROM passover section in the SoC RM.
|
||||
|
||||
message_id: 0x7
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the data was got successfully. |
|
||||
| |NOT_SUPPORTED: if the data is not available. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 num |Size of the passover data in words, max 13 |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 | |
|
||||
|data[0, num - 1] |Passover data array |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_CONTROL_NOTIFY
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x8
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 index |Index of control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Notification flags, varies by control |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: notification configuration was successfully |
|
||||
| |updated. |
|
||||
| |NOT_FOUND: control id not exists. |
|
||||
| |INVALID_PARAMETERS: if the input attributes flag specifies |
|
||||
| |unsupported or invalid configurations.. |
|
||||
| |DENIED: if the calling agent is not permitted to request |
|
||||
| |the notification. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_RESET_REASON_ATTRIBUTES
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x9
|
||||
protocol_id: 0x84
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 reasonid |Identifier for the reason |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Return values |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if valid reason attributes are returned |
|
||||
| |NOT_FOUND: if reasonId pertains to a non-existent reason. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 attributes |Reason attributes. This parameter has the following |
|
||||
| |format: Bits[31:0] Reserved, must be zero |
|
||||
| |Bits[15:0] Number of persistent storage (GPR) words. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint8 name[16] |Null-terminated ASCII string of up to 16 bytes in length |
|
||||
| |describing the reason |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|
||||
MISC_RESET_REASON_GET
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xA
|
||||
protocol_id: 0x84
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Parameters |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 flags |Reason flags. This parameter has the following format: |
|
||||
| |Bits[31:1] Reserved, must be zero. |
|
||||
| |Bit[0] System: |
|
||||
| |Set to 1 to return the system reason. |
|
||||
| |Set to 0 to return the LM reason |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: reset reason return |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 bootflags |Boot reason flags. This parameter has the format: |
|
||||
| |Bits[31] Valid. |
|
||||
| |Set to 1 if the entire reason is valid. |
|
||||
| |Set to 0 if the entire reason is not valid. |
|
||||
| |Bits[30:29] Reserved, must be zero. |
|
||||
| |Bit[28] Valid origin: |
|
||||
| |Set to 1 if the origin field is valid. |
|
||||
| |Set to 0 if the origin field is not valid. |
|
||||
| |Bits[27:24] Origin. |
|
||||
| |Bit[23] Valid err ID: |
|
||||
| |Set to 1 if the error ID field is valid. |
|
||||
| |Set to 0 if the error ID field is not valid. |
|
||||
| |Bits[22:8] Error ID. |
|
||||
| |Bit[7:0] Reason |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 shutdownflags|Shutdown reason flags. This parameter has the format: |
|
||||
| |Bits[31] Valid. |
|
||||
| |Set to 1 if the entire reason is valid. |
|
||||
| |Set to 0 if the entire reason is not valid. |
|
||||
| |Bits[30:29] Number of valid extended info words. |
|
||||
| |Bit[28] Valid origin: |
|
||||
| |Set to 1 if the origin field is valid. |
|
||||
| |Set to 0 if the origin field is not valid. |
|
||||
| |Bits[27:24] Origin. |
|
||||
| |Bit[23] Valid err ID: |
|
||||
| |Set to 1 if the error ID field is valid. |
|
||||
| |Set to 0 if the error ID field is not valid. |
|
||||
| |Bits[22:8] Error ID. |
|
||||
| |Bit[7:0] Reason |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 extinfo[8] |Array of extended info words |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
MISC_SI_INFO_GET
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xB
|
||||
protocol_id: 0x84
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: silicon info return |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 deviceid |Silicon specific device ID |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 sirev |Silicon specific revision |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 partnum |Silicon specific part number |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint8 siname[16] |Silicon name/revision. Null terminated ASCII string of up|
|
||||
| |to 16 bytes in length |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
MISC_CFG_INFO_GET
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xC
|
||||
protocol_id: 0x84
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: config name return |
|
||||
| |NOT_SUPPORTED: name not available |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 msel |Mode selector value |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint8 cfgname[16] |config file basename. Null terminated ASCII string of up |
|
||||
| |to 16 bytes in length |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
MISC_SYSLOG_GET
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0xD
|
||||
protocol_id: 0x84
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Parameters |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 flags |Device specific flags that might impact the data returned|
|
||||
| |or clearing of the data |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 logindex |Index to the first log word. Will be the first element in|
|
||||
| |the return array |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: system log return |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 numLogflags |Descriptor for the log data returned by this call. |
|
||||
| |Bits[31:20] Number of remaining log words. |
|
||||
| |Bits[15:12] Reserved, must be zero. |
|
||||
| |Bits[11:0] Number of log words that are returned by this |
|
||||
| |call |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 syslog[N] |Log data array, N is defined in bits[11:0] of numLogflags|
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
NEGOTIATE_PROTOCOL_VERSION
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x10
|
||||
protocol_id: 0x84
|
||||
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Parameters |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|uint32 version |The negotiated protocol version the agent intends to use |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Return values |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|int32 status |SUCCESS: if the negotiated protocol version is supported |
|
||||
| |by the platform. All commands, responses, and |
|
||||
| |notifications post successful return of this command must|
|
||||
| |comply with the negotiated version. |
|
||||
| |NOT_SUPPORTED: if the protocol version is not supported. |
|
||||
+--------------------+---------------------------------------------------------+
|
||||
|
||||
Notifications
|
||||
_____________
|
||||
|
||||
MISC_CONTROL_EVENT
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
message_id: 0x0
|
||||
protocol_id: 0x81
|
||||
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Parameters |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|Name |Description |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 ctrlid |Identifier for the control that caused the event. |
|
||||
+------------------+-----------------------------------------------------------+
|
||||
|uint32 flags |Event flags, varies by control. |
|
||||
+------------------+-----------------------------------------------------------+
|
@ -11,7 +11,7 @@
|
||||
#include "protocols.h"
|
||||
|
||||
/* Updated only after ALL the mandatory features for that version are merged */
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
|
||||
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001
|
||||
|
||||
#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
|
||||
#define REMAINING_LEVELS_MASK GENMASK(31, 16)
|
||||
@ -229,8 +229,10 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
|
||||
/* Retrieve domain attributes at first ... */
|
||||
put_unaligned_le32(dom, td->tx.buf);
|
||||
/* Skip domain on comms error */
|
||||
if (ph->xops->do_xfer(ph, td))
|
||||
if (ph->xops->do_xfer(ph, td)) {
|
||||
ph->xops->reset_rx_to_maxsz(ph, td);
|
||||
continue;
|
||||
}
|
||||
|
||||
v = vinfo->domains + dom;
|
||||
v->id = dom;
|
||||
|
@ -22,3 +22,14 @@ config IMX_SCU
|
||||
|
||||
This driver manages the IPC interface between host CPU and the
|
||||
SCU firmware running on M4.
|
||||
|
||||
config IMX_SCMI_MISC_DRV
|
||||
tristate "IMX SCMI MISC Protocol driver"
|
||||
depends on IMX_SCMI_MISC_EXT || COMPILE_TEST
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
The System Controller Management Interface firmware (SCMI FW) is
|
||||
a low-level system function which runs on a dedicated Cortex-M
|
||||
core that could provide misc functions such as board control.
|
||||
|
||||
This driver can also be built as a module.
|
||||
|
@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_IMX_DSP) += imx-dsp.o
|
||||
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o
|
||||
obj-${CONFIG_IMX_SCMI_MISC_DRV} += sm-misc.o
|
||||
|
119
drivers/firmware/imx/sm-misc.c
Normal file
119
drivers/firmware/imx/sm-misc.c
Normal file
@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2024 NXP
|
||||
*/
|
||||
|
||||
#include <linux/firmware/imx/sm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops;
|
||||
static struct scmi_protocol_handle *ph;
|
||||
struct notifier_block scmi_imx_misc_ctrl_nb;
|
||||
|
||||
int scmi_imx_misc_ctrl_set(u32 id, u32 val)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return imx_misc_ctrl_ops->misc_ctrl_set(ph, id, 1, &val);
|
||||
};
|
||||
EXPORT_SYMBOL(scmi_imx_misc_ctrl_set);
|
||||
|
||||
int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val)
|
||||
{
|
||||
if (!ph)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return imx_misc_ctrl_ops->misc_ctrl_get(ph, id, num, val);
|
||||
}
|
||||
EXPORT_SYMBOL(scmi_imx_misc_ctrl_get);
|
||||
|
||||
static int scmi_imx_misc_ctrl_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
/*
|
||||
* notifier_chain_register requires a valid notifier_block and
|
||||
* valid notifier_call. SCMI_EVENT_IMX_MISC_CONTROL is needed
|
||||
* to let SCMI firmware enable control events, but the hook here
|
||||
* is just a dummy function to avoid kernel panic as of now.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
struct device_node *np = sdev->dev.of_node;
|
||||
u32 src_id, flags;
|
||||
int ret, i, num;
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
if (imx_misc_ctrl_ops) {
|
||||
dev_err(&sdev->dev, "misc ctrl already initialized\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
imx_misc_ctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_MISC, &ph);
|
||||
if (IS_ERR(imx_misc_ctrl_ops))
|
||||
return PTR_ERR(imx_misc_ctrl_ops);
|
||||
|
||||
num = of_property_count_u32_elems(np, "nxp,ctrl-ids");
|
||||
if (num % 2) {
|
||||
dev_err(&sdev->dev, "Invalid wakeup-sources\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scmi_imx_misc_ctrl_nb.notifier_call = &scmi_imx_misc_ctrl_notifier;
|
||||
for (i = 0; i < num; i += 2) {
|
||||
ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i, &src_id);
|
||||
if (ret) {
|
||||
dev_err(&sdev->dev, "Failed to read ctrl-id: %i\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i + 1, &flags);
|
||||
if (ret) {
|
||||
dev_err(&sdev->dev, "Failed to read ctrl-id value: %d\n", i + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_MISC,
|
||||
SCMI_EVENT_IMX_MISC_CONTROL,
|
||||
&src_id,
|
||||
&scmi_imx_misc_ctrl_nb);
|
||||
if (ret) {
|
||||
dev_err(&sdev->dev, "Failed to register scmi misc event: %d\n", src_id);
|
||||
} else {
|
||||
ret = imx_misc_ctrl_ops->misc_ctrl_req_notify(ph, src_id,
|
||||
SCMI_EVENT_IMX_MISC_CONTROL,
|
||||
flags);
|
||||
if (ret)
|
||||
dev_err(&sdev->dev, "Failed to req notify: %d\n", src_id);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_IMX_MISC, "imx-misc-ctrl" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_imx_misc_ctrl_driver = {
|
||||
.name = "scmi-imx-misc-ctrl",
|
||||
.probe = scmi_imx_misc_ctrl_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_imx_misc_ctrl_driver);
|
||||
|
||||
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX SM MISC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -41,17 +41,6 @@ config QCOM_TZMEM_MODE_SHMBRIDGE
|
||||
|
||||
endchoice
|
||||
|
||||
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
|
||||
bool "Qualcomm download mode enabled by default"
|
||||
depends on QCOM_SCM
|
||||
help
|
||||
A device with "download mode" enabled will upon an unexpected
|
||||
warm-restart enter a special debug mode that allows the user to
|
||||
"download" memory content over USB for offline postmortem analysis.
|
||||
The feature can be enabled/disabled on the kernel command line.
|
||||
|
||||
Say Y here to enable "download mode" by default.
|
||||
|
||||
config QCOM_QSEECOM
|
||||
bool "Qualcomm QSEECOM interface driver"
|
||||
depends on QCOM_SCM=y
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
@ -32,8 +33,7 @@
|
||||
#include "qcom_scm.h"
|
||||
#include "qcom_tzmem.h"
|
||||
|
||||
static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
|
||||
module_param(download_mode, bool, 0);
|
||||
static u32 download_mode;
|
||||
|
||||
struct qcom_scm {
|
||||
struct device *dev;
|
||||
@ -126,6 +126,8 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
|
||||
#define QCOM_DLOAD_MASK GENMASK(5, 4)
|
||||
#define QCOM_DLOAD_NODUMP 0
|
||||
#define QCOM_DLOAD_FULLDUMP 1
|
||||
#define QCOM_DLOAD_MINIDUMP 2
|
||||
#define QCOM_DLOAD_BOTHDUMP 3
|
||||
|
||||
static const char * const qcom_scm_convention_names[] = {
|
||||
[SMC_CONVENTION_UNKNOWN] = "unknown",
|
||||
@ -134,6 +136,13 @@ static const char * const qcom_scm_convention_names[] = {
|
||||
[SMC_CONVENTION_LEGACY] = "smc legacy",
|
||||
};
|
||||
|
||||
static const char * const download_mode_name[] = {
|
||||
[QCOM_DLOAD_NODUMP] = "off",
|
||||
[QCOM_DLOAD_FULLDUMP] = "full",
|
||||
[QCOM_DLOAD_MINIDUMP] = "mini",
|
||||
[QCOM_DLOAD_BOTHDUMP] = "full,mini",
|
||||
};
|
||||
|
||||
static struct qcom_scm *__scm;
|
||||
|
||||
static int qcom_scm_clk_enable(void)
|
||||
@ -526,17 +535,16 @@ static int qcom_scm_io_rmw(phys_addr_t addr, unsigned int mask, unsigned int val
|
||||
return qcom_scm_io_writel(addr, new);
|
||||
}
|
||||
|
||||
static void qcom_scm_set_download_mode(bool enable)
|
||||
static void qcom_scm_set_download_mode(u32 dload_mode)
|
||||
{
|
||||
u32 val = enable ? QCOM_DLOAD_FULLDUMP : QCOM_DLOAD_NODUMP;
|
||||
int ret = 0;
|
||||
|
||||
if (__scm->dload_mode_addr) {
|
||||
ret = qcom_scm_io_rmw(__scm->dload_mode_addr, QCOM_DLOAD_MASK,
|
||||
FIELD_PREP(QCOM_DLOAD_MASK, val));
|
||||
FIELD_PREP(QCOM_DLOAD_MASK, dload_mode));
|
||||
} 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);
|
||||
ret = __qcom_scm_set_dload_mode(__scm->dev, !!dload_mode);
|
||||
} else {
|
||||
dev_err(__scm->dev,
|
||||
"No available mechanism for setting download mode\n");
|
||||
@ -1724,7 +1732,10 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send);
|
||||
*/
|
||||
static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
|
||||
{ .compatible = "lenovo,flex-5g" },
|
||||
{ .compatible = "lenovo,thinkpad-t14s" },
|
||||
{ .compatible = "lenovo,thinkpad-x13s", },
|
||||
{ .compatible = "microsoft,romulus13", },
|
||||
{ .compatible = "microsoft,romulus15", },
|
||||
{ .compatible = "qcom,sc8180x-primus" },
|
||||
{ .compatible = "qcom,x1e80100-crd" },
|
||||
{ .compatible = "qcom,x1e80100-qcp" },
|
||||
@ -1886,6 +1897,45 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int get_download_mode(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
if (download_mode >= ARRAY_SIZE(download_mode_name))
|
||||
return sysfs_emit(buffer, "unknown mode\n");
|
||||
|
||||
return sysfs_emit(buffer, "%s\n", download_mode_name[download_mode]);
|
||||
}
|
||||
|
||||
static int set_download_mode(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
bool tmp;
|
||||
int ret;
|
||||
|
||||
ret = sysfs_match_string(download_mode_name, val);
|
||||
if (ret < 0) {
|
||||
ret = kstrtobool(val, &tmp);
|
||||
if (ret < 0) {
|
||||
pr_err("qcom_scm: err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = tmp ? 1 : 0;
|
||||
}
|
||||
|
||||
download_mode = ret;
|
||||
if (__scm)
|
||||
qcom_scm_set_download_mode(download_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops download_mode_param_ops = {
|
||||
.get = get_download_mode,
|
||||
.set = set_download_mode,
|
||||
};
|
||||
|
||||
module_param_cb(download_mode, &download_mode_param_ops, NULL, 0644);
|
||||
MODULE_PARM_DESC(download_mode, "download mode: off/0/N for no dump mode, full/on/1/Y for full dump mode, mini for minidump mode and full,mini for both full and minidump mode together are acceptable values");
|
||||
|
||||
static int qcom_scm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_tzmem_pool_config pool_config;
|
||||
@ -1950,18 +2000,16 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
||||
__get_convention();
|
||||
|
||||
/*
|
||||
* If requested enable "download mode", from this point on warmboot
|
||||
* If "download mode" is requested, from this point on warmboot
|
||||
* will cause the boot stages to enter download mode, unless
|
||||
* disabled below by a clean shutdown/reboot.
|
||||
*/
|
||||
if (download_mode)
|
||||
qcom_scm_set_download_mode(true);
|
||||
|
||||
qcom_scm_set_download_mode(download_mode);
|
||||
|
||||
/*
|
||||
* Disable SDI if indicated by DT that it is enabled by default.
|
||||
*/
|
||||
if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled"))
|
||||
if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled") || !download_mode)
|
||||
qcom_scm_disable_sdi();
|
||||
|
||||
ret = of_reserved_mem_device_init(__scm->dev);
|
||||
@ -2003,7 +2051,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
||||
static void qcom_scm_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
/* Clean shutdown, disable download mode to allow normal restart */
|
||||
qcom_scm_set_download_mode(false);
|
||||
qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP);
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_scm_dt_match[] = {
|
||||
|
@ -62,7 +62,6 @@ rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data)
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -ETIMEDOUT;
|
||||
WARN_ONCE(1, "Firmware transaction timeout");
|
||||
}
|
||||
} else {
|
||||
dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret);
|
||||
@ -125,6 +124,8 @@ int rpi_firmware_property_list(struct rpi_firmware *fw,
|
||||
dev_err(fw->cl.dev, "Request 0x%08x returned status 0x%08x\n",
|
||||
buf[2], buf[1]);
|
||||
ret = -EINVAL;
|
||||
} else if (ret == -ETIMEDOUT) {
|
||||
WARN_ONCE(1, "Firmware transaction 0x%08x timeout", buf[2]);
|
||||
}
|
||||
|
||||
dma_free_coherent(fw->chan->mbox->dev, PAGE_ALIGN(size), buf, bus_addr);
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
@ -24,12 +25,6 @@
|
||||
#define MSG_RING BIT(1)
|
||||
#define TAG_SZ 32
|
||||
|
||||
static inline struct tegra_bpmp *
|
||||
mbox_client_to_bpmp(struct mbox_client *client)
|
||||
{
|
||||
return container_of(client, struct tegra_bpmp, mbox.client);
|
||||
}
|
||||
|
||||
static inline const struct tegra_bpmp_ops *
|
||||
channel_to_ops(struct tegra_bpmp_channel *channel)
|
||||
{
|
||||
@ -40,29 +35,24 @@ channel_to_ops(struct tegra_bpmp_channel *channel)
|
||||
|
||||
struct tegra_bpmp *tegra_bpmp_get(struct device *dev)
|
||||
{
|
||||
struct device_node *np __free(device_node);
|
||||
struct platform_device *pdev;
|
||||
struct tegra_bpmp *bpmp;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(dev->of_node, "nvidia,bpmp", 0);
|
||||
if (!np)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev) {
|
||||
bpmp = ERR_PTR(-ENODEV);
|
||||
goto put;
|
||||
}
|
||||
if (!pdev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
bpmp = platform_get_drvdata(pdev);
|
||||
if (!bpmp) {
|
||||
bpmp = ERR_PTR(-EPROBE_DEFER);
|
||||
put_device(&pdev->dev);
|
||||
goto put;
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
put:
|
||||
of_node_put(np);
|
||||
return bpmp;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_bpmp_get);
|
||||
|
@ -5,26 +5,43 @@
|
||||
* Copyright (C) 2019, 2024 Marek Behún <kabel@kernel.org>
|
||||
*/
|
||||
|
||||
#include <crypto/sha2.h>
|
||||
#include <linux/align.h>
|
||||
#include <linux/armada-37xx-rwtm-mailbox.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DRIVER_NAME "turris-mox-rwtm"
|
||||
|
||||
#define RWTM_DMA_BUFFER_SIZE SZ_4K
|
||||
|
||||
/*
|
||||
* The macros and constants below come from Turris Mox's rWTM firmware code.
|
||||
* This firmware is open source and it's sources can be found at
|
||||
* https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi.
|
||||
*/
|
||||
|
||||
#define MOX_ECC_NUMBER_WORDS 17
|
||||
#define MOX_ECC_NUMBER_LEN (MOX_ECC_NUMBER_WORDS * sizeof(u32))
|
||||
|
||||
#define MOX_ECC_SIGNATURE_WORDS (2 * MOX_ECC_NUMBER_WORDS)
|
||||
|
||||
#define MBOX_STS_SUCCESS (0 << 30)
|
||||
#define MBOX_STS_FAIL (1 << 30)
|
||||
#define MBOX_STS_BADCMD (2 << 30)
|
||||
@ -44,13 +61,9 @@ enum mbox_cmd {
|
||||
MBOX_CMD_OTP_WRITE = 8,
|
||||
};
|
||||
|
||||
struct mox_kobject;
|
||||
|
||||
struct mox_rwtm {
|
||||
struct device *dev;
|
||||
struct mbox_client mbox_client;
|
||||
struct mbox_chan *mbox;
|
||||
struct mox_kobject *kobj;
|
||||
struct hwrng hwrng;
|
||||
|
||||
struct armada_37xx_rwtm_rx_msg reply;
|
||||
@ -62,13 +75,13 @@ struct mox_rwtm {
|
||||
struct completion cmd_done;
|
||||
|
||||
/* board information */
|
||||
int has_board_info;
|
||||
bool has_board_info;
|
||||
u64 serial_number;
|
||||
int board_version, ram_size;
|
||||
u8 mac_address1[6], mac_address2[6];
|
||||
u8 mac_address1[ETH_ALEN], mac_address2[ETH_ALEN];
|
||||
|
||||
/* public key burned in eFuse */
|
||||
int has_pubkey;
|
||||
bool has_pubkey;
|
||||
u8 pubkey[135];
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
@ -78,65 +91,27 @@ struct mox_rwtm {
|
||||
* It should be rewritten via crypto API once akcipher API is available
|
||||
* from userspace.
|
||||
*/
|
||||
struct dentry *debugfs_root;
|
||||
u32 last_sig[34];
|
||||
int last_sig_done;
|
||||
u32 last_sig[MOX_ECC_SIGNATURE_WORDS];
|
||||
bool last_sig_done;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mox_kobject {
|
||||
struct kobject kobj;
|
||||
struct mox_rwtm *rwtm;
|
||||
};
|
||||
|
||||
static inline struct kobject *rwtm_to_kobj(struct mox_rwtm *rwtm)
|
||||
static inline struct device *rwtm_dev(struct mox_rwtm *rwtm)
|
||||
{
|
||||
return &rwtm->kobj->kobj;
|
||||
}
|
||||
|
||||
static inline struct mox_rwtm *to_rwtm(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct mox_kobject, kobj)->rwtm;
|
||||
}
|
||||
|
||||
static void mox_kobj_release(struct kobject *kobj)
|
||||
{
|
||||
kfree(to_rwtm(kobj)->kobj);
|
||||
}
|
||||
|
||||
static const struct kobj_type mox_kobj_ktype = {
|
||||
.release = mox_kobj_release,
|
||||
.sysfs_ops = &kobj_sysfs_ops,
|
||||
};
|
||||
|
||||
static int mox_kobj_create(struct mox_rwtm *rwtm)
|
||||
{
|
||||
rwtm->kobj = kzalloc(sizeof(*rwtm->kobj), GFP_KERNEL);
|
||||
if (!rwtm->kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
kobject_init(rwtm_to_kobj(rwtm), &mox_kobj_ktype);
|
||||
if (kobject_add(rwtm_to_kobj(rwtm), firmware_kobj, "turris-mox-rwtm")) {
|
||||
kobject_put(rwtm_to_kobj(rwtm));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rwtm->kobj->rwtm = rwtm;
|
||||
|
||||
return 0;
|
||||
return rwtm->mbox_client.dev;
|
||||
}
|
||||
|
||||
#define MOX_ATTR_RO(name, format, cat) \
|
||||
static ssize_t \
|
||||
name##_show(struct kobject *kobj, struct kobj_attribute *a, \
|
||||
name##_show(struct device *dev, struct device_attribute *a, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct mox_rwtm *rwtm = to_rwtm(kobj); \
|
||||
struct mox_rwtm *rwtm = dev_get_drvdata(dev); \
|
||||
if (!rwtm->has_##cat) \
|
||||
return -ENODATA; \
|
||||
return sprintf(buf, format, rwtm->name); \
|
||||
return sysfs_emit(buf, format, rwtm->name); \
|
||||
} \
|
||||
static struct kobj_attribute mox_attr_##name = __ATTR_RO(name)
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
MOX_ATTR_RO(serial_number, "%016llX\n", board_info);
|
||||
MOX_ATTR_RO(board_version, "%i\n", board_info);
|
||||
@ -145,6 +120,17 @@ MOX_ATTR_RO(mac_address1, "%pM\n", board_info);
|
||||
MOX_ATTR_RO(mac_address2, "%pM\n", board_info);
|
||||
MOX_ATTR_RO(pubkey, "%s\n", pubkey);
|
||||
|
||||
static struct attribute *turris_mox_rwtm_attrs[] = {
|
||||
&dev_attr_serial_number.attr,
|
||||
&dev_attr_board_version.attr,
|
||||
&dev_attr_ram_size.attr,
|
||||
&dev_attr_mac_address1.attr,
|
||||
&dev_attr_mac_address2.attr,
|
||||
&dev_attr_pubkey.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(turris_mox_rwtm);
|
||||
|
||||
static int mox_get_status(enum mbox_cmd cmd, u32 retval)
|
||||
{
|
||||
if (MBOX_STS_CMD(retval) != cmd)
|
||||
@ -152,23 +138,13 @@ static int mox_get_status(enum mbox_cmd cmd, u32 retval)
|
||||
else if (MBOX_STS_ERROR(retval) == MBOX_STS_FAIL)
|
||||
return -(int)MBOX_STS_VALUE(retval);
|
||||
else if (MBOX_STS_ERROR(retval) == MBOX_STS_BADCMD)
|
||||
return -ENOSYS;
|
||||
return -EOPNOTSUPP;
|
||||
else if (MBOX_STS_ERROR(retval) != MBOX_STS_SUCCESS)
|
||||
return -EIO;
|
||||
else
|
||||
return MBOX_STS_VALUE(retval);
|
||||
}
|
||||
|
||||
static const struct attribute *mox_rwtm_attrs[] = {
|
||||
&mox_attr_serial_number.attr,
|
||||
&mox_attr_board_version.attr,
|
||||
&mox_attr_ram_size.attr,
|
||||
&mox_attr_mac_address1.attr,
|
||||
&mox_attr_mac_address2.attr,
|
||||
&mox_attr_pubkey.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void mox_rwtm_rx_callback(struct mbox_client *cl, void *data)
|
||||
{
|
||||
struct mox_rwtm *rwtm = dev_get_drvdata(cl->dev);
|
||||
@ -181,6 +157,34 @@ static void mox_rwtm_rx_callback(struct mbox_client *cl, void *data)
|
||||
complete(&rwtm->cmd_done);
|
||||
}
|
||||
|
||||
static int mox_rwtm_exec(struct mox_rwtm *rwtm, enum mbox_cmd cmd,
|
||||
struct armada_37xx_rwtm_tx_msg *msg,
|
||||
bool interruptible)
|
||||
{
|
||||
struct armada_37xx_rwtm_tx_msg _msg = {};
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
msg = &_msg;
|
||||
|
||||
msg->command = cmd;
|
||||
|
||||
ret = mbox_send_message(rwtm->mbox, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (interruptible) {
|
||||
ret = wait_for_completion_interruptible(&rwtm->cmd_done);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return mox_get_status(cmd, rwtm->reply.retval);
|
||||
}
|
||||
|
||||
static void reply_to_mac_addr(u8 *mac, u32 t1, u32 t2)
|
||||
{
|
||||
mac[0] = t1 >> 8;
|
||||
@ -193,24 +197,16 @@ static void reply_to_mac_addr(u8 *mac, u32 t1, u32 t2)
|
||||
|
||||
static int mox_get_board_info(struct mox_rwtm *rwtm)
|
||||
{
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
struct device *dev = rwtm_dev(rwtm);
|
||||
struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
|
||||
int ret;
|
||||
|
||||
msg.command = MBOX_CMD_BOARD_INFO;
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = mox_get_status(MBOX_CMD_BOARD_INFO, reply->retval);
|
||||
ret = mox_rwtm_exec(rwtm, MBOX_CMD_BOARD_INFO, NULL, false);
|
||||
if (ret == -ENODATA) {
|
||||
dev_warn(rwtm->dev,
|
||||
dev_warn(dev,
|
||||
"Board does not have manufacturing information burned!\n");
|
||||
} else if (ret == -ENOSYS) {
|
||||
dev_notice(rwtm->dev,
|
||||
} else if (ret == -EOPNOTSUPP) {
|
||||
dev_notice(dev,
|
||||
"Firmware does not support the BOARD_INFO command\n");
|
||||
} else if (ret < 0) {
|
||||
return ret;
|
||||
@ -224,7 +220,7 @@ static int mox_get_board_info(struct mox_rwtm *rwtm)
|
||||
reply->status[5]);
|
||||
reply_to_mac_addr(rwtm->mac_address2, reply->status[6],
|
||||
reply->status[7]);
|
||||
rwtm->has_board_info = 1;
|
||||
rwtm->has_board_info = true;
|
||||
|
||||
pr_info("Turris Mox serial number %016llX\n",
|
||||
rwtm->serial_number);
|
||||
@ -232,26 +228,18 @@ static int mox_get_board_info(struct mox_rwtm *rwtm)
|
||||
pr_info(" burned RAM size %i MiB\n", rwtm->ram_size);
|
||||
}
|
||||
|
||||
msg.command = MBOX_CMD_ECDSA_PUB_KEY;
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = mox_get_status(MBOX_CMD_ECDSA_PUB_KEY, reply->retval);
|
||||
ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false);
|
||||
if (ret == -ENODATA) {
|
||||
dev_warn(rwtm->dev, "Board has no public key burned!\n");
|
||||
} else if (ret == -ENOSYS) {
|
||||
dev_notice(rwtm->dev,
|
||||
dev_warn(dev, "Board has no public key burned!\n");
|
||||
} else if (ret == -EOPNOTSUPP) {
|
||||
dev_notice(dev,
|
||||
"Firmware does not support the ECDSA_PUB_KEY command\n");
|
||||
} else if (ret < 0) {
|
||||
return ret;
|
||||
} else {
|
||||
u32 *s = reply->status;
|
||||
|
||||
rwtm->has_pubkey = 1;
|
||||
rwtm->has_pubkey = true;
|
||||
sprintf(rwtm->pubkey,
|
||||
"%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],
|
||||
@ -263,37 +251,22 @@ static int mox_get_board_info(struct mox_rwtm *rwtm)
|
||||
|
||||
static int check_get_random_support(struct mox_rwtm *rwtm)
|
||||
{
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
int ret;
|
||||
struct armada_37xx_rwtm_tx_msg msg = {
|
||||
.args = { 1, rwtm->buf_phys, 4 },
|
||||
};
|
||||
|
||||
msg.command = MBOX_CMD_GET_RANDOM;
|
||||
msg.args[0] = 1;
|
||||
msg.args[1] = rwtm->buf_phys;
|
||||
msg.args[2] = 4;
|
||||
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return mox_get_status(MBOX_CMD_GET_RANDOM, rwtm->reply.retval);
|
||||
return mox_rwtm_exec(rwtm, MBOX_CMD_GET_RANDOM, &msg, false);
|
||||
}
|
||||
|
||||
static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
||||
{
|
||||
struct mox_rwtm *rwtm = (struct mox_rwtm *) rng->priv;
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
struct mox_rwtm *rwtm = container_of(rng, struct mox_rwtm, hwrng);
|
||||
struct armada_37xx_rwtm_tx_msg msg = {
|
||||
.args = { 1, rwtm->buf_phys, ALIGN(max, 4) },
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (max > 4096)
|
||||
max = 4096;
|
||||
|
||||
msg.command = MBOX_CMD_GET_RANDOM;
|
||||
msg.args[0] = 1;
|
||||
msg.args[1] = rwtm->buf_phys;
|
||||
msg.args[2] = (max + 3) & ~3;
|
||||
max = min(max, RWTM_DMA_BUFFER_SIZE);
|
||||
|
||||
if (!wait) {
|
||||
if (!mutex_trylock(&rwtm->busy))
|
||||
@ -302,15 +275,7 @@ static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
||||
mutex_lock(&rwtm->busy);
|
||||
}
|
||||
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
ret = wait_for_completion_interruptible(&rwtm->cmd_done);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
ret = mox_get_status(MBOX_CMD_GET_RANDOM, rwtm->reply.retval);
|
||||
ret = mox_rwtm_exec(rwtm, MBOX_CMD_GET_RANDOM, &msg, true);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
@ -336,19 +301,19 @@ static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len,
|
||||
struct mox_rwtm *rwtm = file->private_data;
|
||||
ssize_t ret;
|
||||
|
||||
/* only allow one read, of 136 bytes, from position 0 */
|
||||
/* only allow one read, of whole signature, from position 0 */
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
if (len < 136)
|
||||
if (len < sizeof(rwtm->last_sig))
|
||||
return -EINVAL;
|
||||
|
||||
if (!rwtm->last_sig_done)
|
||||
return -ENODATA;
|
||||
|
||||
/* 2 arrays of 17 32-bit words are 136 bytes */
|
||||
ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig, 136);
|
||||
rwtm->last_sig_done = 0;
|
||||
ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig,
|
||||
sizeof(rwtm->last_sig));
|
||||
rwtm->last_sig_done = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -357,13 +322,11 @@ static ssize_t do_sign_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
struct mox_rwtm *rwtm = file->private_data;
|
||||
struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
loff_t dummy = 0;
|
||||
ssize_t ret;
|
||||
|
||||
/* the input is a SHA-512 hash, so exactly 64 bytes have to be read */
|
||||
if (len != 64)
|
||||
if (len != SHA512_DIGEST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* if last result is not zero user has not read that information yet */
|
||||
@ -384,37 +347,32 @@ static ssize_t do_sign_write(struct file *file, const char __user *buf,
|
||||
* 3. Address of the buffer where ECDSA signature value S shall be
|
||||
* stored by the rWTM firmware.
|
||||
*/
|
||||
memset(rwtm->buf, 0, 4);
|
||||
ret = simple_write_to_buffer(rwtm->buf + 4, 64, &dummy, buf, len);
|
||||
memset(rwtm->buf, 0, sizeof(u32));
|
||||
ret = simple_write_to_buffer(rwtm->buf + sizeof(u32),
|
||||
SHA512_DIGEST_SIZE, &dummy, buf, len);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
be32_to_cpu_array(rwtm->buf, rwtm->buf, 17);
|
||||
be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUMBER_WORDS);
|
||||
|
||||
msg.command = MBOX_CMD_SIGN;
|
||||
msg.args[0] = 1;
|
||||
msg.args[1] = rwtm->buf_phys;
|
||||
msg.args[2] = rwtm->buf_phys + 68;
|
||||
msg.args[3] = rwtm->buf_phys + 2 * 68;
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
msg.args[2] = rwtm->buf_phys + MOX_ECC_NUMBER_LEN;
|
||||
msg.args[3] = rwtm->buf_phys + 2 * MOX_ECC_NUMBER_LEN;
|
||||
|
||||
ret = wait_for_completion_interruptible(&rwtm->cmd_done);
|
||||
ret = mox_rwtm_exec(rwtm, MBOX_CMD_SIGN, &msg, true);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
ret = MBOX_STS_VALUE(reply->retval);
|
||||
if (MBOX_STS_ERROR(reply->retval) != MBOX_STS_SUCCESS)
|
||||
goto unlock_mutex;
|
||||
|
||||
/*
|
||||
* Here we read the R and S values of the ECDSA signature
|
||||
* computed by the rWTM firmware and convert their words from
|
||||
* LE to BE.
|
||||
*/
|
||||
memcpy(rwtm->last_sig, rwtm->buf + 68, 136);
|
||||
cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig, 34);
|
||||
rwtm->last_sig_done = 1;
|
||||
memcpy(rwtm->last_sig, rwtm->buf + MOX_ECC_NUMBER_LEN,
|
||||
sizeof(rwtm->last_sig));
|
||||
cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig,
|
||||
MOX_ECC_SIGNATURE_WORDS);
|
||||
rwtm->last_sig_done = true;
|
||||
|
||||
mutex_unlock(&rwtm->busy);
|
||||
return len;
|
||||
@ -431,43 +389,37 @@ static const struct file_operations do_sign_fops = {
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static int rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
static void rwtm_debugfs_release(void *root)
|
||||
{
|
||||
struct dentry *root, *entry;
|
||||
debugfs_remove_recursive(root);
|
||||
}
|
||||
|
||||
static void rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
struct dentry *root;
|
||||
|
||||
root = debugfs_create_dir("turris-mox-rwtm", NULL);
|
||||
|
||||
if (IS_ERR(root))
|
||||
return PTR_ERR(root);
|
||||
debugfs_create_file_unsafe("do_sign", 0600, root, rwtm, &do_sign_fops);
|
||||
|
||||
entry = debugfs_create_file_unsafe("do_sign", 0600, root, rwtm,
|
||||
&do_sign_fops);
|
||||
if (IS_ERR(entry))
|
||||
goto err_remove;
|
||||
|
||||
rwtm->debugfs_root = root;
|
||||
|
||||
return 0;
|
||||
err_remove:
|
||||
debugfs_remove_recursive(root);
|
||||
return PTR_ERR(entry);
|
||||
}
|
||||
|
||||
static void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
debugfs_remove_recursive(rwtm->debugfs_root);
|
||||
devm_add_action_or_reset(rwtm_dev(rwtm), rwtm_debugfs_release, root);
|
||||
}
|
||||
#else
|
||||
static inline int rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
|
||||
static inline void rwtm_register_debugfs(struct mox_rwtm *rwtm)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void rwtm_devm_mbox_release(void *mbox)
|
||||
{
|
||||
mbox_free_channel(mbox);
|
||||
}
|
||||
|
||||
static void rwtm_firmware_symlink_drop(void *parent)
|
||||
{
|
||||
sysfs_remove_link(parent, DRIVER_NAME);
|
||||
}
|
||||
|
||||
static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mox_rwtm *rwtm;
|
||||
@ -478,40 +430,30 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
if (!rwtm)
|
||||
return -ENOMEM;
|
||||
|
||||
rwtm->dev = dev;
|
||||
rwtm->buf = dmam_alloc_coherent(dev, PAGE_SIZE, &rwtm->buf_phys,
|
||||
GFP_KERNEL);
|
||||
rwtm->buf = dmam_alloc_coherent(dev, RWTM_DMA_BUFFER_SIZE,
|
||||
&rwtm->buf_phys, GFP_KERNEL);
|
||||
if (!rwtm->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mox_kobj_create(rwtm);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot create turris-mox-rwtm kobject!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sysfs_create_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot create sysfs files!\n");
|
||||
goto put_kobj;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rwtm);
|
||||
|
||||
mutex_init(&rwtm->busy);
|
||||
ret = devm_mutex_init(dev, &rwtm->busy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_completion(&rwtm->cmd_done);
|
||||
|
||||
rwtm->mbox_client.dev = dev;
|
||||
rwtm->mbox_client.rx_callback = mox_rwtm_rx_callback;
|
||||
|
||||
rwtm->mbox = mbox_request_channel(&rwtm->mbox_client, 0);
|
||||
if (IS_ERR(rwtm->mbox)) {
|
||||
ret = PTR_ERR(rwtm->mbox);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Cannot request mailbox channel: %i\n",
|
||||
ret);
|
||||
goto remove_files;
|
||||
}
|
||||
if (IS_ERR(rwtm->mbox))
|
||||
return dev_err_probe(dev, PTR_ERR(rwtm->mbox),
|
||||
"Cannot request mailbox channel!\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, rwtm_devm_mbox_release, rwtm->mbox);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mox_get_board_info(rwtm);
|
||||
if (ret < 0)
|
||||
@ -521,46 +463,30 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
if (ret < 0) {
|
||||
dev_notice(dev,
|
||||
"Firmware does not support the GET_RANDOM command\n");
|
||||
goto free_channel;
|
||||
return ret;
|
||||
}
|
||||
|
||||
rwtm->hwrng.name = DRIVER_NAME "_hwrng";
|
||||
rwtm->hwrng.read = mox_hwrng_read;
|
||||
rwtm->hwrng.priv = (unsigned long) rwtm;
|
||||
|
||||
ret = devm_hwrng_register(dev, &rwtm->hwrng);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot register HWRNG: %i\n", ret);
|
||||
goto free_channel;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Cannot register HWRNG!\n");
|
||||
|
||||
ret = rwtm_register_debugfs(rwtm);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed creating debugfs entries: %i\n", ret);
|
||||
goto free_channel;
|
||||
}
|
||||
rwtm_register_debugfs(rwtm);
|
||||
|
||||
dev_info(dev, "HWRNG successfully registered\n");
|
||||
|
||||
/*
|
||||
* For sysfs ABI compatibility, create symlink
|
||||
* /sys/firmware/turris-mox-rwtm to this device's sysfs directory.
|
||||
*/
|
||||
ret = sysfs_create_link(firmware_kobj, &dev->kobj, DRIVER_NAME);
|
||||
if (!ret)
|
||||
devm_add_action_or_reset(dev, rwtm_firmware_symlink_drop,
|
||||
firmware_kobj);
|
||||
|
||||
return 0;
|
||||
|
||||
free_channel:
|
||||
mbox_free_channel(rwtm->mbox);
|
||||
remove_files:
|
||||
sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
|
||||
put_kobj:
|
||||
kobject_put(rwtm_to_kobj(rwtm));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void turris_mox_rwtm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mox_rwtm *rwtm = platform_get_drvdata(pdev);
|
||||
|
||||
rwtm_unregister_debugfs(rwtm);
|
||||
sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
|
||||
kobject_put(rwtm_to_kobj(rwtm));
|
||||
mbox_free_channel(rwtm->mbox);
|
||||
}
|
||||
|
||||
static const struct of_device_id turris_mox_rwtm_match[] = {
|
||||
@ -573,10 +499,10 @@ MODULE_DEVICE_TABLE(of, turris_mox_rwtm_match);
|
||||
|
||||
static struct platform_driver turris_mox_rwtm_driver = {
|
||||
.probe = turris_mox_rwtm_probe,
|
||||
.remove_new = turris_mox_rwtm_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = turris_mox_rwtm_match,
|
||||
.dev_groups = turris_mox_rwtm_groups,
|
||||
},
|
||||
};
|
||||
module_platform_driver(turris_mox_rwtm_driver);
|
||||
|
@ -466,6 +466,17 @@ config KEYBOARD_IMX
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx_keypad.
|
||||
|
||||
config KEYBOARD_IMX_BBM_SCMI
|
||||
tristate "IMX BBM SCMI Key Driver"
|
||||
depends on IMX_SCMI_BBM_EXT || COMPILE_TEST
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
This is the BBM key driver for NXP i.MX SoCs managed through
|
||||
SCMI protocol.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called scmi-imx-bbm-key.
|
||||
|
||||
config KEYBOARD_IMX_SC_KEY
|
||||
tristate "IMX SCU Key Driver"
|
||||
depends on IMX_SCU
|
||||
|
@ -31,6 +31,7 @@ obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_IQS62X) += iqs62x-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_IMX_SC_KEY) += imx_sc_key.o
|
||||
obj-$(CONFIG_KEYBOARD_IMX_BBM_SCMI) += imx-sm-bbm-key.o
|
||||
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
|
||||
|
225
drivers/input/keyboard/imx-sm-bbm-key.c
Normal file
225
drivers/input/keyboard/imx-sm-bbm-key.c
Normal file
@ -0,0 +1,225 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2024 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#define DEBOUNCE_TIME 30
|
||||
#define REPEAT_INTERVAL 60
|
||||
|
||||
struct scmi_imx_bbm {
|
||||
struct scmi_protocol_handle *ph;
|
||||
const struct scmi_imx_bbm_proto_ops *ops;
|
||||
struct notifier_block nb;
|
||||
int keycode;
|
||||
int keystate; /* 1:pressed */
|
||||
bool suspended;
|
||||
struct delayed_work check_work;
|
||||
struct input_dev *input;
|
||||
};
|
||||
|
||||
static void scmi_imx_bbm_pwrkey_check_for_events(struct work_struct *work)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = container_of(to_delayed_work(work),
|
||||
struct scmi_imx_bbm, check_work);
|
||||
struct scmi_protocol_handle *ph = bbnsm->ph;
|
||||
struct input_dev *input = bbnsm->input;
|
||||
u32 state = 0;
|
||||
int ret;
|
||||
|
||||
ret = bbnsm->ops->button_get(ph, &state);
|
||||
if (ret) {
|
||||
pr_err("%s: %d\n", __func__, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_debug("%s: state: %d, keystate %d\n", __func__, state, bbnsm->keystate);
|
||||
|
||||
/* only report new event if status changed */
|
||||
if (state ^ bbnsm->keystate) {
|
||||
bbnsm->keystate = state;
|
||||
input_event(input, EV_KEY, bbnsm->keycode, state);
|
||||
input_sync(input);
|
||||
pm_relax(bbnsm->input->dev.parent);
|
||||
pr_debug("EV_KEY: %x\n", bbnsm->keycode);
|
||||
}
|
||||
|
||||
/* repeat check if pressed long */
|
||||
if (state)
|
||||
schedule_delayed_work(&bbnsm->check_work, msecs_to_jiffies(REPEAT_INTERVAL));
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_pwrkey_event(struct scmi_imx_bbm *bbnsm)
|
||||
{
|
||||
struct input_dev *input = bbnsm->input;
|
||||
|
||||
pm_wakeup_event(input->dev.parent, 0);
|
||||
|
||||
/*
|
||||
* Directly report key event after resume to make no key press
|
||||
* event is missed.
|
||||
*/
|
||||
if (READ_ONCE(bbnsm->suspended)) {
|
||||
bbnsm->keystate = 1;
|
||||
input_event(input, EV_KEY, bbnsm->keycode, 1);
|
||||
input_sync(input);
|
||||
WRITE_ONCE(bbnsm->suspended, false);
|
||||
}
|
||||
|
||||
schedule_delayed_work(&bbnsm->check_work, msecs_to_jiffies(DEBOUNCE_TIME));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scmi_imx_bbm_pwrkey_act(void *pdata)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = pdata;
|
||||
|
||||
cancel_delayed_work_sync(&bbnsm->check_work);
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_key_notifier(struct notifier_block *nb, unsigned long event, void *data)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = container_of(nb, struct scmi_imx_bbm, nb);
|
||||
struct scmi_imx_bbm_notif_report *r = data;
|
||||
|
||||
if (r->is_button) {
|
||||
pr_debug("BBM Button Power key pressed\n");
|
||||
scmi_imx_bbm_pwrkey_event(bbnsm);
|
||||
} else {
|
||||
/* Should never reach here */
|
||||
pr_err("Unexpected BBM event: %s\n", __func__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_pwrkey_init(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
struct device *dev = &sdev->dev;
|
||||
struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
|
||||
struct input_dev *input;
|
||||
int ret;
|
||||
|
||||
if (device_property_read_u32(dev, "linux,code", &bbnsm->keycode)) {
|
||||
bbnsm->keycode = KEY_POWER;
|
||||
dev_warn(dev, "key code is not specified, using default KEY_POWER\n");
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&bbnsm->check_work, scmi_imx_bbm_pwrkey_check_for_events);
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input) {
|
||||
dev_err(dev, "failed to allocate the input device for SCMI IMX BBM\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input->name = dev_name(dev);
|
||||
input->phys = "bbnsm-pwrkey/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
|
||||
input_set_capability(input, EV_KEY, bbnsm->keycode);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, scmi_imx_bbm_pwrkey_act, bbnsm);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register remove action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bbnsm->input = input;
|
||||
|
||||
bbnsm->nb.notifier_call = &scmi_imx_bbm_key_notifier;
|
||||
ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_BBM,
|
||||
SCMI_EVENT_IMX_BBM_BUTTON,
|
||||
NULL, &bbnsm->nb);
|
||||
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to register BBM Button Events %d:", ret);
|
||||
|
||||
ret = input_register_device(input);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register input device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_key_probe(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
struct device *dev = &sdev->dev;
|
||||
struct scmi_protocol_handle *ph;
|
||||
struct scmi_imx_bbm *bbnsm;
|
||||
int ret;
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
bbnsm = devm_kzalloc(dev, sizeof(*bbnsm), GFP_KERNEL);
|
||||
if (!bbnsm)
|
||||
return -ENOMEM;
|
||||
|
||||
bbnsm->ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_BBM, &ph);
|
||||
if (IS_ERR(bbnsm->ops))
|
||||
return PTR_ERR(bbnsm->ops);
|
||||
|
||||
bbnsm->ph = ph;
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
|
||||
dev_set_drvdata(dev, bbnsm);
|
||||
|
||||
ret = scmi_imx_bbm_pwrkey_init(sdev);
|
||||
if (ret)
|
||||
device_init_wakeup(dev, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused scmi_imx_bbm_key_suspend(struct device *dev)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
|
||||
|
||||
WRITE_ONCE(bbnsm->suspended, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused scmi_imx_bbm_key_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(scmi_imx_bbm_pm_key_ops, scmi_imx_bbm_key_suspend,
|
||||
scmi_imx_bbm_key_resume);
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_IMX_BBM, "imx-bbm-key" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_imx_bbm_key_driver = {
|
||||
.driver = {
|
||||
.pm = &scmi_imx_bbm_pm_key_ops,
|
||||
},
|
||||
.name = "scmi-imx-bbm-key",
|
||||
.probe = scmi_imx_bbm_key_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_imx_bbm_key_driver);
|
||||
|
||||
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX SM BBM Key driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -6,6 +6,7 @@
|
||||
* Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
@ -517,7 +518,7 @@ static int atmel_ebi_dev_disable(struct atmel_ebi *ebi, struct device_node *np)
|
||||
static int atmel_ebi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child, *np = dev->of_node, *smc_np;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct atmel_ebi *ebi;
|
||||
int ret, reg_cells;
|
||||
struct clk *clk;
|
||||
@ -541,30 +542,24 @@ static int atmel_ebi_probe(struct platform_device *pdev)
|
||||
|
||||
ebi->clk = clk;
|
||||
|
||||
smc_np = of_parse_phandle(dev->of_node, "atmel,smc", 0);
|
||||
struct device_node *smc_np __free(device_node) =
|
||||
of_parse_phandle(dev->of_node, "atmel,smc", 0);
|
||||
|
||||
ebi->smc.regmap = syscon_node_to_regmap(smc_np);
|
||||
if (IS_ERR(ebi->smc.regmap)) {
|
||||
ret = PTR_ERR(ebi->smc.regmap);
|
||||
goto put_node;
|
||||
}
|
||||
if (IS_ERR(ebi->smc.regmap))
|
||||
return PTR_ERR(ebi->smc.regmap);
|
||||
|
||||
ebi->smc.layout = atmel_hsmc_get_reg_layout(smc_np);
|
||||
if (IS_ERR(ebi->smc.layout)) {
|
||||
ret = PTR_ERR(ebi->smc.layout);
|
||||
goto put_node;
|
||||
}
|
||||
if (IS_ERR(ebi->smc.layout))
|
||||
return PTR_ERR(ebi->smc.layout);
|
||||
|
||||
ebi->smc.clk = of_clk_get(smc_np, 0);
|
||||
if (IS_ERR(ebi->smc.clk)) {
|
||||
if (PTR_ERR(ebi->smc.clk) != -ENOENT) {
|
||||
ret = PTR_ERR(ebi->smc.clk);
|
||||
goto put_node;
|
||||
}
|
||||
if (PTR_ERR(ebi->smc.clk) != -ENOENT)
|
||||
return PTR_ERR(ebi->smc.clk);
|
||||
|
||||
ebi->smc.clk = NULL;
|
||||
}
|
||||
of_node_put(smc_np);
|
||||
ret = clk_prepare_enable(ebi->smc.clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -597,7 +592,7 @@ static int atmel_ebi_probe(struct platform_device *pdev)
|
||||
|
||||
reg_cells += val;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
if (!of_property_present(child, "reg"))
|
||||
continue;
|
||||
|
||||
@ -607,18 +602,12 @@ static int atmel_ebi_probe(struct platform_device *pdev)
|
||||
child);
|
||||
|
||||
ret = atmel_ebi_dev_disable(ebi, child);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return of_platform_populate(np, NULL, NULL, dev);
|
||||
|
||||
put_node:
|
||||
of_node_put(smc_np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __maybe_unused int atmel_ebi_resume(struct device *dev)
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Aneesh V <aneesh@ti.com>
|
||||
* Santosh Shilimkar <santosh.shilimkar@ti.com>
|
||||
*/
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/reboot.h>
|
||||
@ -57,7 +58,6 @@ struct emif_data {
|
||||
u8 temperature_level;
|
||||
u8 lpmode;
|
||||
struct list_head node;
|
||||
unsigned long irq_state;
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct emif_regs *regs_cache[EMIF_MAX_NUM_FREQUENCIES];
|
||||
@ -69,7 +69,6 @@ struct emif_data {
|
||||
|
||||
static struct emif_data *emif1;
|
||||
static DEFINE_SPINLOCK(emif_lock);
|
||||
static unsigned long irq_state;
|
||||
static LIST_HEAD(device_list);
|
||||
|
||||
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
|
||||
@ -523,18 +522,18 @@ static void setup_temperature_sensitive_regs(struct emif_data *emif,
|
||||
static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
|
||||
{
|
||||
u32 old_temp_level;
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
irqreturn_t ret;
|
||||
struct emif_custom_configs *custom_configs;
|
||||
|
||||
spin_lock_irqsave(&emif_lock, irq_state);
|
||||
guard(spinlock_irqsave)(&emif_lock);
|
||||
old_temp_level = emif->temperature_level;
|
||||
get_temperature_level(emif);
|
||||
|
||||
if (unlikely(emif->temperature_level == old_temp_level)) {
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
} else if (!emif->curr_regs) {
|
||||
dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n");
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
custom_configs = emif->plat_data->custom_configs;
|
||||
@ -554,8 +553,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
|
||||
* from thread context
|
||||
*/
|
||||
emif->temperature_level = SDRAM_TEMP_VERY_HIGH_SHUTDOWN;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
goto out;
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
@ -571,10 +569,9 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
|
||||
/* Temperature is going up - handle immediately */
|
||||
setup_temperature_sensitive_regs(emif, emif->curr_regs);
|
||||
do_freq_update();
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&emif_lock, irq_state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -617,6 +614,7 @@ static irqreturn_t emif_interrupt_handler(int irq, void *dev_id)
|
||||
static irqreturn_t emif_threaded_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct emif_data *emif = dev_id;
|
||||
unsigned long irq_state;
|
||||
|
||||
if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
|
||||
dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
|
||||
@ -864,7 +862,7 @@ static void of_get_custom_configs(struct device_node *np_emif,
|
||||
be32_to_cpup(poll_intvl);
|
||||
}
|
||||
|
||||
if (of_find_property(np_emif, "extended-temp-part", &len))
|
||||
if (of_property_read_bool(np_emif, "extended-temp-part"))
|
||||
cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART;
|
||||
|
||||
if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
|
||||
@ -880,13 +878,9 @@ static void of_get_ddr_info(struct device_node *np_emif,
|
||||
struct ddr_device_info *dev_info)
|
||||
{
|
||||
u32 density = 0, io_width = 0;
|
||||
int len;
|
||||
|
||||
if (of_find_property(np_emif, "cs1-used", &len))
|
||||
dev_info->cs1_used = true;
|
||||
|
||||
if (of_find_property(np_emif, "cal-resistor-per-cs", &len))
|
||||
dev_info->cal_resistors_per_cs = true;
|
||||
dev_info->cs1_used = of_property_read_bool(np_emif, "cs1-used");
|
||||
dev_info->cal_resistors_per_cs = of_property_read_bool(np_emif, "cal-resistor-per-cs");
|
||||
|
||||
if (of_device_is_compatible(np_ddr, "jedec,lpddr2-s4"))
|
||||
dev_info->type = DDR_TYPE_LPDDR2_S4;
|
||||
@ -916,7 +910,6 @@ static struct emif_data *of_get_memory_device_details(
|
||||
struct ddr_device_info *dev_info = NULL;
|
||||
struct emif_platform_data *pd = NULL;
|
||||
struct device_node *np_ddr;
|
||||
int len;
|
||||
|
||||
np_ddr = of_parse_phandle(np_emif, "device-handle", 0);
|
||||
if (!np_ddr)
|
||||
@ -944,7 +937,7 @@ static struct emif_data *of_get_memory_device_details(
|
||||
|
||||
of_property_read_u32(np_emif, "phy-type", &pd->phy_type);
|
||||
|
||||
if (of_find_property(np_emif, "hw-caps-ll-interface", &len))
|
||||
if (of_property_read_bool(np_emif, "hw-caps-ll-interface"))
|
||||
pd->hw_caps |= EMIF_HW_CAPS_LL_INTERFACE;
|
||||
|
||||
of_get_ddr_info(np_emif, np_ddr, dev_info);
|
||||
|
@ -771,13 +771,9 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(common->smi_ao_base))
|
||||
return PTR_ERR(common->smi_ao_base);
|
||||
|
||||
common->clk_async = devm_clk_get(dev, "async");
|
||||
common->clk_async = devm_clk_get_enabled(dev, "async");
|
||||
if (IS_ERR(common->clk_async))
|
||||
return PTR_ERR(common->clk_async);
|
||||
|
||||
ret = clk_prepare_enable(common->clk_async);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
common->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(common->base))
|
||||
|
@ -9,6 +9,7 @@
|
||||
* Copyright (C) 2009 Texas Instruments
|
||||
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
|
||||
*/
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -989,18 +990,18 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
|
||||
if (size > (1 << GPMC_SECTION_SHIFT))
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock(&gpmc_mem_lock);
|
||||
if (gpmc_cs_reserved(cs)) {
|
||||
r = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
guard(spinlock)(&gpmc_mem_lock);
|
||||
|
||||
if (gpmc_cs_reserved(cs))
|
||||
return -EBUSY;
|
||||
|
||||
if (gpmc_cs_mem_enabled(cs))
|
||||
r = adjust_resource(res, res->start & ~(size - 1), size);
|
||||
if (r < 0)
|
||||
r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0,
|
||||
size, NULL, NULL);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
return r;
|
||||
|
||||
/* Disable CS while changing base address and size mask */
|
||||
gpmc_cs_disable_mem(cs);
|
||||
@ -1008,16 +1009,15 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
|
||||
r = gpmc_cs_set_memconf(cs, res->start, resource_size(res));
|
||||
if (r < 0) {
|
||||
release_resource(res);
|
||||
goto out;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Enable CS */
|
||||
gpmc_cs_enable_mem(cs);
|
||||
*base = res->start;
|
||||
gpmc_cs_set_reserved(cs, 1);
|
||||
out:
|
||||
spin_unlock(&gpmc_mem_lock);
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(gpmc_cs_request);
|
||||
|
||||
@ -1026,10 +1026,9 @@ void gpmc_cs_free(int cs)
|
||||
struct gpmc_cs_data *gpmc;
|
||||
struct resource *res;
|
||||
|
||||
spin_lock(&gpmc_mem_lock);
|
||||
guard(spinlock)(&gpmc_mem_lock);
|
||||
if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {
|
||||
WARN(1, "Trying to free non-reserved GPMC CS%d\n", cs);
|
||||
spin_unlock(&gpmc_mem_lock);
|
||||
return;
|
||||
}
|
||||
gpmc = &gpmc_cs[cs];
|
||||
@ -1039,7 +1038,6 @@ void gpmc_cs_free(int cs)
|
||||
if (res->flags)
|
||||
release_resource(res);
|
||||
gpmc_cs_set_reserved(cs, 0);
|
||||
spin_unlock(&gpmc_mem_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(gpmc_cs_free);
|
||||
|
||||
|
@ -187,6 +187,13 @@ static int pl172_parse_cs_config(struct amba_device *adev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void pl172_amba_release_regions(void *data)
|
||||
{
|
||||
struct amba_device *adev = data;
|
||||
|
||||
amba_release_regions(adev);
|
||||
}
|
||||
|
||||
static const char * const pl172_revisions[] = {"r1", "r2", "r2p3", "r2p4"};
|
||||
static const char * const pl175_revisions[] = {"r1"};
|
||||
static const char * const pl176_revisions[] = {"r0"};
|
||||
@ -216,38 +223,30 @@ static int pl172_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
if (!pl172)
|
||||
return -ENOMEM;
|
||||
|
||||
pl172->clk = devm_clk_get(dev, "mpmcclk");
|
||||
if (IS_ERR(pl172->clk)) {
|
||||
dev_err(dev, "no mpmcclk provided clock\n");
|
||||
return PTR_ERR(pl172->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(pl172->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to mpmcclk enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
pl172->clk = devm_clk_get_enabled(dev, "mpmcclk");
|
||||
if (IS_ERR(pl172->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(pl172->clk),
|
||||
"no mpmcclk provided clock\n");
|
||||
|
||||
pl172->rate = clk_get_rate(pl172->clk) / MSEC_PER_SEC;
|
||||
if (!pl172->rate) {
|
||||
dev_err(dev, "unable to get mpmcclk clock rate\n");
|
||||
ret = -EINVAL;
|
||||
goto err_clk_enable;
|
||||
}
|
||||
if (!pl172->rate)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"unable to get mpmcclk clock rate\n");
|
||||
|
||||
ret = amba_request_regions(adev, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to request AMBA regions\n");
|
||||
goto err_clk_enable;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, pl172_amba_release_regions, adev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pl172->base = devm_ioremap(dev, adev->res.start,
|
||||
resource_size(&adev->res));
|
||||
if (!pl172->base) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_no_ioremap;
|
||||
}
|
||||
if (!pl172->base)
|
||||
return dev_err_probe(dev, -ENOMEM, "ioremap failed\n");
|
||||
|
||||
amba_set_drvdata(adev, pl172);
|
||||
|
||||
@ -265,20 +264,6 @@ static int pl172_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_no_ioremap:
|
||||
amba_release_regions(adev);
|
||||
err_clk_enable:
|
||||
clk_disable_unprepare(pl172->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pl172_remove(struct amba_device *adev)
|
||||
{
|
||||
struct pl172_data *pl172 = amba_get_drvdata(adev);
|
||||
|
||||
clk_disable_unprepare(pl172->clk);
|
||||
amba_release_regions(adev);
|
||||
}
|
||||
|
||||
static const struct amba_id pl172_ids[] = {
|
||||
@ -306,7 +291,6 @@ static struct amba_driver pl172_driver = {
|
||||
.name = "memory-pl172",
|
||||
},
|
||||
.probe = pl172_probe,
|
||||
.remove = pl172_remove,
|
||||
.id_table = pl172_ids,
|
||||
};
|
||||
module_amba_driver(pl172_driver);
|
||||
|
@ -74,73 +74,39 @@ static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
struct device_node *of_node = adev->dev.of_node;
|
||||
const struct of_device_id *match = NULL;
|
||||
struct pl353_smc_data *pl353_smc;
|
||||
struct device_node *child;
|
||||
int err;
|
||||
|
||||
pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL);
|
||||
if (!pl353_smc)
|
||||
return -ENOMEM;
|
||||
|
||||
pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk");
|
||||
if (IS_ERR(pl353_smc->aclk)) {
|
||||
dev_err(&adev->dev, "aclk clock not found.\n");
|
||||
return PTR_ERR(pl353_smc->aclk);
|
||||
}
|
||||
pl353_smc->aclk = devm_clk_get_enabled(&adev->dev, "apb_pclk");
|
||||
if (IS_ERR(pl353_smc->aclk))
|
||||
return dev_err_probe(&adev->dev, PTR_ERR(pl353_smc->aclk),
|
||||
"aclk clock not found.\n");
|
||||
|
||||
pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk");
|
||||
if (IS_ERR(pl353_smc->memclk)) {
|
||||
dev_err(&adev->dev, "memclk clock not found.\n");
|
||||
return PTR_ERR(pl353_smc->memclk);
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(pl353_smc->aclk);
|
||||
if (err) {
|
||||
dev_err(&adev->dev, "Unable to enable AXI clock.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(pl353_smc->memclk);
|
||||
if (err) {
|
||||
dev_err(&adev->dev, "Unable to enable memory clock.\n");
|
||||
goto disable_axi_clk;
|
||||
}
|
||||
pl353_smc->memclk = devm_clk_get_enabled(&adev->dev, "memclk");
|
||||
if (IS_ERR(pl353_smc->memclk))
|
||||
return dev_err_probe(&adev->dev, PTR_ERR(pl353_smc->memclk),
|
||||
"memclk clock not found.\n");
|
||||
|
||||
amba_set_drvdata(adev, pl353_smc);
|
||||
|
||||
/* Find compatible children. Only a single child is supported */
|
||||
for_each_available_child_of_node(of_node, child) {
|
||||
for_each_available_child_of_node_scoped(of_node, child) {
|
||||
match = of_match_node(pl353_smc_supported_children, child);
|
||||
if (!match) {
|
||||
dev_warn(&adev->dev, "unsupported child node\n");
|
||||
continue;
|
||||
}
|
||||
of_platform_device_create(child, NULL, &adev->dev);
|
||||
break;
|
||||
}
|
||||
if (!match) {
|
||||
err = -ENODEV;
|
||||
dev_err(&adev->dev, "no matching children\n");
|
||||
goto disable_mem_clk;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
of_platform_device_create(child, NULL, &adev->dev);
|
||||
of_node_put(child);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_mem_clk:
|
||||
clk_disable_unprepare(pl353_smc->memclk);
|
||||
disable_axi_clk:
|
||||
clk_disable_unprepare(pl353_smc->aclk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void pl353_smc_remove(struct amba_device *adev)
|
||||
{
|
||||
struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev);
|
||||
|
||||
clk_disable_unprepare(pl353_smc->memclk);
|
||||
clk_disable_unprepare(pl353_smc->aclk);
|
||||
}
|
||||
|
||||
static const struct amba_id pl353_ids[] = {
|
||||
@ -159,7 +125,6 @@ static struct amba_driver pl353_smc_driver = {
|
||||
},
|
||||
.id_table = pl353_ids,
|
||||
.probe = pl353_smc_probe,
|
||||
.remove = pl353_smc_remove,
|
||||
};
|
||||
|
||||
module_amba_driver(pl353_smc_driver);
|
||||
|
@ -367,7 +367,7 @@ int rpcif_hw_init(struct device *dev, bool hyperflash)
|
||||
regmap_update_bits(rpc->regmap, RPCIF_CMNCR,
|
||||
RPCIF_CMNCR_MOIIO(3) | RPCIF_CMNCR_IOFV(3) |
|
||||
RPCIF_CMNCR_BSZ(3),
|
||||
RPCIF_CMNCR_MOIIO(1) | RPCIF_CMNCR_IOFV(2) |
|
||||
RPCIF_CMNCR_MOIIO(1) | RPCIF_CMNCR_IOFV(3) |
|
||||
RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0));
|
||||
else
|
||||
regmap_update_bits(rpc->regmap, RPCIF_CMNCR,
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Author: Lukasz Luba <l.luba@partner.samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/devfreq-event.h>
|
||||
@ -339,19 +340,20 @@ static int exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set)
|
||||
static int exynos5_init_freq_table(struct exynos5_dmc *dmc,
|
||||
struct devfreq_dev_profile *profile)
|
||||
{
|
||||
struct device *dev = dmc->dev;
|
||||
int i, ret;
|
||||
int idx;
|
||||
unsigned long freq;
|
||||
|
||||
ret = devm_pm_opp_of_add_table(dmc->dev);
|
||||
ret = devm_pm_opp_of_add_table(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dmc->dev, "Failed to get OPP table\n");
|
||||
dev_err(dev, "Failed to get OPP table\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dmc->opp_count = dev_pm_opp_get_opp_count(dmc->dev);
|
||||
dmc->opp_count = dev_pm_opp_get_opp_count(dev);
|
||||
|
||||
dmc->opp = devm_kmalloc_array(dmc->dev, dmc->opp_count,
|
||||
dmc->opp = devm_kmalloc_array(dev, dmc->opp_count,
|
||||
sizeof(struct dmc_opp_table), GFP_KERNEL);
|
||||
if (!dmc->opp)
|
||||
return -ENOMEM;
|
||||
@ -360,7 +362,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc,
|
||||
for (i = 0, freq = ULONG_MAX; i < dmc->opp_count; i++, freq--) {
|
||||
struct dev_pm_opp *opp;
|
||||
|
||||
opp = dev_pm_opp_find_freq_floor(dmc->dev, &freq);
|
||||
opp = dev_pm_opp_find_freq_floor(dev, &freq);
|
||||
if (IS_ERR(opp))
|
||||
return PTR_ERR(opp);
|
||||
|
||||
@ -1175,51 +1177,44 @@ static int create_timings_aligned(struct exynos5_dmc *dmc, u32 *reg_timing_row,
|
||||
static int of_get_dram_timings(struct exynos5_dmc *dmc)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = dmc->dev;
|
||||
int idx;
|
||||
struct device_node *np_ddr;
|
||||
u32 freq_mhz, clk_period_ps;
|
||||
|
||||
np_ddr = of_parse_phandle(dmc->dev->of_node, "device-handle", 0);
|
||||
struct device_node *np_ddr __free(device_node) =
|
||||
of_parse_phandle(dev->of_node, "device-handle", 0);
|
||||
if (!np_ddr) {
|
||||
dev_warn(dmc->dev, "could not find 'device-handle' in DT\n");
|
||||
dev_warn(dev, "could not find 'device-handle' in DT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dmc->timing_row = devm_kmalloc_array(dmc->dev, TIMING_COUNT,
|
||||
dmc->timing_row = devm_kmalloc_array(dev, TIMING_COUNT,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!dmc->timing_row) {
|
||||
ret = -ENOMEM;
|
||||
goto put_node;
|
||||
}
|
||||
if (!dmc->timing_row)
|
||||
return -ENOMEM;
|
||||
|
||||
dmc->timing_data = devm_kmalloc_array(dmc->dev, TIMING_COUNT,
|
||||
dmc->timing_data = devm_kmalloc_array(dev, TIMING_COUNT,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!dmc->timing_data) {
|
||||
ret = -ENOMEM;
|
||||
goto put_node;
|
||||
}
|
||||
if (!dmc->timing_data)
|
||||
return -ENOMEM;
|
||||
|
||||
dmc->timing_power = devm_kmalloc_array(dmc->dev, TIMING_COUNT,
|
||||
dmc->timing_power = devm_kmalloc_array(dev, TIMING_COUNT,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!dmc->timing_power) {
|
||||
ret = -ENOMEM;
|
||||
goto put_node;
|
||||
}
|
||||
if (!dmc->timing_power)
|
||||
return -ENOMEM;
|
||||
|
||||
dmc->timings = of_lpddr3_get_ddr_timings(np_ddr, dmc->dev,
|
||||
dmc->timings = of_lpddr3_get_ddr_timings(np_ddr, dev,
|
||||
DDR_TYPE_LPDDR3,
|
||||
&dmc->timings_arr_size);
|
||||
if (!dmc->timings) {
|
||||
dev_warn(dmc->dev, "could not get timings from DT\n");
|
||||
ret = -EINVAL;
|
||||
goto put_node;
|
||||
dev_warn(dev, "could not get timings from DT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dmc->min_tck = of_lpddr3_get_min_tck(np_ddr, dmc->dev);
|
||||
dmc->min_tck = of_lpddr3_get_min_tck(np_ddr, dev);
|
||||
if (!dmc->min_tck) {
|
||||
dev_warn(dmc->dev, "could not get tck from DT\n");
|
||||
ret = -EINVAL;
|
||||
goto put_node;
|
||||
dev_warn(dev, "could not get tck from DT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Sorted array of OPPs with frequency ascending */
|
||||
@ -1239,8 +1234,6 @@ static int of_get_dram_timings(struct exynos5_dmc *dmc)
|
||||
dmc->bypass_timing_data = dmc->timing_data[idx - 1];
|
||||
dmc->bypass_timing_power = dmc->timing_power[idx - 1];
|
||||
|
||||
put_node:
|
||||
of_node_put(np_ddr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1254,34 +1247,34 @@ static int of_get_dram_timings(struct exynos5_dmc *dmc)
|
||||
static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = dmc->dev;
|
||||
unsigned long target_volt = 0;
|
||||
unsigned long target_rate = 0;
|
||||
unsigned int tmp;
|
||||
|
||||
dmc->fout_spll = devm_clk_get(dmc->dev, "fout_spll");
|
||||
dmc->fout_spll = devm_clk_get(dev, "fout_spll");
|
||||
if (IS_ERR(dmc->fout_spll))
|
||||
return PTR_ERR(dmc->fout_spll);
|
||||
|
||||
dmc->fout_bpll = devm_clk_get(dmc->dev, "fout_bpll");
|
||||
dmc->fout_bpll = devm_clk_get(dev, "fout_bpll");
|
||||
if (IS_ERR(dmc->fout_bpll))
|
||||
return PTR_ERR(dmc->fout_bpll);
|
||||
|
||||
dmc->mout_mclk_cdrex = devm_clk_get(dmc->dev, "mout_mclk_cdrex");
|
||||
dmc->mout_mclk_cdrex = devm_clk_get(dev, "mout_mclk_cdrex");
|
||||
if (IS_ERR(dmc->mout_mclk_cdrex))
|
||||
return PTR_ERR(dmc->mout_mclk_cdrex);
|
||||
|
||||
dmc->mout_bpll = devm_clk_get(dmc->dev, "mout_bpll");
|
||||
dmc->mout_bpll = devm_clk_get(dev, "mout_bpll");
|
||||
if (IS_ERR(dmc->mout_bpll))
|
||||
return PTR_ERR(dmc->mout_bpll);
|
||||
|
||||
dmc->mout_mx_mspll_ccore = devm_clk_get(dmc->dev,
|
||||
"mout_mx_mspll_ccore");
|
||||
dmc->mout_mx_mspll_ccore = devm_clk_get(dev, "mout_mx_mspll_ccore");
|
||||
if (IS_ERR(dmc->mout_mx_mspll_ccore))
|
||||
return PTR_ERR(dmc->mout_mx_mspll_ccore);
|
||||
|
||||
dmc->mout_spll = devm_clk_get(dmc->dev, "ff_dout_spll2");
|
||||
dmc->mout_spll = devm_clk_get(dev, "ff_dout_spll2");
|
||||
if (IS_ERR(dmc->mout_spll)) {
|
||||
dmc->mout_spll = devm_clk_get(dmc->dev, "mout_sclk_spll");
|
||||
dmc->mout_spll = devm_clk_get(dev, "mout_sclk_spll");
|
||||
if (IS_ERR(dmc->mout_spll))
|
||||
return PTR_ERR(dmc->mout_spll);
|
||||
}
|
||||
@ -1329,38 +1322,37 @@ static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc)
|
||||
*/
|
||||
static int exynos5_performance_counters_init(struct exynos5_dmc *dmc)
|
||||
{
|
||||
struct device *dev = dmc->dev;
|
||||
int ret, i;
|
||||
|
||||
dmc->num_counters = devfreq_event_get_edev_count(dmc->dev,
|
||||
"devfreq-events");
|
||||
dmc->num_counters = devfreq_event_get_edev_count(dev, "devfreq-events");
|
||||
if (dmc->num_counters < 0) {
|
||||
dev_err(dmc->dev, "could not get devfreq-event counters\n");
|
||||
dev_err(dev, "could not get devfreq-event counters\n");
|
||||
return dmc->num_counters;
|
||||
}
|
||||
|
||||
dmc->counter = devm_kcalloc(dmc->dev, dmc->num_counters,
|
||||
dmc->counter = devm_kcalloc(dev, dmc->num_counters,
|
||||
sizeof(*dmc->counter), GFP_KERNEL);
|
||||
if (!dmc->counter)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < dmc->num_counters; i++) {
|
||||
dmc->counter[i] =
|
||||
devfreq_event_get_edev_by_phandle(dmc->dev,
|
||||
"devfreq-events", i);
|
||||
devfreq_event_get_edev_by_phandle(dev, "devfreq-events", i);
|
||||
if (IS_ERR_OR_NULL(dmc->counter[i]))
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ret = exynos5_counters_enable_edev(dmc);
|
||||
if (ret < 0) {
|
||||
dev_err(dmc->dev, "could not enable event counter\n");
|
||||
dev_err(dev, "could not enable event counter\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = exynos5_counters_set_event(dmc);
|
||||
if (ret < 0) {
|
||||
exynos5_counters_disable_edev(dmc);
|
||||
dev_err(dmc->dev, "could not set event counter\n");
|
||||
dev_err(dev, "could not set event counter\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1573,29 +1573,22 @@ static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
|
||||
static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi)
|
||||
{
|
||||
struct device *dev = ebi->dev;
|
||||
struct device_node *child;
|
||||
bool child_found = false;
|
||||
u32 bank;
|
||||
int ret;
|
||||
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
for_each_available_child_of_node_scoped(dev->of_node, child) {
|
||||
ret = of_property_read_u32(child, "reg", &bank);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not retrieve reg property: %d\n",
|
||||
ret);
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "could not retrieve reg property\n");
|
||||
|
||||
if (bank >= FMC2_MAX_BANKS) {
|
||||
dev_err(dev, "invalid reg value: %d\n", bank);
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ebi->bank_assigned & BIT(bank)) {
|
||||
dev_err(dev, "bank already assigned: %d\n", bank);
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1603,19 +1596,15 @@ static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi)
|
||||
ret = ebi->data->check_rif(ebi, bank + 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "bank access failed: %d\n", bank);
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (bank < FMC2_MAX_EBI_CE) {
|
||||
ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank);
|
||||
if (ret) {
|
||||
dev_err(dev, "setup chip select %d failed: %d\n",
|
||||
bank, ret);
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"setup chip select %d failed\n", bank);
|
||||
}
|
||||
|
||||
ebi->bank_assigned |= BIT(bank);
|
||||
|
@ -450,7 +450,6 @@ static int load_one_timing(struct tegra_mc *mc,
|
||||
|
||||
static int load_timings(struct tegra_mc *mc, struct device_node *node)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct tegra_mc_timing *timing;
|
||||
int child_count = of_get_child_count(node);
|
||||
int i = 0, err;
|
||||
@ -462,14 +461,12 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node)
|
||||
|
||||
mc->num_timings = child_count;
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
for_each_child_of_node_scoped(node, child) {
|
||||
timing = &mc->timings[i++];
|
||||
|
||||
err = load_one_timing(mc, timing, child);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -477,7 +474,6 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node)
|
||||
|
||||
static int tegra_mc_setup_timings(struct tegra_mc *mc)
|
||||
{
|
||||
struct device_node *node;
|
||||
u32 ram_code, node_ram_code;
|
||||
int err;
|
||||
|
||||
@ -485,14 +481,13 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc)
|
||||
|
||||
mc->num_timings = 0;
|
||||
|
||||
for_each_child_of_node(mc->dev->of_node, node) {
|
||||
for_each_child_of_node_scoped(mc->dev->of_node, node) {
|
||||
err = of_property_read_u32(node, "nvidia,ram-code",
|
||||
&node_ram_code);
|
||||
if (err || (node_ram_code != ram_code))
|
||||
continue;
|
||||
|
||||
err = load_timings(mc, node);
|
||||
of_node_put(node);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
|
@ -992,7 +992,6 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
struct device_node *node)
|
||||
{
|
||||
int child_count = of_get_child_count(node);
|
||||
struct device_node *child;
|
||||
struct emc_timing *timing;
|
||||
unsigned int i = 0;
|
||||
int err;
|
||||
@ -1004,14 +1003,12 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
|
||||
emc->num_timings = child_count;
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
for_each_child_of_node_scoped(node, child) {
|
||||
timing = &emc->timings[i++];
|
||||
|
||||
err = load_one_timing_from_dt(emc, timing, child);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
|
||||
|
@ -35,11 +35,6 @@ struct tegra186_emc {
|
||||
struct icc_provider provider;
|
||||
};
|
||||
|
||||
static inline struct tegra186_emc *to_tegra186_emc(struct icc_provider *provider)
|
||||
{
|
||||
return container_of(provider, struct tegra186_emc, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs interface
|
||||
*
|
||||
|
@ -410,7 +410,6 @@ static int cmp_timings(const void *_a, const void *_b)
|
||||
static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct emc_timing *timing;
|
||||
int child_count;
|
||||
int err;
|
||||
@ -428,15 +427,13 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
|
||||
timing = emc->timings;
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
for_each_child_of_node_scoped(node, child) {
|
||||
if (of_node_name_eq(child, "lpddr2"))
|
||||
continue;
|
||||
|
||||
err = load_one_timing_from_dt(emc, timing++, child);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
emc->num_timings++;
|
||||
}
|
||||
|
@ -75,29 +75,29 @@ enum {
|
||||
* The division portion of the average operation.
|
||||
*/
|
||||
#define __AVERAGE_PTFV(dev) \
|
||||
({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] = \
|
||||
next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \
|
||||
({ next->ptfv_list[(dev)] = \
|
||||
next->ptfv_list[(dev)] / \
|
||||
next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
|
||||
|
||||
/*
|
||||
* Convert val to fixed point and add it to the temporary average.
|
||||
*/
|
||||
#define __INCREMENT_PTFV(dev, val) \
|
||||
({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] += \
|
||||
({ next->ptfv_list[(dev)] += \
|
||||
((val) * MOVAVG_PRECISION_FACTOR); })
|
||||
|
||||
/*
|
||||
* Convert a moving average back to integral form and return the value.
|
||||
*/
|
||||
#define __MOVAVG_AC(timing, dev) \
|
||||
((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \
|
||||
((timing)->ptfv_list[(dev)] / \
|
||||
MOVAVG_PRECISION_FACTOR)
|
||||
|
||||
/* Weighted update. */
|
||||
#define __WEIGHTED_UPDATE_PTFV(dev, nval) \
|
||||
do { \
|
||||
int w = PTFV_MOVAVG_WEIGHT_INDEX; \
|
||||
int dqs = PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX; \
|
||||
int dqs = (dev); \
|
||||
\
|
||||
next->ptfv_list[dqs] = \
|
||||
((nval * MOVAVG_PRECISION_FACTOR) + \
|
||||
@ -105,315 +105,91 @@ enum {
|
||||
next->ptfv_list[w])) / \
|
||||
(next->ptfv_list[w] + 1); \
|
||||
\
|
||||
emc_dbg(emc, EMA_UPDATES, "%s: (s=%lu) EMA: %u\n", \
|
||||
emc_dbg(emc, EMA_UPDATES, "%s: (s=%u) EMA: %u\n", \
|
||||
__stringify(dev), nval, next->ptfv_list[dqs]); \
|
||||
} while (0)
|
||||
|
||||
/* Access a particular average. */
|
||||
#define __MOVAVG(timing, dev) \
|
||||
((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX])
|
||||
((timing)->ptfv_list[(dev)])
|
||||
|
||||
static u32 update_clock_tree_delay(struct tegra210_emc *emc, int type)
|
||||
static bool tegra210_emc_compare_update_delay(struct tegra210_emc_timing *timing,
|
||||
u32 measured, u32 idx)
|
||||
{
|
||||
bool periodic_training_update = type == PERIODIC_TRAINING_UPDATE;
|
||||
struct tegra210_emc_timing *last = emc->last;
|
||||
struct tegra210_emc_timing *next = emc->next;
|
||||
u32 last_timing_rate_mhz = last->rate / 1000;
|
||||
u32 next_timing_rate_mhz = next->rate / 1000;
|
||||
bool dvfs_update = type == DVFS_UPDATE;
|
||||
s32 tdel = 0, tmdel = 0, adel = 0;
|
||||
bool dvfs_pt1 = type == DVFS_PT1;
|
||||
unsigned long cval = 0;
|
||||
u32 temp[2][2], value;
|
||||
unsigned int i;
|
||||
u32 *curr = &timing->current_dram_clktree[idx];
|
||||
u32 rate_mhz = timing->rate / 1000;
|
||||
u32 tmdel;
|
||||
|
||||
/*
|
||||
* Dev0 MSB.
|
||||
*/
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
value = tegra210_emc_mrr_read(emc, 2, 19);
|
||||
tmdel = abs(*curr - measured);
|
||||
|
||||
for (i = 0; i < emc->num_channels; i++) {
|
||||
temp[i][0] = (value & 0x00ff) << 8;
|
||||
temp[i][1] = (value & 0xff00) << 0;
|
||||
value >>= 16;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dev0 LSB.
|
||||
*/
|
||||
value = tegra210_emc_mrr_read(emc, 2, 18);
|
||||
|
||||
for (i = 0; i < emc->num_channels; i++) {
|
||||
temp[i][0] |= (value & 0x00ff) >> 0;
|
||||
temp[i][1] |= (value & 0xff00) >> 8;
|
||||
value >>= 16;
|
||||
}
|
||||
if (tmdel * 128 * rate_mhz / 1000000 > timing->tree_margin) {
|
||||
*curr = measured;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[0][0];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C0D0U0, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C0D0U0);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C0D0U0, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C0D0U0] -
|
||||
__MOVAVG_AC(next, C0D0U0);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C0D0U0] =
|
||||
__MOVAVG_AC(next, C0D0U0);
|
||||
}
|
||||
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[0][1];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C0D0U1, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C0D0U1);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C0D0U1, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C0D0U1] -
|
||||
__MOVAVG_AC(next, C0D0U1);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
|
||||
if (tmdel > adel)
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C0D0U1] =
|
||||
__MOVAVG_AC(next, C0D0U1);
|
||||
}
|
||||
|
||||
if (emc->num_channels > 1) {
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[1][0];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C1D0U0, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C1D0U0);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C1D0U0, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C1D0U0] -
|
||||
__MOVAVG_AC(next, C1D0U0);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
|
||||
if (tmdel > adel)
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C1D0U0] =
|
||||
__MOVAVG_AC(next, C1D0U0);
|
||||
}
|
||||
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[1][1];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C1D0U1, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C1D0U1);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C1D0U1, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C1D0U1] -
|
||||
__MOVAVG_AC(next, C1D0U1);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
|
||||
if (tmdel > adel)
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C1D0U1] =
|
||||
__MOVAVG_AC(next, C1D0U1);
|
||||
}
|
||||
}
|
||||
|
||||
if (emc->num_devices < 2)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* Dev1 MSB.
|
||||
*/
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
value = tegra210_emc_mrr_read(emc, 1, 19);
|
||||
|
||||
for (i = 0; i < emc->num_channels; i++) {
|
||||
temp[i][0] = (value & 0x00ff) << 8;
|
||||
temp[i][1] = (value & 0xff00) << 0;
|
||||
value >>= 16;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dev1 LSB.
|
||||
*/
|
||||
value = tegra210_emc_mrr_read(emc, 1, 18);
|
||||
|
||||
for (i = 0; i < emc->num_channels; i++) {
|
||||
temp[i][0] |= (value & 0x00ff) >> 0;
|
||||
temp[i][1] |= (value & 0xff00) >> 8;
|
||||
value >>= 16;
|
||||
}
|
||||
}
|
||||
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[0][0];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C0D1U0, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C0D1U0);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C0D1U0, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C0D1U0] -
|
||||
__MOVAVG_AC(next, C0D1U0);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
|
||||
if (tmdel > adel)
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C0D1U0] =
|
||||
__MOVAVG_AC(next, C0D1U0);
|
||||
}
|
||||
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[0][1];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C0D1U1, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C0D1U1);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C0D1U1, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C0D1U1] -
|
||||
__MOVAVG_AC(next, C0D1U1);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
|
||||
if (tmdel > adel)
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C0D1U1] =
|
||||
__MOVAVG_AC(next, C0D1U1);
|
||||
}
|
||||
|
||||
if (emc->num_channels > 1) {
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[1][0];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C1D1U0, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C1D1U0);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C1D1U0, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C1D1U0] -
|
||||
__MOVAVG_AC(next, C1D1U0);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
|
||||
if (tmdel > adel)
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C1D1U0] =
|
||||
__MOVAVG_AC(next, C1D1U0);
|
||||
}
|
||||
|
||||
if (dvfs_pt1 || periodic_training_update) {
|
||||
cval = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
cval *= 1000000;
|
||||
cval /= last_timing_rate_mhz * 2 * temp[1][1];
|
||||
}
|
||||
|
||||
if (dvfs_pt1)
|
||||
__INCREMENT_PTFV(C1D1U1, cval);
|
||||
else if (dvfs_update)
|
||||
__AVERAGE_PTFV(C1D1U1);
|
||||
else if (periodic_training_update)
|
||||
__WEIGHTED_UPDATE_PTFV(C1D1U1, cval);
|
||||
|
||||
if (dvfs_update || periodic_training_update) {
|
||||
tdel = next->current_dram_clktree[C1D1U1] -
|
||||
__MOVAVG_AC(next, C1D1U1);
|
||||
tmdel = (tdel < 0) ? -1 * tdel : tdel;
|
||||
|
||||
if (tmdel > adel)
|
||||
adel = tmdel;
|
||||
|
||||
if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
|
||||
next->tree_margin)
|
||||
next->current_dram_clktree[C1D1U1] =
|
||||
__MOVAVG_AC(next, C1D1U1);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return adel;
|
||||
return false;
|
||||
}
|
||||
|
||||
static u32 periodic_compensation_handler(struct tegra210_emc *emc, u32 type,
|
||||
struct tegra210_emc_timing *last,
|
||||
struct tegra210_emc_timing *next)
|
||||
static void tegra210_emc_get_clktree_delay(struct tegra210_emc *emc,
|
||||
u32 delay[DRAM_CLKTREE_NUM])
|
||||
{
|
||||
struct tegra210_emc_timing *curr = emc->last;
|
||||
u32 rate_mhz = curr->rate / 1000;
|
||||
u32 msb, lsb, dqsosc, delay_us;
|
||||
unsigned int c, d, idx;
|
||||
unsigned long clocks;
|
||||
|
||||
clocks = tegra210_emc_actual_osc_clocks(curr->run_clocks);
|
||||
delay_us = 2 + (clocks / rate_mhz);
|
||||
|
||||
tegra210_emc_start_periodic_compensation(emc);
|
||||
udelay(delay_us);
|
||||
|
||||
for (d = 0; d < emc->num_devices; d++) {
|
||||
/* Read DQSOSC from MRR18/19 */
|
||||
msb = tegra210_emc_mrr_read(emc, 2 - d, 19);
|
||||
lsb = tegra210_emc_mrr_read(emc, 2 - d, 18);
|
||||
|
||||
for (c = 0; c < emc->num_channels; c++) {
|
||||
/* C[c]D[d]U[0] */
|
||||
idx = c * 4 + d * 2;
|
||||
|
||||
dqsosc = (msb & 0x00ff) << 8;
|
||||
dqsosc |= (lsb & 0x00ff) >> 0;
|
||||
|
||||
/* Check for unpopulated channels */
|
||||
if (dqsosc)
|
||||
delay[idx] = (clocks * 1000000) /
|
||||
(rate_mhz * 2 * dqsosc);
|
||||
|
||||
/* C[c]D[d]U[1] */
|
||||
idx++;
|
||||
|
||||
dqsosc = (msb & 0xff00) << 0;
|
||||
dqsosc |= (lsb & 0xff00) >> 8;
|
||||
|
||||
/* Check for unpopulated channels */
|
||||
if (dqsosc)
|
||||
delay[idx] = (clocks * 1000000) /
|
||||
(rate_mhz * 2 * dqsosc);
|
||||
|
||||
msb >>= 16;
|
||||
lsb >>= 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool periodic_compensation_handler(struct tegra210_emc *emc, u32 type,
|
||||
struct tegra210_emc_timing *last,
|
||||
struct tegra210_emc_timing *next)
|
||||
{
|
||||
#define __COPY_EMA(nt, lt, dev) \
|
||||
({ __MOVAVG(nt, dev) = __MOVAVG(lt, dev) * \
|
||||
(nt)->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
|
||||
|
||||
u32 i, adel = 0, samples = next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX];
|
||||
u32 delay;
|
||||
|
||||
delay = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
delay *= 1000;
|
||||
delay = 2 + (delay / last->rate);
|
||||
u32 i, samples = next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX];
|
||||
u32 delay[DRAM_CLKTREE_NUM], idx;
|
||||
bool over = false;
|
||||
|
||||
if (!next->periodic_training)
|
||||
return 0;
|
||||
@ -427,57 +203,46 @@ static u32 periodic_compensation_handler(struct tegra210_emc *emc, u32 type,
|
||||
* calibration then we can reuse the previous
|
||||
* frequencies EMA data.
|
||||
*/
|
||||
__COPY_EMA(next, last, C0D0U0);
|
||||
__COPY_EMA(next, last, C0D0U1);
|
||||
__COPY_EMA(next, last, C1D0U0);
|
||||
__COPY_EMA(next, last, C1D0U1);
|
||||
__COPY_EMA(next, last, C0D1U0);
|
||||
__COPY_EMA(next, last, C0D1U1);
|
||||
__COPY_EMA(next, last, C1D1U0);
|
||||
__COPY_EMA(next, last, C1D1U1);
|
||||
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++)
|
||||
__COPY_EMA(next, last, idx);
|
||||
} else {
|
||||
/* Reset the EMA.*/
|
||||
__MOVAVG(next, C0D0U0) = 0;
|
||||
__MOVAVG(next, C0D0U1) = 0;
|
||||
__MOVAVG(next, C1D0U0) = 0;
|
||||
__MOVAVG(next, C1D0U1) = 0;
|
||||
__MOVAVG(next, C0D1U0) = 0;
|
||||
__MOVAVG(next, C0D1U1) = 0;
|
||||
__MOVAVG(next, C1D1U0) = 0;
|
||||
__MOVAVG(next, C1D1U1) = 0;
|
||||
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++)
|
||||
__MOVAVG(next, idx) = 0;
|
||||
|
||||
for (i = 0; i < samples; i++) {
|
||||
tegra210_emc_start_periodic_compensation(emc);
|
||||
udelay(delay);
|
||||
/* Generate next sample of data. */
|
||||
tegra210_emc_get_clktree_delay(emc, delay);
|
||||
|
||||
/*
|
||||
* Generate next sample of data.
|
||||
*/
|
||||
adel = update_clock_tree_delay(emc, DVFS_PT1);
|
||||
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++)
|
||||
__INCREMENT_PTFV(idx, delay[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Seems like it should be part of the
|
||||
* 'if (last_timing->periodic_training)' conditional
|
||||
* since is already done for the else clause.
|
||||
*/
|
||||
adel = update_clock_tree_delay(emc, DVFS_UPDATE);
|
||||
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++) {
|
||||
/* Do the division part of the moving average */
|
||||
__AVERAGE_PTFV(idx);
|
||||
over |= tegra210_emc_compare_update_delay(next,
|
||||
__MOVAVG_AC(next, idx), idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == PERIODIC_TRAINING_SEQUENCE) {
|
||||
tegra210_emc_start_periodic_compensation(emc);
|
||||
udelay(delay);
|
||||
tegra210_emc_get_clktree_delay(emc, delay);
|
||||
|
||||
adel = update_clock_tree_delay(emc, PERIODIC_TRAINING_UPDATE);
|
||||
for (idx = 0; idx < DRAM_CLKTREE_NUM; idx++) {
|
||||
__WEIGHTED_UPDATE_PTFV(idx, delay[idx]);
|
||||
over |= tegra210_emc_compare_update_delay(next,
|
||||
__MOVAVG_AC(next, idx), idx);
|
||||
}
|
||||
}
|
||||
|
||||
return adel;
|
||||
return over;
|
||||
}
|
||||
|
||||
static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc)
|
||||
{
|
||||
u32 emc_cfg, emc_cfg_o, emc_cfg_update, del, value;
|
||||
u32 emc_cfg, emc_cfg_o, emc_cfg_update, value;
|
||||
static const u32 list[] = {
|
||||
EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0,
|
||||
EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1,
|
||||
@ -492,7 +257,6 @@ static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc)
|
||||
};
|
||||
struct tegra210_emc_timing *last = emc->last;
|
||||
unsigned int items = ARRAY_SIZE(list), i;
|
||||
unsigned long delay;
|
||||
|
||||
if (last->periodic_training) {
|
||||
emc_dbg(emc, PER_TRAIN, "Periodic training starting\n");
|
||||
@ -530,30 +294,18 @@ static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc)
|
||||
/*
|
||||
* 2. osc kick off - this assumes training and dvfs have set
|
||||
* correct MR23.
|
||||
*/
|
||||
tegra210_emc_start_periodic_compensation(emc);
|
||||
|
||||
/*
|
||||
*
|
||||
* 3. Let dram capture its clock tree delays.
|
||||
*/
|
||||
delay = tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
delay *= 1000;
|
||||
delay /= last->rate + 1;
|
||||
udelay(delay);
|
||||
|
||||
/*
|
||||
*
|
||||
* 4. Check delta wrt previous values (save value if margin
|
||||
* exceeds what is set in table).
|
||||
*/
|
||||
del = periodic_compensation_handler(emc,
|
||||
PERIODIC_TRAINING_SEQUENCE,
|
||||
last, last);
|
||||
|
||||
if (periodic_compensation_handler(emc, PERIODIC_TRAINING_SEQUENCE,
|
||||
last, last)) {
|
||||
/*
|
||||
* 5. Apply compensation w.r.t. trained values (if clock tree
|
||||
* has drifted more than the set margin).
|
||||
*/
|
||||
if (last->tree_margin < ((del * 128 * (last->rate / 1000)) / 1000000)) {
|
||||
for (i = 0; i < items; i++) {
|
||||
value = tegra210_emc_compensate(last, list[i]);
|
||||
emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n",
|
||||
@ -734,16 +486,7 @@ static void tegra210_emc_r21021_set_clock(struct tegra210_emc *emc, u32 clksrc)
|
||||
EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK,
|
||||
0);
|
||||
|
||||
tegra210_emc_start_periodic_compensation(emc);
|
||||
|
||||
delay = 1000 * tegra210_emc_actual_osc_clocks(last->run_clocks);
|
||||
udelay((delay / last->rate) + 2);
|
||||
|
||||
value = periodic_compensation_handler(emc, DVFS_SEQUENCE, fake,
|
||||
next);
|
||||
value = (value * 128 * next->rate / 1000) / 1000000;
|
||||
|
||||
if (next->periodic_training && value > next->tree_margin)
|
||||
if (periodic_compensation_handler(emc, DVFS_SEQUENCE, fake, next))
|
||||
compensate_trimmer_applicable = true;
|
||||
}
|
||||
|
||||
|
@ -979,7 +979,6 @@ static int emc_check_mc_timings(struct tegra_emc *emc)
|
||||
static int emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct emc_timing *timing;
|
||||
int child_count;
|
||||
int err;
|
||||
@ -998,12 +997,10 @@ static int emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
emc->num_timings = child_count;
|
||||
timing = emc->timings;
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
for_each_child_of_node_scoped(node, child) {
|
||||
err = load_one_timing_from_dt(emc, timing++, child);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/ti-aemif.h>
|
||||
|
||||
#define TA_SHIFT 2
|
||||
#define RHOLD_SHIFT 4
|
||||
@ -330,42 +329,27 @@ static int aemif_probe(struct platform_device *pdev)
|
||||
int ret = -ENODEV;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child_np;
|
||||
struct aemif_device *aemif;
|
||||
struct aemif_platform_data *pdata;
|
||||
struct of_dev_auxdata *dev_lookup;
|
||||
|
||||
aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL);
|
||||
if (!aemif)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
dev_lookup = pdata ? pdata->dev_lookup : NULL;
|
||||
|
||||
platform_set_drvdata(pdev, aemif);
|
||||
|
||||
aemif->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(aemif->clk)) {
|
||||
dev_err(dev, "cannot get clock 'aemif'\n");
|
||||
return PTR_ERR(aemif->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(aemif->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
aemif->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(aemif->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(aemif->clk),
|
||||
"cannot get clock 'aemif'\n");
|
||||
|
||||
aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
|
||||
|
||||
if (np && of_device_is_compatible(np, "ti,da850-aemif"))
|
||||
aemif->cs_offset = 2;
|
||||
else if (pdata)
|
||||
aemif->cs_offset = pdata->cs_offset;
|
||||
|
||||
aemif->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(aemif->base)) {
|
||||
ret = PTR_ERR(aemif->base);
|
||||
goto error;
|
||||
}
|
||||
if (IS_ERR(aemif->base))
|
||||
return PTR_ERR(aemif->base);
|
||||
|
||||
if (np) {
|
||||
/*
|
||||
@ -374,17 +358,10 @@ static int aemif_probe(struct platform_device *pdev)
|
||||
* functions iterate over these nodes and update the cs data
|
||||
* array.
|
||||
*/
|
||||
for_each_available_child_of_node(np, child_np) {
|
||||
for_each_available_child_of_node_scoped(np, child_np) {
|
||||
ret = of_aemif_parse_abus_config(pdev, child_np);
|
||||
if (ret < 0) {
|
||||
of_node_put(child_np);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
} else if (pdata && pdata->num_abus_data > 0) {
|
||||
for (i = 0; i < pdata->num_abus_data; i++, aemif->num_cs++) {
|
||||
aemif->cs_data[i].cs = pdata->abus_data[i].cs;
|
||||
aemif_get_hw_params(pdev, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,7 +370,7 @@ static int aemif_probe(struct platform_device *pdev)
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error configuring chip select %d\n",
|
||||
aemif->cs_data[i].cs);
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,41 +379,18 @@ static int aemif_probe(struct platform_device *pdev)
|
||||
* child will be probed after the AEMIF timing parameters are set.
|
||||
*/
|
||||
if (np) {
|
||||
for_each_available_child_of_node(np, child_np) {
|
||||
ret = of_platform_populate(child_np, NULL,
|
||||
dev_lookup, dev);
|
||||
if (ret < 0) {
|
||||
of_node_put(child_np);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
} else if (pdata) {
|
||||
for (i = 0; i < pdata->num_sub_devices; i++) {
|
||||
pdata->sub_devices[i].dev.parent = dev;
|
||||
ret = platform_device_register(&pdata->sub_devices[i]);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Error register sub device %s\n",
|
||||
pdata->sub_devices[i].name);
|
||||
}
|
||||
for_each_available_child_of_node_scoped(np, child_np) {
|
||||
ret = of_platform_populate(child_np, NULL, NULL, dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
clk_disable_unprepare(aemif->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aemif_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aemif_device *aemif = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(aemif->clk);
|
||||
}
|
||||
|
||||
static struct platform_driver aemif_driver = {
|
||||
.probe = aemif_probe,
|
||||
.remove_new = aemif_remove,
|
||||
.driver = {
|
||||
.name = "ti-aemif",
|
||||
.of_match_table = of_match_ptr(aemif_of_match),
|
||||
|
@ -70,8 +70,8 @@ int omnia_mcu_register_trng(struct omnia_mcu *mcu)
|
||||
|
||||
irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)];
|
||||
irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx));
|
||||
if (!irq)
|
||||
return dev_err_probe(dev, -ENXIO, "Cannot get TRNG IRQ\n");
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, irq, "Cannot get TRNG IRQ\n");
|
||||
|
||||
/*
|
||||
* If someone else cleared the TRNG interrupt but did not read the
|
||||
|
@ -66,6 +66,19 @@ config RESET_BRCMSTB_RESCAL
|
||||
This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on
|
||||
BCM7216.
|
||||
|
||||
config RESET_EYEQ
|
||||
bool "Mobileye EyeQ reset controller"
|
||||
depends on MACH_EYEQ5 || MACH_EYEQ6H || COMPILE_TEST
|
||||
select AUXILIARY_BUS
|
||||
default MACH_EYEQ5 || MACH_EYEQ6H
|
||||
help
|
||||
This enables the Mobileye EyeQ reset controller, used in EyeQ5, EyeQ6L
|
||||
and EyeQ6H SoCs.
|
||||
|
||||
It has one or more domains, with a varying number of resets in each.
|
||||
Registers are located in a shared register region called OLB. EyeQ6H
|
||||
has multiple reset instances.
|
||||
|
||||
config RESET_GPIO
|
||||
tristate "GPIO reset controller"
|
||||
depends on GPIOLIB
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
|
||||
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
|
||||
obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
|
||||
obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
|
||||
obj-$(CONFIG_RESET_EYEQ) += reset-eyeq.o
|
||||
obj-$(CONFIG_RESET_GPIO) += reset-gpio.o
|
||||
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
|
||||
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
|
||||
|
@ -812,6 +812,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
|
||||
kref_init(&rstc->refcnt);
|
||||
rstc->acquired = acquired;
|
||||
rstc->shared = shared;
|
||||
get_device(rcdev->dev);
|
||||
|
||||
return rstc;
|
||||
}
|
||||
@ -826,6 +827,7 @@ static void __reset_control_release(struct kref *kref)
|
||||
module_put(rstc->rcdev->owner);
|
||||
|
||||
list_del(&rstc->list);
|
||||
put_device(rstc->rcdev->dev);
|
||||
kfree(rstc);
|
||||
}
|
||||
|
||||
@ -916,20 +918,18 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
||||
*/
|
||||
lockdep_assert_not_held(&reset_list_mutex);
|
||||
|
||||
mutex_lock(&reset_gpio_lookup_mutex);
|
||||
guard(mutex)(&reset_gpio_lookup_mutex);
|
||||
|
||||
list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) {
|
||||
if (args->np == rgpio_dev->of_args.np) {
|
||||
if (of_phandle_args_equal(args, &rgpio_dev->of_args))
|
||||
goto out; /* Already on the list, done */
|
||||
return 0; /* Already on the list, done */
|
||||
}
|
||||
}
|
||||
|
||||
id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
ret = id;
|
||||
goto err_unlock;
|
||||
}
|
||||
if (id < 0)
|
||||
return id;
|
||||
|
||||
/* Not freed on success, because it is persisent subsystem data. */
|
||||
rgpio_dev = kzalloc(sizeof(*rgpio_dev), GFP_KERNEL);
|
||||
@ -959,9 +959,6 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
||||
|
||||
list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
|
||||
|
||||
out:
|
||||
mutex_unlock(&reset_gpio_lookup_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put:
|
||||
@ -970,8 +967,6 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
||||
kfree(rgpio_dev);
|
||||
err_ida_free:
|
||||
ida_free(&reset_gpio_ida, id);
|
||||
err_unlock:
|
||||
mutex_unlock(&reset_gpio_lookup_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -68,13 +68,14 @@ static int berlin_reset_xlate(struct reset_controller_dev *rcdev,
|
||||
|
||||
static int berlin2_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
|
||||
struct device_node *parent_np;
|
||||
struct berlin_reset_priv *priv;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
parent_np = of_get_parent(pdev->dev.of_node);
|
||||
priv->regmap = syscon_node_to_regmap(parent_np);
|
||||
of_node_put(parent_np);
|
||||
if (IS_ERR(priv->regmap))
|
||||
|
570
drivers/reset/reset-eyeq.c
Normal file
570
drivers/reset/reset-eyeq.c
Normal file
@ -0,0 +1,570 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Reset driver for the Mobileye EyeQ5, EyeQ6L and EyeQ6H platforms.
|
||||
*
|
||||
* Controllers live in a shared register region called OLB. EyeQ5 and EyeQ6L
|
||||
* have a single OLB instance for a single reset controller. EyeQ6H has seven
|
||||
* OLB instances; three host reset controllers.
|
||||
*
|
||||
* Each reset controller has one or more domain. Domains are of a given type
|
||||
* (see enum eqr_domain_type), with a valid offset mask (up to 32 resets per
|
||||
* domain).
|
||||
*
|
||||
* Domain types define expected behavior: one-register-per-reset,
|
||||
* one-bit-per-reset, status detection method, busywait duration, etc.
|
||||
*
|
||||
* We use eqr_ as prefix, as-in "EyeQ Reset", but way shorter.
|
||||
*
|
||||
* Known resets in EyeQ5 domain 0 (type EQR_EYEQ5_SARCR):
|
||||
* 3. CAN0 4. CAN1 5. CAN2 6. SPI0
|
||||
* 7. SPI1 8. SPI2 9. SPI3 10. UART0
|
||||
* 11. UART1 12. UART2 13. I2C0 14. I2C1
|
||||
* 15. I2C2 16. I2C3 17. I2C4 18. TIMER0
|
||||
* 19. TIMER1 20. TIMER2 21. TIMER3 22. TIMER4
|
||||
* 23. WD0 24. EXT0 25. EXT1 26. GPIO
|
||||
* 27. WD1
|
||||
*
|
||||
* Known resets in EyeQ5 domain 1 (type EQR_EYEQ5_ACRP):
|
||||
* 0. VMP0 1. VMP1 2. VMP2 3. VMP3
|
||||
* 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1
|
||||
* 8. MPC0 9. MPC1 10. MPC2 11. MPC3
|
||||
* 12. MPC4
|
||||
*
|
||||
* Known resets in EyeQ5 domain 2 (type EQR_EYEQ5_PCIE):
|
||||
* 0. PCIE0_CORE 1. PCIE0_APB 2. PCIE0_LINK_AXI 3. PCIE0_LINK_MGMT
|
||||
* 4. PCIE0_LINK_HOT 5. PCIE0_LINK_PIPE 6. PCIE1_CORE 7. PCIE1_APB
|
||||
* 8. PCIE1_LINK_AXI 9. PCIE1_LINK_MGMT 10. PCIE1_LINK_HOT 11. PCIE1_LINK_PIPE
|
||||
* 12. MULTIPHY 13. MULTIPHY_APB 15. PCIE0_LINK_MGMT 16. PCIE1_LINK_MGMT
|
||||
* 17. PCIE0_LINK_PM 18. PCIE1_LINK_PM
|
||||
*
|
||||
* Known resets in EyeQ6L domain 0 (type EQR_EYEQ5_SARCR):
|
||||
* 0. SPI0 1. SPI1 2. UART0 3. I2C0
|
||||
* 4. I2C1 5. TIMER0 6. TIMER1 7. TIMER2
|
||||
* 8. TIMER3 9. WD0 10. WD1 11. EXT0
|
||||
* 12. EXT1 13. GPIO
|
||||
*
|
||||
* Known resets in EyeQ6L domain 1 (type EQR_EYEQ5_ACRP):
|
||||
* 0. VMP0 1. VMP1 2. VMP2 3. VMP3
|
||||
* 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1
|
||||
* 8. MPC0 9. MPC1 10. MPC2 11. MPC3
|
||||
* 12. MPC4
|
||||
*
|
||||
* Known resets in EyeQ6H west/east (type EQR_EYEQ6H_SARCR):
|
||||
* 0. CAN 1. SPI0 2. SPI1 3. UART0
|
||||
* 4. UART1 5. I2C0 6. I2C1 7. -hole-
|
||||
* 8. TIMER0 9. TIMER1 10. WD 11. EXT TIMER
|
||||
* 12. GPIO
|
||||
*
|
||||
* Known resets in EyeQ6H acc (type EQR_EYEQ5_ACRP):
|
||||
* 1. XNN0 2. XNN1 3. XNN2 4. XNN3
|
||||
* 5. VMP0 6. VMP1 7. VMP2 8. VMP3
|
||||
* 9. PMA0 10. PMA1 11. MPC0 12. MPC1
|
||||
* 13. MPC2 14. MPC3 15. PERIPH
|
||||
*
|
||||
* Abbreviations:
|
||||
* - PMA: Programmable Macro Array
|
||||
* - MPC: Multi-threaded Processing Clusters
|
||||
* - VMP: Vector Microcode Processors
|
||||
*
|
||||
* Copyright (C) 2024 Mobileye Vision Technologies Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* A reset ID, as returned by eqr_of_xlate_*(), is a (domain, offset) pair.
|
||||
* Low byte is domain, rest is offset.
|
||||
*/
|
||||
#define ID_DOMAIN_MASK GENMASK(7, 0)
|
||||
#define ID_OFFSET_MASK GENMASK(31, 8)
|
||||
|
||||
enum eqr_domain_type {
|
||||
EQR_EYEQ5_SARCR,
|
||||
EQR_EYEQ5_ACRP,
|
||||
EQR_EYEQ5_PCIE,
|
||||
EQR_EYEQ6H_SARCR,
|
||||
};
|
||||
|
||||
/*
|
||||
* Domain type EQR_EYEQ5_SARCR register offsets.
|
||||
*/
|
||||
#define EQR_EYEQ5_SARCR_REQUEST (0x000)
|
||||
#define EQR_EYEQ5_SARCR_STATUS (0x004)
|
||||
|
||||
/*
|
||||
* Domain type EQR_EYEQ5_ACRP register masks.
|
||||
* Registers are: base + 4 * offset.
|
||||
*/
|
||||
#define EQR_EYEQ5_ACRP_PD_REQ BIT(0)
|
||||
#define EQR_EYEQ5_ACRP_ST_POWER_DOWN BIT(27)
|
||||
#define EQR_EYEQ5_ACRP_ST_ACTIVE BIT(29)
|
||||
|
||||
/*
|
||||
* Domain type EQR_EYEQ6H_SARCR register offsets.
|
||||
*/
|
||||
#define EQR_EYEQ6H_SARCR_RST_REQUEST (0x000)
|
||||
#define EQR_EYEQ6H_SARCR_CLK_STATUS (0x004)
|
||||
#define EQR_EYEQ6H_SARCR_RST_STATUS (0x008)
|
||||
#define EQR_EYEQ6H_SARCR_CLK_REQUEST (0x00C)
|
||||
|
||||
struct eqr_busy_wait_timings {
|
||||
unsigned long sleep_us;
|
||||
unsigned long timeout_us;
|
||||
};
|
||||
|
||||
static const struct eqr_busy_wait_timings eqr_timings[] = {
|
||||
[EQR_EYEQ5_SARCR] = {1, 10},
|
||||
[EQR_EYEQ5_ACRP] = {1, 40 * USEC_PER_MSEC}, /* LBIST implies long timeout. */
|
||||
/* EQR_EYEQ5_PCIE does no busy waiting. */
|
||||
[EQR_EYEQ6H_SARCR] = {1, 400},
|
||||
};
|
||||
|
||||
#define EQR_MAX_DOMAIN_COUNT 3
|
||||
|
||||
struct eqr_domain_descriptor {
|
||||
enum eqr_domain_type type;
|
||||
u32 valid_mask;
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
struct eqr_match_data {
|
||||
unsigned int domain_count;
|
||||
const struct eqr_domain_descriptor *domains;
|
||||
};
|
||||
|
||||
struct eqr_private {
|
||||
/*
|
||||
* One mutex per domain for read-modify-write operations on registers.
|
||||
* Some domains can be involved in LBIST which implies long critical
|
||||
* sections; we wouldn't want other domains to be impacted by that.
|
||||
*/
|
||||
struct mutex mutexes[EQR_MAX_DOMAIN_COUNT];
|
||||
void __iomem *base;
|
||||
const struct eqr_match_data *data;
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
|
||||
static inline struct eqr_private *eqr_rcdev_to_priv(struct reset_controller_dev *x)
|
||||
{
|
||||
return container_of(x, struct eqr_private, rcdev);
|
||||
}
|
||||
|
||||
static u32 eqr_double_readl(void __iomem *addr_a, void __iomem *addr_b,
|
||||
u32 *dest_a, u32 *dest_b)
|
||||
{
|
||||
*dest_a = readl(addr_a);
|
||||
*dest_b = readl(addr_b);
|
||||
return 0; /* read_poll_timeout() op argument must return something. */
|
||||
}
|
||||
|
||||
static int eqr_busy_wait_locked(struct eqr_private *priv, struct device *dev,
|
||||
u32 domain, u32 offset, bool assert)
|
||||
{
|
||||
void __iomem *base = priv->base + priv->data->domains[domain].offset;
|
||||
enum eqr_domain_type domain_type = priv->data->domains[domain].type;
|
||||
unsigned long timeout_us = eqr_timings[domain_type].timeout_us;
|
||||
unsigned long sleep_us = eqr_timings[domain_type].sleep_us;
|
||||
u32 val, mask, rst_status, clk_status;
|
||||
void __iomem *reg;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&priv->mutexes[domain]);
|
||||
|
||||
switch (domain_type) {
|
||||
case EQR_EYEQ5_SARCR:
|
||||
reg = base + EQR_EYEQ5_SARCR_STATUS;
|
||||
mask = BIT(offset);
|
||||
|
||||
ret = readl_poll_timeout(reg, val, !(val & mask) == assert,
|
||||
sleep_us, timeout_us);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ5_ACRP:
|
||||
reg = base + 4 * offset;
|
||||
if (assert)
|
||||
mask = EQR_EYEQ5_ACRP_ST_POWER_DOWN;
|
||||
else
|
||||
mask = EQR_EYEQ5_ACRP_ST_ACTIVE;
|
||||
|
||||
ret = readl_poll_timeout(reg, val, !!(val & mask),
|
||||
sleep_us, timeout_us);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ5_PCIE:
|
||||
ret = 0; /* No busy waiting. */
|
||||
break;
|
||||
|
||||
case EQR_EYEQ6H_SARCR:
|
||||
/*
|
||||
* Wait until both bits change:
|
||||
* readl(base + EQR_EYEQ6H_SARCR_RST_STATUS) & BIT(offset)
|
||||
* readl(base + EQR_EYEQ6H_SARCR_CLK_STATUS) & BIT(offset)
|
||||
*/
|
||||
mask = BIT(offset);
|
||||
ret = read_poll_timeout(eqr_double_readl, val,
|
||||
(!(rst_status & mask) == assert) &&
|
||||
(!(clk_status & mask) == assert),
|
||||
sleep_us, timeout_us, false,
|
||||
base + EQR_EYEQ6H_SARCR_RST_STATUS,
|
||||
base + EQR_EYEQ6H_SARCR_CLK_STATUS,
|
||||
&rst_status, &clk_status);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_dbg(dev, "%u-%u: timeout\n", domain, offset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void eqr_assert_locked(struct eqr_private *priv, u32 domain, u32 offset)
|
||||
{
|
||||
enum eqr_domain_type domain_type = priv->data->domains[domain].type;
|
||||
void __iomem *base, *reg;
|
||||
u32 val;
|
||||
|
||||
lockdep_assert_held(&priv->mutexes[domain]);
|
||||
|
||||
base = priv->base + priv->data->domains[domain].offset;
|
||||
|
||||
switch (domain_type) {
|
||||
case EQR_EYEQ5_SARCR:
|
||||
reg = base + EQR_EYEQ5_SARCR_REQUEST;
|
||||
writel(readl(reg) & ~BIT(offset), reg);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ5_ACRP:
|
||||
reg = base + 4 * offset;
|
||||
writel(readl(reg) | EQR_EYEQ5_ACRP_PD_REQ, reg);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ5_PCIE:
|
||||
writel(readl(base) & ~BIT(offset), base);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ6H_SARCR:
|
||||
/* RST_REQUEST and CLK_REQUEST must be kept in sync. */
|
||||
val = readl(base + EQR_EYEQ6H_SARCR_RST_REQUEST);
|
||||
val &= ~BIT(offset);
|
||||
writel(val, base + EQR_EYEQ6H_SARCR_RST_REQUEST);
|
||||
writel(val, base + EQR_EYEQ6H_SARCR_CLK_REQUEST);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int eqr_assert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
|
||||
u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
|
||||
u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
|
||||
|
||||
dev_dbg(rcdev->dev, "%u-%u: assert request\n", domain, offset);
|
||||
|
||||
guard(mutex)(&priv->mutexes[domain]);
|
||||
|
||||
eqr_assert_locked(priv, domain, offset);
|
||||
return eqr_busy_wait_locked(priv, rcdev->dev, domain, offset, true);
|
||||
}
|
||||
|
||||
static void eqr_deassert_locked(struct eqr_private *priv, u32 domain,
|
||||
u32 offset)
|
||||
{
|
||||
enum eqr_domain_type domain_type = priv->data->domains[domain].type;
|
||||
void __iomem *base, *reg;
|
||||
u32 val;
|
||||
|
||||
lockdep_assert_held(&priv->mutexes[domain]);
|
||||
|
||||
base = priv->base + priv->data->domains[domain].offset;
|
||||
|
||||
switch (domain_type) {
|
||||
case EQR_EYEQ5_SARCR:
|
||||
reg = base + EQR_EYEQ5_SARCR_REQUEST;
|
||||
writel(readl(reg) | BIT(offset), reg);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ5_ACRP:
|
||||
reg = base + 4 * offset;
|
||||
writel(readl(reg) & ~EQR_EYEQ5_ACRP_PD_REQ, reg);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ5_PCIE:
|
||||
writel(readl(base) | BIT(offset), base);
|
||||
break;
|
||||
|
||||
case EQR_EYEQ6H_SARCR:
|
||||
/* RST_REQUEST and CLK_REQUEST must be kept in sync. */
|
||||
val = readl(base + EQR_EYEQ6H_SARCR_RST_REQUEST);
|
||||
val |= BIT(offset);
|
||||
writel(val, base + EQR_EYEQ6H_SARCR_RST_REQUEST);
|
||||
writel(val, base + EQR_EYEQ6H_SARCR_CLK_REQUEST);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int eqr_deassert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
|
||||
u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
|
||||
u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
|
||||
|
||||
dev_dbg(rcdev->dev, "%u-%u: deassert request\n", domain, offset);
|
||||
|
||||
guard(mutex)(&priv->mutexes[domain]);
|
||||
|
||||
eqr_deassert_locked(priv, domain, offset);
|
||||
return eqr_busy_wait_locked(priv, rcdev->dev, domain, offset, false);
|
||||
}
|
||||
|
||||
static int eqr_status(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
|
||||
u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
|
||||
struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
|
||||
enum eqr_domain_type domain_type = priv->data->domains[domain].type;
|
||||
void __iomem *base, *reg;
|
||||
|
||||
dev_dbg(rcdev->dev, "%u-%u: status request\n", domain, offset);
|
||||
|
||||
guard(mutex)(&priv->mutexes[domain]);
|
||||
|
||||
base = priv->base + priv->data->domains[domain].offset;
|
||||
|
||||
switch (domain_type) {
|
||||
case EQR_EYEQ5_SARCR:
|
||||
reg = base + EQR_EYEQ5_SARCR_STATUS;
|
||||
return !(readl(reg) & BIT(offset));
|
||||
case EQR_EYEQ5_ACRP:
|
||||
reg = base + 4 * offset;
|
||||
return !(readl(reg) & EQR_EYEQ5_ACRP_ST_ACTIVE);
|
||||
case EQR_EYEQ5_PCIE:
|
||||
return !(readl(base) & BIT(offset));
|
||||
case EQR_EYEQ6H_SARCR:
|
||||
reg = base + EQR_EYEQ6H_SARCR_RST_STATUS;
|
||||
return !(readl(reg) & BIT(offset));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct reset_control_ops eqr_ops = {
|
||||
.assert = eqr_assert,
|
||||
.deassert = eqr_deassert,
|
||||
.status = eqr_status,
|
||||
};
|
||||
|
||||
static int eqr_of_xlate_internal(struct reset_controller_dev *rcdev,
|
||||
u32 domain, u32 offset)
|
||||
{
|
||||
struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
|
||||
|
||||
if (domain >= priv->data->domain_count || offset > 31 ||
|
||||
!(priv->data->domains[domain].valid_mask & BIT(offset))) {
|
||||
dev_err(rcdev->dev, "%u-%u: invalid reset\n", domain, offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return FIELD_PREP(ID_DOMAIN_MASK, domain) | FIELD_PREP(ID_OFFSET_MASK, offset);
|
||||
}
|
||||
|
||||
static int eqr_of_xlate_onecell(struct reset_controller_dev *rcdev,
|
||||
const struct of_phandle_args *reset_spec)
|
||||
{
|
||||
return eqr_of_xlate_internal(rcdev, 0, reset_spec->args[0]);
|
||||
}
|
||||
|
||||
static int eqr_of_xlate_twocells(struct reset_controller_dev *rcdev,
|
||||
const struct of_phandle_args *reset_spec)
|
||||
{
|
||||
return eqr_of_xlate_internal(rcdev, reset_spec->args[0], reset_spec->args[1]);
|
||||
}
|
||||
|
||||
static int eqr_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &adev->dev;
|
||||
struct eqr_private *priv;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We are an auxiliary device of clk-eyeq. We do not have an OF node by
|
||||
* default; let's reuse our parent's OF node.
|
||||
*/
|
||||
WARN_ON(dev->of_node);
|
||||
device_set_of_node_from_dev(dev, dev->parent);
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Using our newfound OF node, we can get match data. We cannot use
|
||||
* device_get_match_data() because it does not match reused OF nodes.
|
||||
*/
|
||||
match = of_match_node(dev->driver->of_match_table, dev->of_node);
|
||||
if (!match || !match->data)
|
||||
return -ENODEV;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->data = match->data;
|
||||
priv->base = (void __iomem *)dev_get_platdata(dev);
|
||||
priv->rcdev.ops = &eqr_ops;
|
||||
priv->rcdev.owner = THIS_MODULE;
|
||||
priv->rcdev.dev = dev;
|
||||
priv->rcdev.of_node = dev->of_node;
|
||||
|
||||
if (priv->data->domain_count == 1) {
|
||||
priv->rcdev.of_reset_n_cells = 1;
|
||||
priv->rcdev.of_xlate = eqr_of_xlate_onecell;
|
||||
} else {
|
||||
priv->rcdev.of_reset_n_cells = 2;
|
||||
priv->rcdev.of_xlate = eqr_of_xlate_twocells;
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->data->domain_count; i++)
|
||||
mutex_init(&priv->mutexes[i]);
|
||||
|
||||
priv->rcdev.nr_resets = 0;
|
||||
for (i = 0; i < priv->data->domain_count; i++)
|
||||
priv->rcdev.nr_resets += hweight32(priv->data->domains[i].valid_mask);
|
||||
|
||||
ret = devm_reset_controller_register(dev, &priv->rcdev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed registering reset controller\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct eqr_domain_descriptor eqr_eyeq5_domains[] = {
|
||||
{
|
||||
.type = EQR_EYEQ5_SARCR,
|
||||
.valid_mask = 0xFFFFFF8,
|
||||
.offset = 0x004,
|
||||
},
|
||||
{
|
||||
.type = EQR_EYEQ5_ACRP,
|
||||
.valid_mask = 0x0001FFF,
|
||||
.offset = 0x200,
|
||||
},
|
||||
{
|
||||
.type = EQR_EYEQ5_PCIE,
|
||||
.valid_mask = 0x007BFFF,
|
||||
.offset = 0x120,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct eqr_match_data eqr_eyeq5_data = {
|
||||
.domain_count = ARRAY_SIZE(eqr_eyeq5_domains),
|
||||
.domains = eqr_eyeq5_domains,
|
||||
};
|
||||
|
||||
static const struct eqr_domain_descriptor eqr_eyeq6l_domains[] = {
|
||||
{
|
||||
.type = EQR_EYEQ5_SARCR,
|
||||
.valid_mask = 0x3FFF,
|
||||
.offset = 0x004,
|
||||
},
|
||||
{
|
||||
.type = EQR_EYEQ5_ACRP,
|
||||
.valid_mask = 0x00FF,
|
||||
.offset = 0x200,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct eqr_match_data eqr_eyeq6l_data = {
|
||||
.domain_count = ARRAY_SIZE(eqr_eyeq6l_domains),
|
||||
.domains = eqr_eyeq6l_domains,
|
||||
};
|
||||
|
||||
/* West and east OLBs each have an instance. */
|
||||
static const struct eqr_domain_descriptor eqr_eyeq6h_we_domains[] = {
|
||||
{
|
||||
.type = EQR_EYEQ6H_SARCR,
|
||||
.valid_mask = 0x1F7F,
|
||||
.offset = 0x004,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct eqr_match_data eqr_eyeq6h_we_data = {
|
||||
.domain_count = ARRAY_SIZE(eqr_eyeq6h_we_domains),
|
||||
.domains = eqr_eyeq6h_we_domains,
|
||||
};
|
||||
|
||||
static const struct eqr_domain_descriptor eqr_eyeq6h_acc_domains[] = {
|
||||
{
|
||||
.type = EQR_EYEQ5_ACRP,
|
||||
.valid_mask = 0x7FFF,
|
||||
.offset = 0x000,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct eqr_match_data eqr_eyeq6h_acc_data = {
|
||||
.domain_count = ARRAY_SIZE(eqr_eyeq6h_acc_domains),
|
||||
.domains = eqr_eyeq6h_acc_domains,
|
||||
};
|
||||
|
||||
/*
|
||||
* Table describes OLB system-controller compatibles.
|
||||
* It does not get used to match against devicetree node.
|
||||
*/
|
||||
static const struct of_device_id eqr_match_table[] = {
|
||||
{ .compatible = "mobileye,eyeq5-olb", .data = &eqr_eyeq5_data },
|
||||
{ .compatible = "mobileye,eyeq6l-olb", .data = &eqr_eyeq6l_data },
|
||||
{ .compatible = "mobileye,eyeq6h-west-olb", .data = &eqr_eyeq6h_we_data },
|
||||
{ .compatible = "mobileye,eyeq6h-east-olb", .data = &eqr_eyeq6h_we_data },
|
||||
{ .compatible = "mobileye,eyeq6h-acc-olb", .data = &eqr_eyeq6h_acc_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, eqr_match_table);
|
||||
|
||||
static const struct auxiliary_device_id eqr_id_table[] = {
|
||||
{ .name = "clk_eyeq.reset" },
|
||||
{ .name = "clk_eyeq.reset_west" },
|
||||
{ .name = "clk_eyeq.reset_east" },
|
||||
{ .name = "clk_eyeq.reset_acc" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, eqr_id_table);
|
||||
|
||||
static struct auxiliary_driver eqr_driver = {
|
||||
.probe = eqr_probe,
|
||||
.id_table = eqr_id_table,
|
||||
.driver = {
|
||||
.of_match_table = eqr_match_table,
|
||||
}
|
||||
};
|
||||
module_auxiliary_driver(eqr_driver);
|
@ -90,7 +90,7 @@ static const struct reset_control_ops k210_rst_ops = {
|
||||
static int k210_rst_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *parent_np = of_get_parent(dev->of_node);
|
||||
struct device_node *parent_np;
|
||||
struct k210_rst *ksr;
|
||||
|
||||
dev_info(dev, "K210 reset controller\n");
|
||||
@ -99,6 +99,7 @@ static int k210_rst_probe(struct platform_device *pdev)
|
||||
if (!ksr)
|
||||
return -ENOMEM;
|
||||
|
||||
parent_np = of_get_parent(dev->of_node);
|
||||
ksr->map = syscon_node_to_regmap(parent_np);
|
||||
of_node_put(parent_np);
|
||||
if (IS_ERR(ksr->map))
|
||||
|
@ -150,29 +150,15 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(rc->base))
|
||||
return PTR_ERR(rc->base);
|
||||
|
||||
rc->clk_reg = devm_clk_get(&pdev->dev, "reg");
|
||||
if (IS_ERR(rc->clk_reg)) {
|
||||
dev_err(&pdev->dev, "reg clock not found\n");
|
||||
return PTR_ERR(rc->clk_reg);
|
||||
}
|
||||
rc->clk_reg = devm_clk_get_enabled(&pdev->dev, "reg");
|
||||
if (IS_ERR(rc->clk_reg))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(rc->clk_reg),
|
||||
"reg clock not found\n");
|
||||
|
||||
rc->clk_delay = devm_clk_get(&pdev->dev, "delay");
|
||||
if (IS_ERR(rc->clk_delay)) {
|
||||
dev_err(&pdev->dev, "delay clock not found\n");
|
||||
return PTR_ERR(rc->clk_delay);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(rc->clk_reg);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable reg clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(rc->clk_delay);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable delay clock\n");
|
||||
goto dis_clk_reg;
|
||||
}
|
||||
rc->clk_delay = devm_clk_get_enabled(&pdev->dev, "delay");
|
||||
if (IS_ERR(rc->clk_delay))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(rc->clk_delay),
|
||||
"delay clock not found\n");
|
||||
|
||||
fcclk = clk_get_rate(rc->clk_reg) / USEC_PER_SEC;
|
||||
firc = clk_get_rate(rc->clk_delay) / USEC_PER_SEC;
|
||||
@ -189,10 +175,8 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev)
|
||||
rc->rcdev.of_node = pdev->dev.of_node;
|
||||
|
||||
ret = reset_controller_register(&rc->rcdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to register device\n");
|
||||
goto dis_clks;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret, "unable to register device\n");
|
||||
|
||||
rc->restart_nb.priority = 192,
|
||||
rc->restart_nb.notifier_call = lpc18xx_rgu_restart,
|
||||
@ -201,13 +185,6 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "failed to register restart handler\n");
|
||||
|
||||
return 0;
|
||||
|
||||
dis_clks:
|
||||
clk_disable_unprepare(rc->clk_delay);
|
||||
dis_clk_reg:
|
||||
clk_disable_unprepare(rc->clk_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id lpc18xx_rgu_match[] = {
|
||||
|
@ -102,6 +102,11 @@ static const struct meson_reset_param meson_s4_param = {
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct meson_reset_param t7_param = {
|
||||
.reg_count = 7,
|
||||
.level_offset = 0x40,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_reset_dt_ids[] = {
|
||||
{ .compatible = "amlogic,meson8b-reset", .data = &meson8b_param},
|
||||
{ .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
|
||||
@ -109,6 +114,7 @@ static const struct of_device_id meson_reset_dt_ids[] = {
|
||||
{ .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param},
|
||||
{ .compatible = "amlogic,meson-s4-reset", .data = &meson_s4_param},
|
||||
{ .compatible = "amlogic,c3-reset", .data = &meson_s4_param},
|
||||
{ .compatible = "amlogic,t7-reset", .data = &t7_param},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
|
||||
|
@ -1827,6 +1827,17 @@ config RTC_DRV_BBNSM
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-bbnsm".
|
||||
|
||||
config RTC_DRV_IMX_BBM_SCMI
|
||||
depends on IMX_SCMI_BBM_EXT || COMPILE_TEST
|
||||
default y if ARCH_MXC
|
||||
tristate "NXP i.MX BBM SCMI RTC support"
|
||||
help
|
||||
If you say yes here you get support for the NXP i.MX BBSM SCMI
|
||||
RTC module.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rtc-imx-sm-bbm.
|
||||
|
||||
config RTC_DRV_IMX_SC
|
||||
depends on IMX_SCU
|
||||
depends on HAVE_ARM_SMCCC
|
||||
|
@ -74,6 +74,7 @@ obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
|
||||
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
|
||||
obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
|
||||
obj-$(CONFIG_RTC_DRV_IMX_SC) += rtc-imx-sc.o
|
||||
obj-$(CONFIG_RTC_DRV_IMX_BBM_SCMI) += rtc-imx-sm-bbm.o
|
||||
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
|
||||
obj-$(CONFIG_RTC_DRV_ISL12026) += rtc-isl12026.o
|
||||
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
|
||||
|
162
drivers/rtc/rtc-imx-sm-bbm.c
Normal file
162
drivers/rtc/rtc-imx-sm-bbm.c
Normal file
@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2024 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
|
||||
struct scmi_imx_bbm {
|
||||
const struct scmi_imx_bbm_proto_ops *ops;
|
||||
struct rtc_device *rtc_dev;
|
||||
struct scmi_protocol_handle *ph;
|
||||
struct notifier_block nb;
|
||||
};
|
||||
|
||||
static int scmi_imx_bbm_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
|
||||
struct scmi_protocol_handle *ph = bbnsm->ph;
|
||||
u64 val;
|
||||
int ret;
|
||||
|
||||
ret = bbnsm->ops->rtc_time_get(ph, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc_time64_to_tm(val, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
|
||||
struct scmi_protocol_handle *ph = bbnsm->ph;
|
||||
u64 val;
|
||||
|
||||
val = rtc_tm_to_time64(tm);
|
||||
|
||||
return bbnsm->ops->rtc_time_set(ph, 0, val);
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
|
||||
struct scmi_protocol_handle *ph = bbnsm->ph;
|
||||
|
||||
/* scmi_imx_bbm_set_alarm enables the irq, just handle disable here */
|
||||
if (!enable)
|
||||
return bbnsm->ops->rtc_alarm_set(ph, 0, false, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
|
||||
struct scmi_protocol_handle *ph = bbnsm->ph;
|
||||
struct rtc_time *alrm_tm = &alrm->time;
|
||||
u64 val;
|
||||
|
||||
val = rtc_tm_to_time64(alrm_tm);
|
||||
|
||||
return bbnsm->ops->rtc_alarm_set(ph, 0, true, val);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops smci_imx_bbm_rtc_ops = {
|
||||
.read_time = scmi_imx_bbm_read_time,
|
||||
.set_time = scmi_imx_bbm_set_time,
|
||||
.set_alarm = scmi_imx_bbm_set_alarm,
|
||||
.alarm_irq_enable = scmi_imx_bbm_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int scmi_imx_bbm_rtc_notifier(struct notifier_block *nb, unsigned long event, void *data)
|
||||
{
|
||||
struct scmi_imx_bbm *bbnsm = container_of(nb, struct scmi_imx_bbm, nb);
|
||||
struct scmi_imx_bbm_notif_report *r = data;
|
||||
|
||||
if (r->is_rtc)
|
||||
rtc_update_irq(bbnsm->rtc_dev, 1, RTC_AF | RTC_IRQF);
|
||||
else
|
||||
pr_err("Unexpected bbm event: %s\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_rtc_init(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
struct device *dev = &sdev->dev;
|
||||
struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
bbnsm->rtc_dev = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(bbnsm->rtc_dev))
|
||||
return PTR_ERR(bbnsm->rtc_dev);
|
||||
|
||||
bbnsm->rtc_dev->ops = &smci_imx_bbm_rtc_ops;
|
||||
bbnsm->rtc_dev->range_max = U32_MAX;
|
||||
|
||||
bbnsm->nb.notifier_call = &scmi_imx_bbm_rtc_notifier;
|
||||
ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_BBM,
|
||||
SCMI_EVENT_IMX_BBM_RTC,
|
||||
NULL, &bbnsm->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_rtc_register_device(bbnsm->rtc_dev);
|
||||
}
|
||||
|
||||
static int scmi_imx_bbm_rtc_probe(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
struct device *dev = &sdev->dev;
|
||||
struct scmi_protocol_handle *ph;
|
||||
struct scmi_imx_bbm *bbnsm;
|
||||
int ret;
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
bbnsm = devm_kzalloc(dev, sizeof(*bbnsm), GFP_KERNEL);
|
||||
if (!bbnsm)
|
||||
return -ENOMEM;
|
||||
|
||||
bbnsm->ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_BBM, &ph);
|
||||
if (IS_ERR(bbnsm->ops))
|
||||
return PTR_ERR(bbnsm->ops);
|
||||
|
||||
bbnsm->ph = ph;
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
|
||||
dev_set_drvdata(dev, bbnsm);
|
||||
|
||||
ret = scmi_imx_bbm_rtc_init(sdev);
|
||||
if (ret)
|
||||
device_init_wakeup(dev, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_IMX_BBM, "imx-bbm-rtc" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_imx_bbm_rtc_driver = {
|
||||
.name = "scmi-imx-bbm-rtc",
|
||||
.probe = scmi_imx_bbm_rtc_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_imx_bbm_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX SM BBM RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user