diff options
61 files changed, 1653 insertions, 590 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/amd,bram-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/amd,bram-rproc.yaml new file mode 100644 index 000000000000..c0359f447ea8 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/amd,bram-rproc.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/amd,bram-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMD MicroBlaze/V BRAM-based Remote Processor + +maintainers: + - Ben Levinsky <ben.levinsky@amd.com> + +description: | + Soft-core processor subsystem instantiated in AMD programmable logic and + using dual-port BRAM for firmware storage and execution. + + Hardware Architecture: + + Host (PS) Programmable Logic (PL) + ========= ====================== + + AXI Interface -----------------> AXI BRAM Controller (Host Port) + | + | Port A + v + +-----------------+ + | Dual-Port BRAM | + | (shared memory) | + +-----------------+ + ^ + | Port B + | + AXI BRAM Controller (Soft-core Port) + ^ + | LMB + | + Soft-core CPU (MicroBlaze/V) + + GPIO --------------------------> Proc Sys Reset ----> CPU Reset Signal + + Clock -------------------------> Clock Distribution -> CPU Clock + + Memory Architecture: + + The dual-port BRAM allows simultaneous access from both processors: + - Port A: Connected to the host AXI BRAM controller for firmware loading + - Port B: Connected to the soft-core local memory bus for execution + + The reg property describes the executable BRAM window in the processor-local + address space. The parent bus node translates that window to the system + physical address space by using standard devicetree address translation + through ranges. A clock input and a reset GPIO control the subsystem. + +properties: + compatible: + oneOf: + - const: xlnx,zynqmp-bram-rproc + - items: + - enum: + - amd,versal2-bram-rproc + - xlnx,versal-bram-rproc + - xlnx,versal-net-bram-rproc + - const: xlnx,zynqmp-bram-rproc + + reg: + maxItems: 1 + description: + Processor-local address and size of the BRAM firmware memory window, + as seen by the soft-core processor (typically 0x0 for reset vector). + The parent bus ranges property must translate this window to the + corresponding system physical address. + + clocks: + maxItems: 1 + description: + Clock input for the soft-core processor subsystem. + + firmware-name: + maxItems: 1 + description: + Name of the firmware ELF file to load. + + reset-gpios: + maxItems: 1 + description: + GPIO specifier controlling the soft-core reset input. + +required: + - compatible + - reg + - clocks + - reset-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + remoteproc@0 { + compatible = "xlnx,zynqmp-bram-rproc"; + reg = <0x0 0x40000>; + clocks = <&pl_clk>; + firmware-name = "firmware.elf"; + reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + }; +... diff --git a/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml b/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml index 3f710433e937..4cd5121b9a4e 100644 --- a/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml @@ -25,7 +25,7 @@ properties: - const: amlogic,meson-mx-ao-arc firmware-name: - $ref: /schemas/types.yaml#/definitions/string + maxItems: 1 description: The name of the firmware which should be loaded for this remote processor. diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml index 16a245fe2738..7e8ecae8e6cb 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml @@ -32,6 +32,14 @@ properties: reg: maxItems: 1 + clocks: + items: + - description: XO clock + + clock-names: + items: + - const: xo + cx-supply: true px-supply: @@ -49,6 +57,30 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + + interrupt-names: + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - memory-region @@ -66,56 +98,6 @@ allOf: - qcom,msm8953-adsp-pil - qcom,msm8974-adsp-pil - qcom,msm8996-adsp-pil - - qcom,msm8996-slpi-pil - - qcom,msm8998-adsp-pas - - qcom,msm8998-slpi-pas - - qcom,sdm660-adsp-pas - - qcom,sdm660-cdsp-pas - - qcom,sdm845-adsp-pas - - qcom,sdm845-cdsp-pas - - qcom,sdm845-slpi-pas - then: - properties: - clocks: - items: - - description: XO clock - clock-names: - items: - - const: xo - - - if: - properties: - compatible: - contains: - enum: - - qcom,msm8226-adsp-pil - - qcom,msm8953-adsp-pil - - qcom,msm8974-adsp-pil - - qcom,msm8996-adsp-pil - - qcom,msm8996-slpi-pil - - qcom,msm8998-adsp-pas - - qcom,msm8998-slpi-pas - - qcom,sdm660-adsp-pas - - qcom,sdm660-cdsp-pas - - qcom,sdm845-adsp-pas - - qcom,sdm845-cdsp-pas - - qcom,sdm845-slpi-pas - then: - properties: - interrupts: - maxItems: 5 - interrupt-names: - maxItems: 5 - - - if: - properties: - compatible: - contains: - enum: - - qcom,msm8226-adsp-pil - - qcom,msm8953-adsp-pil - - qcom,msm8974-adsp-pil - - qcom,msm8996-adsp-pil - qcom,msm8998-adsp-pas - qcom,sdm660-adsp-pas - qcom,sdm660-cdsp-pas diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,kaanapali-soccp-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,kaanapali-soccp-pas.yaml new file mode 100644 index 000000000000..e6c78d621d17 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/qcom,kaanapali-soccp-pas.yaml @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/qcom,kaanapali-soccp-pas.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Kaanapali SoCCP Peripheral Authentication Service + +maintainers: + - Jingyi Wang <jingyi.wang@oss.qualcomm.com> + +description: + The SoC Control Processor (SoCCP) is a small RISC-V MCU that controls USB + Type-C, battery charging and various other functions on Qualcomm SoCs, somewhat + analogous to traditional PC Embedded Controllers. This document describes + the Peripheral Authentication Service that loads and boots firmware for SoCCP. + +properties: + compatible: + oneOf: + - items: + - enum: + - qcom,hawi-soccp-pas + - qcom,maili-soccp-pas + - const: qcom,kaanapali-soccp-pas + - enum: + - qcom,glymur-soccp-pas + - qcom,kaanapali-soccp-pas + + reg: + maxItems: 1 + + clocks: + items: + - description: XO clock + + clock-names: + items: + - const: xo + + power-domains: + items: + - description: CX power domain + - description: MX power domain + + power-domain-names: + items: + - const: cx + - const: mx + + firmware-name: + items: + - description: Firmware name of the SoC Control Processor + - description: Firmware name of the SoCCP Devicetree + + memory-region: + items: + - description: Memory region for main Firmware authentication + - description: Memory region for Devicetree Firmware authentication + + interrupts: + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Pong interrupt + + interrupt-names: + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: pong + + qcom,smem-states: + minItems: 2 + description: States used by the AP to signal the SoC Control Processor + + qcom,smem-state-names: + minItems: 2 + description: The names of the state bits used for SMP2P output + +required: + - compatible + - reg + - memory-region + - power-domains + - power-domain-names + +allOf: + - $ref: /schemas/remoteproc/qcom,pas-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,rpmh.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/mailbox/qcom-ipcc.h> + #include <dt-bindings/power/qcom-rpmpd.h> + #define IPCC_MPROC_SOCCP + + remoteproc@d00000 { + compatible = "qcom,kaanapali-soccp-pas"; + reg = <0x00d00000 0x200000>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "xo"; + + interrupts-extended = <&intc GIC_SPI 167 IRQ_TYPE_EDGE_RISING>, + <&soccp_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&soccp_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&soccp_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&soccp_smp2p_in 3 IRQ_TYPE_EDGE_RISING>, + <&soccp_smp2p_in 9 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", + "fatal", + "ready", + "handover", + "stop-ack", + "pong"; + + memory-region = <&soccp_mem>, + <&soccp_dtb_mem_mem>; + + firmware-name = "qcom/kaanapali/soccp.mbn", + "qcom/kaanapali/soccp_dtb.mbn"; + + power-domains = <&rpmhpd RPMHPD_CX>, + <&rpmhpd RPMHPD_MX>; + power-domain-names = "cx", + "mx"; + + qcom,smem-states = <&soccp_smp2p_out 0>, + <&soccp_smp2p_out 8>; + qcom,smem-state-names = "stop", + "ping"; + + glink-edge { + interrupts-extended = <&ipcc IPCC_MPROC_SOCCP + IPCC_MPROC_SIGNAL_GLINK_QMP + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipcc IPCC_MPROC_SOCCP + IPCC_MPROC_SIGNAL_GLINK_QMP>; + + label = "soccp"; + qcom,remote-pid = <19>; + + /* ... */ + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml index e5cce0d05fc6..99d7337e58ec 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml @@ -34,12 +34,22 @@ properties: - const: xo interrupts: - minItems: 6 - maxItems: 6 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt interrupt-names: - minItems: 6 - maxItems: 6 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack qcom,qmp: $ref: /schemas/types.yaml#/definitions/phandle @@ -59,6 +69,14 @@ properties: - description: Memory region for core Firmware authentication - description: Memory region for Devicetree Firmware authentication + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml index 4607b459131b..11faf655f530 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml @@ -26,23 +26,11 @@ properties: interrupts: minItems: 5 - items: - - description: Watchdog interrupt - - description: Fatal interrupt - - description: Ready interrupt - - description: Handover interrupt - - description: Stop acknowledge interrupt - - description: Shutdown acknowledge interrupt + maxItems: 6 interrupt-names: minItems: 5 - items: - - const: wdog - - const: fatal - - const: ready - - const: handover - - const: stop-ack - - const: shutdown-ack + maxItems: 6 iommus: maxItems: 1 @@ -58,8 +46,16 @@ properties: qcom,smem-states: $ref: /schemas/types.yaml#/definitions/phandle-array description: States used by the AP to signal the Hexagon core + minItems: 1 items: - - description: Stop the modem + - description: Stop the remoteproc + items: + - description: Phandle to the Shared Memory Point 2 Point device + handling the communication with a remote processor + - description: Single bit index to toggle in the value sent to + the remote processor + maximum: 32 + - description: Ping the remoteproc items: - description: Phandle to the Shared Memory Point 2 Point device handling the communication with a remote processor @@ -69,8 +65,10 @@ properties: qcom,smem-state-names: description: The names of the state bits used for SMP2P output + minItems: 1 items: - const: stop + - const: ping smd-edge: $ref: /schemas/remoteproc/qcom,smd-edge.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml index ad45fd00ae34..bf9bf1af9ff1 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml @@ -32,10 +32,20 @@ properties: - const: xo interrupts: - maxItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt interrupt-names: - maxItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack power-domains: false power-domain-names: false @@ -49,6 +59,14 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml index bcd2bcf96e24..dda2d144b720 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml @@ -59,10 +59,28 @@ properties: - description: Memory region for main Firmware authentication interrupts: - maxItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt interrupt-names: - maxItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output required: - compatible diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml index 66b455d0a8e3..b20780e5e26b 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml @@ -48,6 +48,34 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml index 8227527c1d77..4bbe4a986c7c 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml @@ -45,6 +45,34 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml index 8c4abde74915..8c16b01c53e4 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml @@ -30,10 +30,22 @@ properties: - const: xo interrupts: - minItems: 6 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt interrupt-names: - minItems: 6 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack power-domains: items: @@ -59,6 +71,14 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml index 253b14eb2b59..34a2f15e9361 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml @@ -51,6 +51,26 @@ properties: and devices related to the remoteproc core. unevaluatedProperties: false + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + qcom,smem-states: $ref: /schemas/types.yaml#/definitions/phandle-array description: States used by the AP to signal the Hexagon core diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml index eeb6a8aafeb9..454ba82bd6f1 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml @@ -51,6 +51,34 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml index c1a3cc308bdb..42e02c64347a 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml @@ -45,6 +45,34 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm6375-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm6375-pas.yaml index 7286b2baa19f..274f87880e2e 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm6375-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm6375-pas.yaml @@ -39,8 +39,36 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + smd-edge: false + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml index a8cddf7e2fe1..5a7c5f8c92d1 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml @@ -61,6 +61,34 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml index 6d09823153fc..72d0db5698c5 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml @@ -55,6 +55,34 @@ properties: maxItems: 1 description: Firmware name for the Hexagon core + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml index 9f30a38152a3..2a4c527552e6 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml @@ -32,6 +32,7 @@ properties: - qcom,glymur-adsp-pas - qcom,hawi-adsp-pas - qcom,kaanapali-adsp-pas + - qcom,maili-adsp-pas - qcom,sm8750-adsp-pas - const: qcom,sm8550-adsp-pas - items: @@ -39,6 +40,7 @@ properties: - qcom,glymur-cdsp-pas - qcom,hawi-cdsp-pas - qcom,kaanapali-cdsp-pas + - qcom,maili-cdsp-pas - const: qcom,sm8550-cdsp-pas - items: - const: qcom,sm8750-cdsp-pas @@ -75,6 +77,34 @@ properties: - description: DSM Memory region 2 - description: Memory region for Qlink Logging + interrupts: + minItems: 5 + items: + - description: Watchdog interrupt + - description: Fatal interrupt + - description: Ready interrupt + - description: Handover interrupt + - description: Stop acknowledge interrupt + - description: Shutdown acknowledge interrupt + + interrupt-names: + minItems: 5 + items: + - const: wdog + - const: fatal + - const: ready + - const: handover + - const: stop-ack + - const: shutdown-ack + + qcom,smem-states: + maxItems: 1 + description: States used by the AP to signal the Hexagon core + + qcom,smem-state-names: + maxItems: 1 + description: The names of the state bits used for SMP2P output + required: - compatible - reg @@ -110,6 +140,8 @@ allOf: - qcom,hawi-cdsp-pas - qcom,kaanapali-adsp-pas - qcom,kaanapali-cdsp-pas + - qcom,maili-adsp-pas + - qcom,maili-cdsp-pas - qcom,sm8750-adsp-pas then: properties: diff --git a/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml b/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml new file mode 100644 index 000000000000..9e8a03acdec4 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/ti,am3352-wkup-m3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI AM33xx/AM43xx Wakeup M3 Remote Processor + +maintainers: + - Suman Anna <s-anna@ti.com> + - Bhargav Joshi <j.bhargav.u@gmail.com> + +description: + The TI AM33xx and AM43xx family of devices use a small Cortex M3 co-processor + (commonly referred to as Wakeup M3 or CM3) to help with various low power + tasks that cannot be controlled from the MPU. This CM3 processor requires a + firmware binary to accomplish this. A wkup_m3 device node is used to + represent the Wakeup M3 processor instance within the SoC. It is added as a + child node of the parent interconnect bus (l4_wkup) through which it is + accessible to the MPU. + +properties: + compatible: + enum: + - ti,am3352-wkup-m3 + - ti,am4372-wkup-m3 + + reg: + items: + - description: Address range for UMEM + - description: Address range for DMEM + + reg-names: + items: + - const: umem + - const: dmem + + ti,pm-firmware: + $ref: /schemas/types.yaml#/definitions/string + description: + Name of firmware file to be used for loading and booting the wkup_m3 + remote processor. + + resets: + maxItems: 1 + + reset-names: + items: + - const: rstctrl + + ti,hwmods: + $ref: /schemas/types.yaml#/definitions/string + deprecated: true + description: + Name of the hwmod associated with the wkupm3 device. + +required: + - compatible + - reg + - reg-names + - ti,pm-firmware + +dependencies: + resets: [reset-names] + reset-names: [resets] + +additionalProperties: false + +examples: + - | + target-module@0 { + compatible = "ti,sysc-omap4", "ti,sysc"; + reg = <0x0 0x4>; + reg-names = "rev"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x00000000 0x00000000 0x4000>, + <0x00080000 0x00080000 0x2000>; + + cpu@0 { + compatible = "ti,am3352-wkup-m3"; + reg = <0x00000000 0x4000>, + <0x00080000 0x2000>; + reg-names = "umem", "dmem"; + resets = <&prm_wkup 3>; + reset-names = "rstctrl"; + ti,pm-firmware = "am335x-pm-firmware.elf"; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml b/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml index 2811334515d1..591471e5f93a 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ti,pru-consumer.yaml @@ -30,7 +30,6 @@ properties: maxItems: 1 firmware-name: - $ref: /schemas/types.yaml#/definitions/string-array minItems: 1 maxItems: 6 description: | diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt deleted file mode 100644 index 3a70073797eb..000000000000 --- a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt +++ /dev/null @@ -1,52 +0,0 @@ -TI Wakeup M3 Remoteproc Driver -============================== - -The TI AM33xx and AM43xx family of devices use a small Cortex M3 co-processor -(commonly referred to as Wakeup M3 or CM3) to help with various low power tasks -that cannot be controlled from the MPU. This CM3 processor requires a firmware -binary to accomplish this. The wkup_m3 remoteproc driver handles the loading of -the firmware and booting of the CM3. - -Wkup M3 Device Node: -==================== -A wkup_m3 device node is used to represent the Wakeup M3 processor instance -within the SoC. It is added as a child node of the parent interconnect bus -(l4_wkup) through which it is accessible to the MPU. - -Required properties: --------------------- -- compatible: Should be one of, - "ti,am3352-wkup-m3" for AM33xx SoCs - "ti,am4372-wkup-m3" for AM43xx SoCs -- reg: Should contain the address ranges for the two internal - memory regions, UMEM and DMEM. The parent node should - provide an appropriate ranges property for properly - translating these into bus addresses. -- reg-names: Contains the corresponding names for the two memory - regions. These should be named "umem" & "dmem". -- ti,hwmods: Name of the hwmod associated with the wkupm3 device. -- ti,pm-firmware: Name of firmware file to be used for loading and - booting the wkup_m3 remote processor. - -Example: --------- -/* AM33xx */ -ocp { - l4_wkup: l4_wkup@44c00000 { - compatible = "am335-l4-wkup", "simple-bus"; - ranges = <0 0x44c00000 0x400000>; - #address-cells = <1>; - #size-cells = <1>; - - wkup_m3: wkup_m3@100000 { - compatible = "ti,am3352-wkup-m3"; - reg = <0x100000 0x4000>, - <0x180000 0x2000>; - reg-names = "umem", "dmem"; - ti,hwmods = "wkup_m3"; - ti,pm-firmware = "am335x-pm-firmware.elf"; - }; - }; - - ... -}; diff --git a/Documentation/staging/rpmsg.rst b/Documentation/staging/rpmsg.rst index 42bac1149d9d..63612b7ee120 100644 --- a/Documentation/staging/rpmsg.rst +++ b/Documentation/staging/rpmsg.rst @@ -212,7 +212,6 @@ be probed with. unregisters an rpmsg driver from the rpmsg bus. user should provide a pointer to a previously-registered rpmsg_driver struct. -Returns 0 on success, and an appropriate error value on failure. Typical usage diff --git a/MAINTAINERS b/MAINTAINERS index 2cfe91796644..0b12628147c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11479,7 +11479,7 @@ M: Bjorn Andersson <andersson@kernel.org> R: Baolin Wang <baolin.wang7@gmail.com> L: linux-remoteproc@vger.kernel.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc.git hwspinlock-next +T: git https://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux.git hwspinlock-next F: Documentation/devicetree/bindings/hwlock/ F: Documentation/locking/hwspinlock.rst F: drivers/hwspinlock/ diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index af838b2dc327..f9a3a95b0638 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -1514,6 +1514,99 @@ int zynqmp_pm_request_wake(const u32 node, EXPORT_SYMBOL_GPL(zynqmp_pm_request_wake); /** + * zynqmp_pm_start_rpu - Boot Real-time Processing Unit (Cortex-R) on SoC + * + * @node: power-domains id of the core + * @bootaddr: Boot address of elf + * + * Return: status, either success or error+reason + */ +int zynqmp_pm_start_rpu(const u32 node, const u64 bootaddr) +{ + enum rpu_boot_mem bootmem; + int ret; + + /* + * The exception vector pointers (EVP) refer to the base-address of + * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector + * starts at the base-address and subsequent vectors are on 4-byte + * boundaries. + * + * Exception vectors can start either from 0x0000_0000 (LOVEC) or + * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip Memory) + * + * Usually firmware will put Exception vectors at LOVEC. + * + * It is not recommend that you change the exception vector. + * Changing the EVP to HIVEC will result in increased interrupt latency + * and jitter. Also, if the OCM is secured and the Cortex-R5F processor + * is non-secured, then the Cortex-R5F processor cannot access the + * HIVEC exception vectors in the OCM. + */ + bootmem = (bootaddr >= 0xFFFC0000) ? + PM_RPU_BOOTMEM_HIVEC : PM_RPU_BOOTMEM_LOVEC; + + pr_debug("RPU boot addr 0x%llx from %s.", bootaddr, + bootmem == PM_RPU_BOOTMEM_HIVEC ? "OCM" : "TCM"); + + /* Request node before starting RPU core if new version of API is supported */ + if (zynqmp_pm_feature(PM_REQUEST_NODE) > PM_API_VERSION_1) { + ret = zynqmp_pm_request_node(node, + ZYNQMP_PM_CAPABILITY_ACCESS, 0, + ZYNQMP_PM_REQUEST_ACK_BLOCKING); + if (ret < 0) { + pr_err("failed to request 0x%x", node); + return ret; + } + } + + ret = zynqmp_pm_request_wake(node, true, + bootmem, ZYNQMP_PM_REQUEST_ACK_NO); + if (ret) + pr_err("failed to start RPU = 0x%x\n", node); + return ret; +} +EXPORT_SYMBOL_GPL(zynqmp_pm_start_rpu); + +/** + * zynqmp_pm_stop_rpu - Stop Real-time Processing Unit (Cortex-R) on SoC + * + * @node: power-domains id of the core + * + * Return: status, either success or error+reason + */ +int zynqmp_pm_stop_rpu(const u32 node) +{ + int ret; + + /* Use release node API to stop core if new version of API is supported */ + if (zynqmp_pm_feature(PM_RELEASE_NODE) > PM_API_VERSION_1) { + ret = zynqmp_pm_release_node(node); + if (ret) + pr_err("failed to stop remoteproc RPU %d\n", ret); + return ret; + } + + /* + * Check expected version of EEMI call before calling it. This avoids + * any error or warning prints from firmware as it is expected that fw + * doesn't support it. + */ + if (zynqmp_pm_feature(PM_FORCE_POWERDOWN) != PM_API_VERSION_1) { + pr_debug("EEMI interface %d ver 1 not supported\n", + PM_FORCE_POWERDOWN); + return -EOPNOTSUPP; + } + + /* maintain force pwr down for backward compatibility */ + ret = zynqmp_pm_force_pwrdwn(node, ZYNQMP_PM_REQUEST_ACK_BLOCKING); + if (ret) + pr_err("core force power down failed\n"); + return ret; +} +EXPORT_SYMBOL_GPL(zynqmp_pm_stop_rpu); + +/** * zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves * @node: Node ID of the slave * @capabilities: Requested capabilities of the slave diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index cc8e952a6772..a509b73da190 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -472,7 +472,7 @@ static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) out: mutex_unlock(&hwspinlock_tree_lock); - return 0; + return ret; } static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index c521c744e7db..5b56b2dcc725 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -23,6 +23,15 @@ config REMOTEPROC_CDEV It's safe to say N if you don't want to use this interface. +config AMD_MBV_BRAM_REMOTEPROC + tristate "AMD MicroBlaze/V BRAM-based remoteproc support" + depends on OF && COMMON_CLK && (GPIOLIB || COMPILE_TEST) + help + Say y or m here to support a MicroBlaze/V BRAM-based remote + processor managed through the remoteproc framework. + + If unsure, say N. + config IMX_REMOTEPROC tristate "i.MX remoteproc support" depends on ARCH_MXC @@ -210,6 +219,7 @@ config QCOM_Q6V5_MSS select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM + select QCOM_PAS help Say y here to support the Qualcomm self-authenticating modem subsystem based on Hexagon V5. The TrustZone based system is @@ -230,6 +240,7 @@ config QCOM_Q6V5_PAS select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM + select QCOM_PAS help Say y here to support the TrustZone based Peripheral Image Loader for the Qualcomm remote processors. This is commonly used to control @@ -282,7 +293,7 @@ config QCOM_WCNSS_PIL select QCOM_MDT_LOADER select QCOM_PIL_INFO select QCOM_RPROC_COMMON - select QCOM_SCM + select QCOM_PAS help Say y here to support the Peripheral Image Loader for loading WCNSS firmware and boot the core on e.g. MSM8974, MSM8916. The firmware is diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 1c7598b8475d..689686de0d41 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_elf_loader.o obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o +obj-$(CONFIG_AMD_MBV_BRAM_REMOTEPROC) += amd_mbv_bram_rproc.o obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o diff --git a/drivers/remoteproc/amd_mbv_bram_rproc.c b/drivers/remoteproc/amd_mbv_bram_rproc.c new file mode 100644 index 000000000000..e4a103cf8455 --- /dev/null +++ b/drivers/remoteproc/amd_mbv_bram_rproc.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD MicroBlaze/V BRAM-based Remote Processor driver + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + * + * This driver supports soft-core processors (MicroBlaze, MicroBlaze-V, or + * similar) instantiated in AMD programmable logic, using dual-port BRAM + * for firmware storage and execution. + * + * The firmware memory (BRAM) is described in the processor-local address + * space and translated to the Linux-visible system physical address with + * standard devicetree address translation. + * + * Reset is controlled via GPIO connected to Processor System Reset IP. + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> + +#include "remoteproc_internal.h" + +/** + * struct amd_bram_rproc - AMD MicroBlaze/V BRAM-based remoteproc private data + * @dev: device pointer + * @reset: GPIO descriptor for reset control (active-low) + * @clk: processor clock + */ +struct amd_bram_rproc { + struct device *dev; + struct gpio_desc *reset; + struct clk *clk; +}; + +static int amd_bram_rproc_prepare(struct rproc *rproc) +{ + struct amd_bram_rproc *priv = rproc->priv; + struct rproc_mem_entry *mem; + struct resource res; + u64 da, size; + int ret; + + ret = of_property_read_reg(priv->dev->of_node, 0, &da, &size); + if (ret) { + dev_err(priv->dev, "failed to parse executable memory reg\n"); + return ret; + } + + if (!size || size > U32_MAX) { + dev_err(priv->dev, "invalid executable memory size\n"); + return -EINVAL; + } + + if (da > U32_MAX) { + dev_err(priv->dev, "invalid executable memory address\n"); + return -EINVAL; + } + + ret = of_address_to_resource(priv->dev->of_node, 0, &res); + if (ret) { + dev_err(priv->dev, "failed to translate executable memory reg\n"); + return ret; + } + + mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)res.start, + resource_size(&res), da, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, + dev_name(priv->dev)); + if (!mem) + return -ENOMEM; + + rproc_add_carveout(rproc, mem); + rproc_coredump_add_segment(rproc, da, resource_size(&res)); + + return 0; +} + +static int amd_bram_rproc_start(struct rproc *rproc) +{ + struct amd_bram_rproc *priv = rproc->priv; + int ret; + + /* Enable clock before releasing reset */ + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(priv->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + /* Deassert reset and let the processor run. */ + ret = gpiod_set_value_cansleep(priv->reset, 0); + if (ret) { + dev_err(priv->dev, "failed to deassert reset: %d\n", ret); + clk_disable_unprepare(priv->clk); + return ret; + } + + return 0; +} + +static int amd_bram_rproc_stop(struct rproc *rproc) +{ + struct amd_bram_rproc *priv = rproc->priv; + int ret; + + /* Assert reset before disabling the processor clock. */ + ret = gpiod_set_value_cansleep(priv->reset, 1); + if (ret) { + dev_err(priv->dev, "failed to assert reset: %d\n", ret); + return ret; + } + + /* Disable clock after asserting reset */ + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int amd_bram_rproc_parse_fw(struct rproc *rproc, + const struct firmware *fw) +{ + rproc_elf_load_rsc_table_optional(rproc, fw, dev_dbg, + "no resource table found\n"); + return 0; +} + +static const struct rproc_ops amd_bram_rproc_ops = { + .prepare = amd_bram_rproc_prepare, + .start = amd_bram_rproc_start, + .stop = amd_bram_rproc_stop, + .load = rproc_elf_load_segments, + .sanity_check = rproc_elf_sanity_check, + .get_boot_addr = rproc_elf_get_boot_addr, + .parse_fw = amd_bram_rproc_parse_fw, +}; + +static int amd_bram_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct amd_bram_rproc *priv; + const char *fw_name = NULL; + struct rproc *rproc; + int ret; + + ret = rproc_of_parse_firmware(dev, 0, &fw_name); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "failed to parse firmware-name property\n"); + + rproc = devm_rproc_alloc(dev, dev_name(dev), &amd_bram_rproc_ops, + fw_name, sizeof(*priv)); + if (!rproc) + return -ENOMEM; + + priv = rproc->priv; + priv->dev = dev; + + /* Get the processor clock */ + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "failed to get clock\n"); + + /* + * Keep the processor in reset until remoteproc has finished loading + * firmware into the executable memory window described by reg and + * translated through the parent bus ranges property. + */ + priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset)) + return dev_err_probe(dev, PTR_ERR(priv->reset), + "failed to get reset gpio\n"); + + rproc->auto_boot = false; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) + return dev_err_probe(dev, ret, "failed to set DMA mask\n"); + + platform_set_drvdata(pdev, rproc); + + ret = devm_rproc_add(dev, rproc); + if (ret) + return dev_err_probe(dev, ret, "failed to register rproc\n"); + + return 0; +} + +static const struct of_device_id amd_bram_rproc_of_match[] = { + { .compatible = "xlnx,zynqmp-bram-rproc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, amd_bram_rproc_of_match); + +static struct platform_driver amd_bram_rproc_driver = { + .probe = amd_bram_rproc_probe, + .driver = { + .name = "amd-bram-rproc", + .of_match_table = amd_bram_rproc_of_match, + }, +}; +module_platform_driver(amd_bram_rproc_driver); + +MODULE_DESCRIPTION("AMD MicroBlaze/V BRAM-based Remote Processor driver"); +MODULE_AUTHOR("Ben Levinsky <ben.levinsky@amd.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 23fca7176539..006fa7b56727 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -298,7 +298,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev) handle_event, 0, "da8xx-remoteproc", rproc); if (ret) - return dev_err_probe(dev, ret, "devm_request_threaded_irq error\n"); + return ret; /* * rproc_add() can end up enabling the DSP's clk with the DSP diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index 008741af9f11..fd60c67ba8a9 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -644,32 +644,6 @@ static void imx_dsp_rproc_free_mbox(struct imx_dsp_rproc *priv) mbox_free_channel(priv->rxdb_ch); } -static int imx_dsp_rproc_mem_alloc(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - struct device *dev = rproc->dev.parent; - void *va; - - va = ioremap_wc(mem->dma, mem->len); - if (!va) { - dev_err(dev, "Unable to map memory region: %pa+%zx\n", - &mem->dma, mem->len); - return -ENOMEM; - } - - mem->va = va; - - return 0; -} - -static int imx_dsp_rproc_mem_release(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - iounmap(mem->va); - - return 0; -} - /** * imx_dsp_rproc_add_carveout() - request mailbox channels * @priv: private data pointer @@ -700,8 +674,10 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) /* Register memory region */ mem = rproc_mem_entry_init(dev, NULL, (dma_addr_t)att->sa, - att->size, da, imx_dsp_rproc_mem_alloc, - imx_dsp_rproc_mem_release, "dsp_mem"); + att->size, da, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, + "dsp_mem"); if (mem) rproc_coredump_add_segment(rproc, da, att->size); @@ -732,8 +708,8 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) /* Register memory region */ mem = rproc_mem_entry_init(dev, NULL, (dma_addr_t)res.start, resource_size(&res), da, - imx_dsp_rproc_mem_alloc, - imx_dsp_rproc_mem_release, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, "%.*s", strchrnul(res.name, '@') - res.name, res.name); if (!mem) return -ENOMEM; @@ -980,9 +956,8 @@ static int imx_dsp_rproc_elf_load_segments(struct rproc *rproc, const struct fir static int imx_dsp_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { - if (rproc_elf_load_rsc_table(rproc, fw)) - dev_warn(&rproc->dev, "no resource table found for this firmware\n"); - + rproc_elf_load_rsc_table_optional(rproc, fw, dev_warn, + "no resource table found for this firmware\n"); return 0; } diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 7662ebd9d2f4..745ce52cd822 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -600,35 +600,6 @@ static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *i return va; } -static int imx_rproc_mem_alloc(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - struct device *dev = rproc->dev.parent; - void *va; - - dev_dbg(dev, "map memory: %p+%zx\n", &mem->dma, mem->len); - va = ioremap_wc(mem->dma, mem->len); - if (IS_ERR_OR_NULL(va)) { - dev_err(dev, "Unable to map memory region: %p+%zx\n", - &mem->dma, mem->len); - return -ENOMEM; - } - - /* Update memory entry va */ - mem->va = va; - - return 0; -} - -static int imx_rproc_mem_release(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma); - iounmap(mem->va); - - return 0; -} - static int imx_rproc_sm_lmm_prepare(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; @@ -692,7 +663,8 @@ static int imx_rproc_prepare(struct rproc *rproc) /* Register memory region */ mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)res.start, resource_size(&res), da, - imx_rproc_mem_alloc, imx_rproc_mem_release, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, "%.*s", strchrnul(res.name, '@') - res.name, res.name); if (!mem) @@ -710,12 +682,8 @@ static int imx_rproc_prepare(struct rproc *rproc) static int imx_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { - int ret; - - ret = rproc_elf_load_rsc_table(rproc, fw); - if (ret) - dev_info(&rproc->dev, "No resource table in elf\n"); - + rproc_elf_load_rsc_table_optional(rproc, fw, dev_info, + "No resource table in elf\n"); return 0; } diff --git a/drivers/remoteproc/ingenic_rproc.c b/drivers/remoteproc/ingenic_rproc.c index 1b78d8ddeacf..252519f48964 100644 --- a/drivers/remoteproc/ingenic_rproc.c +++ b/drivers/remoteproc/ingenic_rproc.c @@ -219,10 +219,8 @@ static int ingenic_rproc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, vpu->irq, vpu_interrupt, IRQF_NO_AUTOEN, "VPU", rproc); - if (ret < 0) { - dev_err(dev, "Failed to request IRQ\n"); + if (ret < 0) return ret; - } ret = devm_rproc_add(dev, rproc); if (ret) { diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index e7fde5509786..407d6034f748 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -317,7 +317,7 @@ static int keystone_rproc_of_get_dev_syscon(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - if (!of_property_read_bool(np, "ti,syscon-dev")) { + if (!of_property_present(np, "ti,syscon-dev")) { dev_err(dev, "ti,syscon-dev property is absent\n"); return -EINVAL; } @@ -411,7 +411,7 @@ static int keystone_rproc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ksproc->irq_ring, keystone_rproc_vring_interrupt, IRQF_NO_AUTOEN, dev_name(dev), ksproc); if (ret) - return dev_err_probe(dev, ret, "failed to request vring interrupt\n"); + return ret; ksproc->irq_fault = platform_get_irq_byname(pdev, "exception"); if (ksproc->irq_fault < 0) @@ -419,7 +419,7 @@ static int keystone_rproc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ksproc->irq_fault, keystone_rproc_exception_interrupt, IRQF_NO_AUTOEN, dev_name(dev), ksproc); if (ret) - return dev_err_probe(dev, ret, "failed to enable exception interrupt\n"); + return ret; ksproc->kick_gpio = devm_gpiod_get(dev, "kick", GPIOD_ASIS); ret = PTR_ERR_OR_ZERO(ksproc->kick_gpio); diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 85a74c9ec521..9751acc2bf83 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -36,6 +36,7 @@ struct mtk_scp *scp_get(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *scp_node; struct platform_device *scp_pdev; + struct mtk_scp *scp; scp_node = of_parse_phandle(dev->of_node, "mediatek,scp", 0); if (!scp_node) { @@ -51,7 +52,13 @@ struct mtk_scp *scp_get(struct platform_device *pdev) return NULL; } - return platform_get_drvdata(scp_pdev); + scp = platform_get_drvdata(scp_pdev); + if (!scp) { + put_device(&scp_pdev->dev); + return NULL; + } + + return scp; } EXPORT_SYMBOL_GPL(scp_get); @@ -1239,10 +1246,8 @@ static struct mtk_scp *scp_rproc_init(struct platform_device *pdev, scp_irq_handler, IRQF_ONESHOT, pdev->name, scp); - if (ret) { - dev_err(dev, "failed to request irq\n"); + if (ret) goto remove_subdev; - } return scp; diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index cb01354248af..6ed0f28edac9 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -1140,7 +1140,7 @@ static int omap_rproc_get_boot_data(struct platform_device *pdev, if (!data) return -ENODEV; - if (!of_property_read_bool(np, "ti,bootreg")) + if (!of_property_present(np, "ti,bootreg")) return 0; oproc->boot_data = devm_kzalloc(&pdev->dev, sizeof(*oproc->boot_data), diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index e1a955476c9b..5294e327f158 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -253,8 +253,10 @@ void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink, return; glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL); - if (!glink->ssr_name) + if (!glink->ssr_name) { + of_node_put(glink->node); return; + } glink->dev = dev; glink->subdev.start = glink_subdev_start; diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index b07fbaa091a0..b0e7e336d363 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -68,6 +68,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, int ssctl_instance); void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon); bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon); +bool qcom_sysmon_shutdown_irq_state(struct qcom_sysmon *sysmon); #else static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, const char *name, @@ -84,6 +85,11 @@ static inline bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon) { return false; } + +static inline bool qcom_sysmon_shutdown_irq_state(struct qcom_sysmon *sysmon) +{ + return false; +} #endif #endif diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c index 58d5b85e58cd..fe148b4b3775 100644 --- a/drivers/remoteproc/qcom_q6v5.c +++ b/drivers/remoteproc/qcom_q6v5.c @@ -36,6 +36,40 @@ static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable) return ret; } +static void q6v5_handover_irq_enable(struct qcom_q6v5 *q6v5) +{ + unsigned long flags; + bool enable = false; + + spin_lock_irqsave(&q6v5->handover_lock, flags); + if (!q6v5->handover_irq_enabled) { + q6v5->handover_irq_enabled = true; + enable = true; + } + spin_unlock_irqrestore(&q6v5->handover_lock, flags); + + if (enable) + enable_irq(q6v5->handover_irq); +} + +static void q6v5_handover_irq_disable(struct qcom_q6v5 *q6v5, bool sync) +{ + unsigned long flags; + bool disable = false; + + spin_lock_irqsave(&q6v5->handover_lock, flags); + if (q6v5->handover_irq_enabled) { + q6v5->handover_irq_enabled = false; + disable = true; + } + spin_unlock_irqrestore(&q6v5->handover_lock, flags); + + if (disable) + disable_irq_nosync(q6v5->handover_irq); + if (sync) + synchronize_irq(q6v5->handover_irq); +} + /** * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start * @q6v5: reference to qcom_q6v5 context to be reinitialized @@ -64,7 +98,7 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5) q6v5->running = true; q6v5->handover_issued = false; - enable_irq(q6v5->handover_irq); + q6v5_handover_irq_enable(q6v5); return 0; } @@ -78,7 +112,8 @@ EXPORT_SYMBOL_GPL(qcom_q6v5_prepare); */ int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5) { - disable_irq(q6v5->handover_irq); + q6v5_handover_irq_disable(q6v5, true); + q6v5_load_state_toggle(q6v5, false); /* Disable interconnect vote, in case handover never happened */ @@ -164,18 +199,15 @@ static irqreturn_t q6v5_handover_interrupt(int irq, void *data) { struct qcom_q6v5 *q6v5 = data; - if (q6v5->handover_issued) { - dev_err(q6v5->dev, "Handover signaled, but it already happened\n"); - return IRQ_HANDLED; - } + q6v5->handover_issued = true; + + q6v5_handover_irq_disable(q6v5, false); if (q6v5->handover) q6v5->handover(q6v5); icc_set_bw(q6v5->path, 0, 0); - q6v5->handover_issued = true; - return IRQ_HANDLED; } @@ -197,12 +229,13 @@ static irqreturn_t q6v5_stop_interrupt(int irq, void *data) */ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon) { + bool was_running = q6v5->running; int ret; q6v5->running = false; - /* Don't perform SMP2P dance if remote isn't running */ - if (q6v5->rproc->state != RPROC_RUNNING || qcom_sysmon_shutdown_acked(sysmon)) + /* A watchdog/fatal IRQ clears running; logical crashes still need a stop. */ + if (!was_running || qcom_sysmon_shutdown_acked(sysmon)) return 0; qcom_smem_state_update_bits(q6v5->state, @@ -256,6 +289,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, q6v5->crash_reason = crash_reason; q6v5->handover = handover; + spin_lock_init(&q6v5->handover_lock); + init_completion(&q6v5->start_done); init_completion(&q6v5->stop_done); @@ -267,10 +302,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, NULL, q6v5_wdog_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "q6v5 wdog", q6v5); - if (ret) { - dev_err(&pdev->dev, "failed to acquire wdog IRQ\n"); + if (ret) return ret; - } q6v5->fatal_irq = platform_get_irq_byname(pdev, "fatal"); if (q6v5->fatal_irq < 0) @@ -280,10 +313,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, NULL, q6v5_fatal_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "q6v5 fatal", q6v5); - if (ret) { - dev_err(&pdev->dev, "failed to acquire fatal IRQ\n"); + if (ret) return ret; - } q6v5->ready_irq = platform_get_irq_byname(pdev, "ready"); if (q6v5->ready_irq < 0) @@ -293,10 +324,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, NULL, q6v5_ready_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "q6v5 ready", q6v5); - if (ret) { - dev_err(&pdev->dev, "failed to acquire ready IRQ\n"); + if (ret) return ret; - } q6v5->handover_irq = platform_get_irq_byname(pdev, "handover"); if (q6v5->handover_irq < 0) @@ -304,13 +333,11 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq, NULL, q6v5_handover_interrupt, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + IRQF_NO_AUTOEN, "q6v5 handover", q6v5); - if (ret) { - dev_err(&pdev->dev, "failed to acquire handover IRQ\n"); + if (ret) return ret; - } - disable_irq(q6v5->handover_irq); q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack"); if (q6v5->stop_irq < 0) @@ -320,10 +347,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, NULL, q6v5_stop_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "q6v5 stop", q6v5); - if (ret) { - dev_err(&pdev->dev, "failed to acquire stop-ack IRQ\n"); + if (ret) return ret; - } q6v5->state = devm_qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit); if (IS_ERR(q6v5->state)) { diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h index 5a859c41896e..8991ff090579 100644 --- a/drivers/remoteproc/qcom_q6v5.h +++ b/drivers/remoteproc/qcom_q6v5.h @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/completion.h> +#include <linux/spinlock.h> #include <linux/soc/qcom/qcom_aoss.h> struct icc_path; @@ -29,6 +30,9 @@ struct qcom_q6v5 { int handover_irq; int stop_irq; + /* Protects handover_irq_enabled against stop/handover races. */ + spinlock_t handover_lock; + bool handover_irq_enabled; bool handover_issued; struct completion start_done; diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index b5c8d6d38c9c..c81e6c33c747 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -355,6 +355,7 @@ static int adsp_map_carveout(struct rproc *rproc) return ret; sid = args.args[0] & SID_MASK_DEFAULT; + of_node_put(args.np); /* Add SID configuration for ADSP Firmware to SMMU */ iova = adsp->mem_phys | (sid << 32); diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index ae78f5c7c1b6..bef198b9ee63 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -34,6 +34,7 @@ #include "qcom_pil_info.h" #include "qcom_q6v5.h" +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/firmware/qcom/qcom_scm.h> #define MPSS_CRASH_REASON_SMEM 421 @@ -162,6 +163,7 @@ struct rproc_hexagon_res { char **active_clk_names; char **proxy_pd_names; int version; + int ssctl_id; bool need_mem_protection; bool need_pas_mem_setup; bool has_alt_reset; @@ -1480,7 +1482,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) } if (qproc->need_pas_mem_setup) { - ret = qcom_scm_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size); + ret = qcom_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size); if (ret) { dev_err(qproc->dev, "setting up mpss memory failed: %d\n", ret); @@ -2077,7 +2079,7 @@ static int q6v5_probe(struct platform_device *pdev) if (!desc) return -EINVAL; - if (desc->need_mem_protection && !qcom_scm_is_available()) + if (desc->need_mem_protection && !qcom_pas_is_available()) return -EPROBE_DEFER; mba_image = desc->hexagon_mba_image; @@ -2191,7 +2193,7 @@ static int q6v5_probe(struct platform_device *pdev) qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_pdm_subdev(rproc, &qproc->pdm_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); - qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); + qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", desc->ssctl_id); if (IS_ERR(qproc->sysmon)) { ret = PTR_ERR(qproc->sysmon); goto remove_subdevs; @@ -2271,6 +2273,7 @@ static const struct rproc_hexagon_res sc7180_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_SC7180, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res sc7280_mss = { @@ -2301,6 +2304,7 @@ static const struct rproc_hexagon_res sc7280_mss = { .has_ext_cntl_regs = true, .has_vq6 = true, .version = MSS_SC7280, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res sdm660_mss = { @@ -2334,6 +2338,7 @@ static const struct rproc_hexagon_res sdm660_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_SDM660, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res sdm845_mss = { @@ -2371,6 +2376,7 @@ static const struct rproc_hexagon_res sdm845_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_SDM845, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8998_mss = { @@ -2404,6 +2410,7 @@ static const struct rproc_hexagon_res msm8998_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8998, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8996_mss = { @@ -2444,6 +2451,7 @@ static const struct rproc_hexagon_res msm8996_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8996, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res mdm9607_mss = { @@ -2479,6 +2487,7 @@ static const struct rproc_hexagon_res mdm9607_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MDM9607, + .ssctl_id = 0x22, }; static const struct rproc_hexagon_res msm8909_mss = { @@ -2515,6 +2524,7 @@ static const struct rproc_hexagon_res msm8909_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8909, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8916_mss = { @@ -2562,6 +2572,7 @@ static const struct rproc_hexagon_res msm8916_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8916, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8917_mss = { @@ -2606,6 +2617,7 @@ static const struct rproc_hexagon_res msm8917_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8917, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8937_mss = { @@ -2650,6 +2662,7 @@ static const struct rproc_hexagon_res msm8937_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8937, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8940_mss = { @@ -2694,6 +2707,7 @@ static const struct rproc_hexagon_res msm8940_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8940, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8953_mss = { @@ -2731,6 +2745,7 @@ static const struct rproc_hexagon_res msm8953_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8953, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8974_mss = { @@ -2785,6 +2800,7 @@ static const struct rproc_hexagon_res msm8974_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8974, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8226_mss = { @@ -2824,6 +2840,7 @@ static const struct rproc_hexagon_res msm8226_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8226, + .ssctl_id = 0x12, }; static const struct rproc_hexagon_res msm8926_mss = { @@ -2871,6 +2888,7 @@ static const struct rproc_hexagon_res msm8926_mss = { .has_ext_cntl_regs = false, .has_vq6 = false, .version = MSS_MSM8926, + .ssctl_id = 0x12, }; static const struct of_device_id q6v5_of_match[] = { diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 808e9609988d..25599d728208 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -16,10 +16,12 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_platform.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/firmware/qcom/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> @@ -60,6 +62,8 @@ struct qcom_pas_data { int region_assign_count; bool region_assign_shared; int region_assign_vmid; + bool early_boot; + bool needs_tzmem; }; struct qcom_pas { @@ -114,9 +118,10 @@ struct qcom_pas { struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; + struct platform_device *bam_dmux; - struct qcom_scm_pas_context *pas_ctx; - struct qcom_scm_pas_context *dtb_pas_ctx; + struct qcom_pas_context *pas_ctx; + struct qcom_pas_context *dtb_pas_ctx; }; static void qcom_pas_segment_dump(struct rproc *rproc, @@ -193,7 +198,7 @@ static int qcom_pas_shutdown_poll_decrypt(struct qcom_pas *pas) do { msleep(QCOM_PAS_DECRYPT_SHUTDOWN_DELAY_MS); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_pas_shutdown(pas->pas_id); } while (ret == -EINVAL && --retry_num); return ret; @@ -209,9 +214,9 @@ static int qcom_pas_unprepare(struct rproc *rproc) * auth_and_reset() was successful, but in other cases clean it up * here. */ - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); return 0; } @@ -225,9 +230,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) pas->firmware = fw; if (pas->lite_pas_id) - qcom_scm_pas_shutdown(pas->lite_pas_id); + qcom_pas_shutdown(pas->lite_pas_id); if (pas->lite_dtb_pas_id) - qcom_scm_pas_shutdown(pas->lite_dtb_pas_id); + qcom_pas_shutdown(pas->lite_dtb_pas_id); if (pas->dtb_pas_id) { ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name, pas->dev); @@ -247,7 +252,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) return 0; release_dtb_metadata: - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); release_firmware(pas->dtb_firmware); return ret; @@ -307,7 +312,7 @@ static int qcom_pas_start(struct rproc *rproc) if (ret) goto disable_px_supply; - ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx); + ret = qcom_pas_prepare_and_auth_reset(pas->dtb_pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate dtb image and release reset\n"); @@ -326,7 +331,7 @@ static int qcom_pas_start(struct rproc *rproc) if (ret) goto release_pas_metadata; - ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx); + ret = qcom_pas_prepare_and_auth_reset(pas->pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate image and release reset\n"); @@ -336,13 +341,13 @@ static int qcom_pas_start(struct rproc *rproc) ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000)); if (ret == -ETIMEDOUT) { dev_err(pas->dev, "start timed out\n"); - qcom_scm_pas_shutdown(pas->pas_id); + qcom_pas_shutdown(pas->pas_id); goto unmap_carveout; } - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); /* firmware is used to pass reference from qcom_pas_start(), drop it now */ pas->firmware = NULL; @@ -352,9 +357,9 @@ static int qcom_pas_start(struct rproc *rproc) unmap_carveout: qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size); release_pas_metadata: - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); unmap_dtb_carveout: if (pas->dtb_pas_id) @@ -403,7 +408,7 @@ static int qcom_pas_stop(struct rproc *rproc) if (ret == -ETIMEDOUT) dev_err(pas->dev, "timed out on wait\n"); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_pas_shutdown(pas->pas_id); if (ret && pas->decrypt_shutdown) ret = qcom_pas_shutdown_poll_decrypt(pas); @@ -411,7 +416,7 @@ static int qcom_pas_stop(struct rproc *rproc) dev_err(pas->dev, "failed to shutdown: %d\n", ret); if (pas->dtb_pas_id) { - ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); + ret = qcom_pas_shutdown(pas->dtb_pas_id); if (ret) dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); @@ -481,11 +486,11 @@ static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *f * * Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources * or not and if it is not having then we pass NULL and zero as input resource - * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table() + * table pointer and size respectively to the argument of qcom_pas_get_rsc_table() * and this is even true for Qualcomm remote processor who does follow remoteproc * framework. */ - output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size); + output_rt = qcom_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size); ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0; if (ret) { dev_err(pas->dev, "Error in getting resource table: %d\n", ret); @@ -507,6 +512,67 @@ static unsigned long qcom_pas_panic(struct rproc *rproc) return qcom_q6v5_panic(&pas->q6v5); } +static int qcom_pas_attach(struct rproc *rproc) +{ + struct qcom_pas *pas = rproc->priv; + bool ready_state; + bool crash_state; + bool stop_state; + int ret; + + pas->q6v5.handover_issued = true; + enable_irq(pas->q6v5.handover_irq); + + pas->q6v5.running = true; + ret = irq_get_irqchip_state(pas->q6v5.fatal_irq, + IRQCHIP_STATE_LINE_LEVEL, &crash_state); + if (ret) + goto disable_running; + + if (crash_state) { + dev_err(pas->dev, "Subsystem has crashed before driver probe\n"); + rproc_report_crash(rproc, RPROC_FATAL_ERROR); + ret = -EINVAL; + goto disable_running; + } + + ret = irq_get_irqchip_state(pas->q6v5.stop_irq, + IRQCHIP_STATE_LINE_LEVEL, &stop_state); + if (ret) + goto disable_running; + + if (stop_state || qcom_sysmon_shutdown_irq_state(pas->sysmon)) { + dev_info(pas->dev, "Subsystem found stop state set. Falling back to start.\n"); + goto unroll_attach; + } + + ret = irq_get_irqchip_state(pas->q6v5.ready_irq, + IRQCHIP_STATE_LINE_LEVEL, &ready_state); + if (ret) + goto disable_running; + + if (unlikely(!ready_state)) { + /* + * The bootloader may not support early boot, mark the state as + * RPROC_OFFLINE so that the PAS driver can load the firmware and + * start the remoteproc. + */ + dev_err(pas->dev, "Failed to get subsystem ready interrupt\n"); + goto unroll_attach; + } + + return 0; + +unroll_attach: + pas->rproc->state = RPROC_OFFLINE; + ret = -EINVAL; +disable_running: + disable_irq(pas->q6v5.handover_irq); + pas->q6v5.running = false; + + return ret; +} + static const struct rproc_ops qcom_pas_ops = { .unprepare = qcom_pas_unprepare, .start = qcom_pas_start, @@ -515,6 +581,7 @@ static const struct rproc_ops qcom_pas_ops = { .parse_fw = qcom_pas_parse_firmware, .load = qcom_pas_load, .panic = qcom_pas_panic, + .attach = qcom_pas_attach, }; static const struct rproc_ops qcom_pas_minidump_ops = { @@ -526,6 +593,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = { .load = qcom_pas_load, .panic = qcom_pas_panic, .coredump = qcom_pas_minidump, + .attach = qcom_pas_attach, }; static int qcom_pas_init_clock(struct qcom_pas *pas) @@ -735,6 +803,7 @@ static int qcom_pas_probe(struct platform_device *pdev) const struct qcom_pas_data *desc; struct qcom_pas *pas; struct rproc *rproc; + struct device_node *node; const char *fw_name, *dtb_fw_name = NULL; const struct rproc_ops *ops = &qcom_pas_ops; int ret; @@ -743,7 +812,7 @@ static int qcom_pas_probe(struct platform_device *pdev) if (!desc) return -EINVAL; - if (!qcom_scm_is_available()) + if (!qcom_pas_is_available()) return -EPROBE_DEFER; fw_name = desc->firmware_name; @@ -835,27 +904,35 @@ static int qcom_pas_probe(struct platform_device *pdev) qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name); - pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id, - pas->mem_phys, pas->mem_size); + pas->pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->pas_id, + pas->mem_phys, pas->mem_size); if (IS_ERR(pas->pas_ctx)) { ret = PTR_ERR(pas->pas_ctx); goto remove_ssr_sysmon; } - pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id, - pas->dtb_mem_phys, - pas->dtb_mem_size); + pas->dtb_pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->dtb_pas_id, + pas->dtb_mem_phys, + pas->dtb_mem_size); if (IS_ERR(pas->dtb_pas_ctx)) { ret = PTR_ERR(pas->dtb_pas_ctx); goto remove_ssr_sysmon; } - pas->pas_ctx->use_tzmem = rproc->has_iommu; - pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu; + pas->pas_ctx->use_tzmem = desc->needs_tzmem || rproc->has_iommu; + pas->dtb_pas_ctx->use_tzmem = desc->needs_tzmem || rproc->has_iommu; + + if (desc->early_boot) + pas->rproc->state = RPROC_DETACHED; + ret = rproc_add(rproc); if (ret) goto remove_ssr_sysmon; + node = of_get_compatible_child(pdev->dev.of_node, "qcom,bam-dmux"); + pas->bam_dmux = of_platform_device_create(node, NULL, &pdev->dev); + of_node_put(node); + return 0; remove_ssr_sysmon: @@ -880,6 +957,9 @@ static void qcom_pas_remove(struct platform_device *pdev) { struct qcom_pas *pas = platform_get_drvdata(pdev); + if (pas->bam_dmux) + of_platform_device_destroy(&pas->bam_dmux->dev, NULL); + rproc_del(pas->rproc); qcom_q6v5_deinit(&pas->q6v5); @@ -1572,8 +1652,45 @@ static const struct qcom_pas_data sm8750_mpss_resource = { .region_assign_vmid = QCOM_SCM_VMID_MSS_MSA, }; +static const struct qcom_pas_data kaanapali_soccp_resource = { + .crash_reason_smem = 656, + .firmware_name = "soccp.mbn", + .dtb_firmware_name = "soccp_dtb.mbn", + .pas_id = 51, + .dtb_pas_id = 0x41, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + NULL + }, + .ssr_name = "soccp", + .sysmon_name = "soccp", + .auto_boot = true, + .early_boot = true, +}; + +static const struct qcom_pas_data glymur_soccp_resource = { + .crash_reason_smem = 656, + .firmware_name = "soccp.mbn", + .dtb_firmware_name = "soccp_dtb.mbn", + .pas_id = 51, + .dtb_pas_id = 0x41, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + NULL + }, + .ssr_name = "soccp", + .sysmon_name = "soccp", + .auto_boot = true, + .early_boot = true, + .needs_tzmem = true, +}; + static const struct of_device_id qcom_pas_of_match[] = { { .compatible = "qcom,eliza-adsp-pas", .data = &sm8550_adsp_resource }, + { .compatible = "qcom,glymur-soccp-pas", .data = &glymur_soccp_resource }, + { .compatible = "qcom,kaanapali-soccp-pas", .data = &kaanapali_soccp_resource }, { .compatible = "qcom,milos-adsp-pas", .data = &sm8550_adsp_resource }, { .compatible = "qcom,milos-cdsp-pas", .data = &milos_cdsp_resource }, { .compatible = "qcom,milos-mpss-pas", .data = &sm8450_mpss_resource }, diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 913e3b750a86..61e1038328e8 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -662,8 +662,6 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "q6v5 shutdown-ack", sysmon); if (ret) { - dev_err(sysmon->dev, - "failed to acquire shutdown-ack IRQ\n"); kfree(sysmon); return ERR_PTR(ret); } @@ -736,6 +734,25 @@ bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon) } EXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_acked); +bool qcom_sysmon_shutdown_irq_state(struct qcom_sysmon *sysmon) +{ + bool shutdown_state; + int ret; + + if (!sysmon) + return false; + + ret = irq_get_irqchip_state(sysmon->shutdown_irq, + IRQCHIP_STATE_LINE_LEVEL, &shutdown_state); + if (ret) { + dev_warn(sysmon->dev, "failed to get shutdown_state: %d\n", ret); + return false; + } + + return shutdown_state; +} +EXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_irq_state); + /** * sysmon_probe() - probe sys_mon channel * @rpdev: rpmsg device handle diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 4add9037dbd5..e9a00efe97f6 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -19,7 +19,7 @@ #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> -#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> #include <linux/soc/qcom/mdt_loader.h> @@ -257,7 +257,7 @@ static int wcnss_start(struct rproc *rproc) wcnss_indicate_nv_download(wcnss); wcnss_configure_iris(wcnss); - ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID); + ret = qcom_pas_auth_and_reset(WCNSS_PAS_ID); if (ret) { dev_err(wcnss->dev, "failed to authenticate image and release reset\n"); @@ -269,7 +269,7 @@ static int wcnss_start(struct rproc *rproc) if (wcnss->ready_irq > 0 && ret == 0) { /* We have a ready_irq, but it didn't fire in time. */ dev_err(wcnss->dev, "start timed out\n"); - qcom_scm_pas_shutdown(WCNSS_PAS_ID); + qcom_pas_shutdown(WCNSS_PAS_ID); ret = -ETIMEDOUT; goto disable_iris; } @@ -311,7 +311,7 @@ static int wcnss_stop(struct rproc *rproc) 0); } - ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID); + ret = qcom_pas_shutdown(WCNSS_PAS_ID); if (ret) dev_err(wcnss->dev, "failed to shutdown: %d\n", ret); @@ -441,25 +441,31 @@ static void wcnss_release_pds(struct qcom_wcnss *wcnss) } static int wcnss_init_regulators(struct qcom_wcnss *wcnss, - const struct wcnss_vreg_info *info, - int num_vregs, int num_pd_vregs) + const struct wcnss_data *data) { + const struct wcnss_vreg_info *info = data->vregs; struct regulator_bulk_data *bulk; + size_t i, possible_pds = 0, num_vregs = data->num_vregs; int ret; - int i; + + for (i = 0; i < WCNSS_MAX_PDS; i++) + if (data->pd_names[i]) + possible_pds++; /* * If attaching the power domains suceeded we can skip requesting * the regulators for the power domains. For old device trees we need to * reserve extra space to manage them through the regulator interface. */ - if (wcnss->num_pds) { + if (possible_pds >= num_vregs) { + /* Do nothing if vregs do not include PD regulators (pronto-v3) */ + } else if (wcnss->num_pds) { info += wcnss->num_pds; /* Handle single power domain case */ - if (wcnss->num_pds < num_pd_vregs) - num_vregs += num_pd_vregs - wcnss->num_pds; + if (wcnss->num_pds < data->num_pd_vregs) + num_vregs += data->num_pd_vregs - wcnss->num_pds; } else { - num_vregs += num_pd_vregs; + num_vregs += data->num_pd_vregs; } bulk = devm_kcalloc(wcnss->dev, @@ -515,10 +521,8 @@ static int wcnss_request_irq(struct qcom_wcnss *wcnss, NULL, thread_fn, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "wcnss", wcnss); - if (ret) { - dev_err(&pdev->dev, "request %s IRQ failed\n", name); + if (ret) return ret; - } /* Return the IRQ number if the IRQ was successfully acquired */ return irq_number; @@ -557,10 +561,10 @@ static int wcnss_probe(struct platform_device *pdev) data = of_device_get_match_data(&pdev->dev); - if (!qcom_scm_is_available()) + if (!qcom_pas_is_available()) return -EPROBE_DEFER; - if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) { + if (!qcom_pas_supported(WCNSS_PAS_ID)) { dev_err(&pdev->dev, "PAS is not available for WCNSS\n"); return -ENXIO; } @@ -607,8 +611,7 @@ static int wcnss_probe(struct platform_device *pdev) if (ret && (ret != -ENODATA || !data->num_pd_vregs)) return ret; - ret = wcnss_init_regulators(wcnss, data->vregs, data->num_vregs, - data->num_pd_vregs); + ret = wcnss_init_regulators(wcnss, data); if (ret) goto detach_pds; diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c index 3c25625f966d..1fe6c01bde40 100644 --- a/drivers/remoteproc/rcar_rproc.c +++ b/drivers/remoteproc/rcar_rproc.c @@ -19,35 +19,6 @@ struct rcar_rproc { struct reset_control *rst; }; -static int rcar_rproc_mem_alloc(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - struct device *dev = &rproc->dev; - void *va; - - dev_dbg(dev, "map memory: %pa+%zx\n", &mem->dma, mem->len); - va = ioremap_wc(mem->dma, mem->len); - if (!va) { - dev_err(dev, "Unable to map memory region: %pa+%zx\n", - &mem->dma, mem->len); - return -ENOMEM; - } - - /* Update memory entry va */ - mem->va = va; - - return 0; -} - -static int rcar_rproc_mem_release(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - dev_dbg(&rproc->dev, "unmap memory: %pa\n", &mem->dma); - iounmap(mem->va); - - return 0; -} - static int rcar_rproc_prepare(struct rproc *rproc) { struct device *dev = rproc->dev.parent; @@ -73,8 +44,8 @@ static int rcar_rproc_prepare(struct rproc *rproc) mem = rproc_mem_entry_init(dev, NULL, res.start, resource_size(&res), da, - rcar_rproc_mem_alloc, - rcar_rproc_mem_release, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, res.name); if (!mem) @@ -86,12 +57,8 @@ static int rcar_rproc_prepare(struct rproc *rproc) static int rcar_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { - int ret; - - ret = rproc_elf_load_rsc_table(rproc, fw); - if (ret) - dev_info(&rproc->dev, "No resource table in elf\n"); - + rproc_elf_load_rsc_table_optional(rproc, fw, dev_info, + "No resource table in elf\n"); return 0; } diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index f003be006b1b..1ed406714849 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1099,6 +1099,8 @@ static int rproc_start_subdevices(struct rproc *rproc) } } + rproc->subdevs_started = true; + return 0; unroll_registration: @@ -1114,10 +1116,15 @@ static void rproc_stop_subdevices(struct rproc *rproc, bool crashed) { struct rproc_subdev *subdev; + if (!rproc->subdevs_started) + return; + list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { if (subdev->stop) subdev->stop(subdev, crashed); } + + rproc->subdevs_started = false; } static void rproc_unprepare_subdevices(struct rproc *rproc) @@ -1668,18 +1675,21 @@ static void rproc_auto_boot_callback(const struct firmware *fw, void *context) release_firmware(fw); } +static void rproc_attach_work(struct work_struct *work) +{ + struct rproc *rproc = container_of(work, struct rproc, attach_work); + + rproc_boot(rproc); +} + static int rproc_trigger_auto_boot(struct rproc *rproc) { int ret; - /* - * Since the remote processor is in a detached state, it has already - * been booted by another entity. As such there is no point in waiting - * for a firmware image to be loaded, we can simply initiate the process - * of attaching to it immediately. - */ - if (rproc->state == RPROC_DETACHED) - return rproc_boot(rproc); + if (rproc->state == RPROC_DETACHED) { + schedule_work(&rproc->attach_work); + return 0; + } /* * We're initiating an asynchronous firmware loading, so we can @@ -1776,7 +1786,20 @@ static int rproc_attach_recovery(struct rproc *rproc) if (ret) return ret; - return __rproc_attach(rproc); + /* clean up all acquired resources */ + rproc_resource_cleanup(rproc); + + /* release HW resources if needed */ + rproc_unprepare_device(rproc); + + rproc_disable_iommu(rproc); + + /* Free the copy of the resource table */ + kfree(rproc->cached_table); + rproc->cached_table = NULL; + rproc->table_ptr = NULL; + + return rproc_attach(rproc); } static int rproc_boot_recovery(struct rproc *rproc) @@ -1828,6 +1851,11 @@ int rproc_trigger_recovery(struct rproc *rproc) if (ret) return ret; + if (READ_ONCE(rproc->deleting)) { + ret = -ENODEV; + goto unlock_mutex; + } + /* State could have changed before we got the mutex */ if (rproc->state != RPROC_CRASHED) goto unlock_mutex; @@ -1860,6 +1888,11 @@ static void rproc_crash_handler_work(struct work_struct *work) mutex_lock(&rproc->lock); + if (READ_ONCE(rproc->deleting)) { + mutex_unlock(&rproc->lock); + goto out; + } + if (rproc->state == RPROC_CRASHED) { /* handle only the first crash detected */ mutex_unlock(&rproc->lock); @@ -1915,9 +1948,9 @@ int rproc_boot(struct rproc *rproc) return ret; } - if (rproc->state == RPROC_DELETED) { + if (READ_ONCE(rproc->deleting)) { ret = -ENODEV; - dev_err(dev, "can't boot deleted rproc %s\n", rproc->name); + dev_err(dev, "can't boot deleting rproc %s\n", rproc->name); goto unlock_mutex; } @@ -1955,30 +1988,10 @@ unlock_mutex: } EXPORT_SYMBOL(rproc_boot); -/** - * rproc_shutdown() - power off the remote processor - * @rproc: the remote processor - * - * Power off a remote processor (previously booted with rproc_boot()). - * - * In case @rproc is still being used by an additional user(s), then - * this function will just decrement the power refcount and exit, - * without really powering off the device. - * - * Every call to rproc_boot() must (eventually) be accompanied by a call - * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug. - * - * Notes: - * - we're not decrementing the rproc's refcount, only the power refcount. - * which means that the @rproc handle stays valid even after rproc_shutdown() - * returns, and users can still use it with a subsequent rproc_boot(), if - * needed. - * - * Return: 0 on success, and an appropriate error value otherwise - */ -int rproc_shutdown(struct rproc *rproc) +static int __rproc_shutdown(struct rproc *rproc, bool force) { struct device *dev = &rproc->dev; + bool crashed; int ret; ret = mutex_lock_interruptible(&rproc->lock); @@ -1988,16 +2001,19 @@ int rproc_shutdown(struct rproc *rproc) } if (rproc->state != RPROC_RUNNING && - rproc->state != RPROC_ATTACHED) { + rproc->state != RPROC_ATTACHED && + rproc->state != RPROC_CRASHED) { ret = -EINVAL; goto out; } + crashed = rproc->state == RPROC_CRASHED; - /* if the remote proc is still needed, bail out */ - if (!atomic_dec_and_test(&rproc->power)) + if (!atomic_dec_and_test(&rproc->power) && !force) { + /* The remote processor is still needed by another user. */ goto out; + } - ret = rproc_stop(rproc, false); + ret = rproc_stop(rproc, crashed); if (ret) { atomic_inc(&rproc->power); goto out; @@ -2019,6 +2035,32 @@ out: mutex_unlock(&rproc->lock); return ret; } + +/** + * rproc_shutdown() - power off the remote processor + * @rproc: the remote processor + * + * Power off a remote processor (previously booted with rproc_boot()). + * + * In case @rproc is still being used by an additional user(s), then + * this function will just decrement the power refcount and exit, + * without really powering off the device. + * + * Every call to rproc_boot() must (eventually) be accompanied by a call + * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug. + * + * Notes: + * - we're not decrementing the rproc's refcount, only the power refcount. + * which means that the @rproc handle stays valid even after rproc_shutdown() + * returns, and users can still use it with a subsequent rproc_boot(), if + * needed. + * + * Return: 0 on success, and an appropriate error value otherwise + */ +int rproc_shutdown(struct rproc *rproc) +{ + return __rproc_shutdown(rproc, false); +} EXPORT_SYMBOL(rproc_shutdown); /** @@ -2317,6 +2359,7 @@ int rproc_add(struct rproc *rproc) return 0; rproc_remove_dev: + cancel_work_sync(&rproc->crash_handler); rproc_delete_debug_dir(rproc); device_del(dev); rproc_remove_cdev: @@ -2506,7 +2549,9 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, INIT_LIST_HEAD(&rproc->subdevs); INIT_LIST_HEAD(&rproc->dump_segments); + INIT_WORK(&rproc->attach_work, rproc_attach_work); INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); + spin_lock_init(&rproc->crash_handler_lock); rproc->state = RPROC_OFFLINE; @@ -2570,15 +2615,19 @@ EXPORT_SYMBOL(rproc_put); */ int rproc_del(struct rproc *rproc) { + unsigned long flags; + if (!rproc) return -EINVAL; - /* TODO: make sure this works with rproc->power > 1 */ - rproc_shutdown(rproc); + spin_lock_irqsave(&rproc->crash_handler_lock, flags); + WRITE_ONCE(rproc->deleting, true); + spin_unlock_irqrestore(&rproc->crash_handler_lock, flags); - mutex_lock(&rproc->lock); - rproc->state = RPROC_DELETED; - mutex_unlock(&rproc->lock); + if (cancel_work_sync(&rproc->crash_handler)) + pm_relax(rproc->dev.parent); + + __rproc_shutdown(rproc, true); rproc_delete_debug_dir(rproc); @@ -2691,18 +2740,26 @@ EXPORT_SYMBOL(rproc_get_by_child); */ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type) { + unsigned long flags; + if (!rproc) { pr_err("NULL rproc pointer\n"); return; } + spin_lock_irqsave(&rproc->crash_handler_lock, flags); + if (READ_ONCE(rproc->deleting)) { + spin_unlock_irqrestore(&rproc->crash_handler_lock, flags); + return; + } + /* Prevent suspend while the remoteproc is being recovered */ pm_stay_awake(rproc->dev.parent); + queue_work(rproc_recovery_wq, &rproc->crash_handler); + spin_unlock_irqrestore(&rproc->crash_handler_lock, flags); dev_err(&rproc->dev, "crash detected in %s: type %s\n", rproc->name, rproc_crash_to_string(type)); - - queue_work(rproc_recovery_wq, &rproc->crash_handler); } EXPORT_SYMBOL(rproc_report_crash); diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 0a5e15744b1d..3a742ef6ef60 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -14,6 +14,9 @@ #include <linux/irqreturn.h> #include <linux/firmware.h> +#ifdef CONFIG_HAS_IOMEM +#include <linux/io.h> +#endif struct rproc; @@ -122,6 +125,57 @@ rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...); void rproc_add_rvdev(struct rproc *rproc, struct rproc_vdev *rvdev); void rproc_remove_rvdev(struct rproc_vdev *rvdev); +#ifdef CONFIG_HAS_IOMEM +static inline int rproc_mem_entry_ioremap_wc(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + void __iomem *va; + + va = ioremap_wc(mem->dma, mem->len); + if (!va) { + dev_err(&rproc->dev, "Unable to map memory region: %pa+%zx\n", + &mem->dma, mem->len); + return -ENOMEM; + } + + mem->va = (__force void *)va; + mem->is_iomem = true; + + return 0; +} + +static inline int rproc_mem_entry_iounmap(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + iounmap((__force __iomem void *)mem->va); + + return 0; +} +#else +static inline int rproc_mem_entry_ioremap_wc(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + return -EOPNOTSUPP; +} + +static inline int rproc_mem_entry_iounmap(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + return 0; +} +#endif + +#define rproc_elf_load_rsc_table_optional(rproc, fw, dev_func, fmt, ...) \ + ({ \ + int ret = rproc_elf_load_rsc_table(rproc, fw); \ + if (ret == -EINVAL) { \ + dev_func(&rproc->dev, fmt, ##__VA_ARGS__); \ + return 0; \ + } else { \ + return ret; \ + } \ + }) + static inline int rproc_prepare_device(struct rproc *rproc) { if (rproc->ops->prepare) diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c index 138e752c5e4e..925b0cdbe577 100644 --- a/drivers/remoteproc/remoteproc_sysfs.c +++ b/drivers/remoteproc/remoteproc_sysfs.c @@ -168,7 +168,6 @@ static const char * const rproc_state_string[] = { [RPROC_SUSPENDED] = "suspended", [RPROC_RUNNING] = "running", [RPROC_CRASHED] = "crashed", - [RPROC_DELETED] = "deleted", [RPROC_ATTACHED] = "attached", [RPROC_DETACHED] = "detached", [RPROC_LAST] = "invalid", diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index a07edf7217d2..486180cdccb4 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -88,33 +88,6 @@ static void st_rproc_kick(struct rproc *rproc, int vqid) dev_err(dev, "failed to send message via mbox: %d\n", ret); } -static int st_rproc_mem_alloc(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - struct device *dev = rproc->dev.parent; - void *va; - - va = ioremap_wc(mem->dma, mem->len); - if (!va) { - dev_err(dev, "Unable to map memory region: %pa+%zx\n", - &mem->dma, mem->len); - return -ENOMEM; - } - - /* Update memory entry va */ - mem->va = va; - - return 0; -} - -static int st_rproc_mem_release(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - iounmap(mem->va); - - return 0; -} - static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { struct device *dev = rproc->dev.parent; @@ -138,8 +111,8 @@ static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) mem = rproc_mem_entry_init(dev, NULL, (dma_addr_t)res.start, resource_size(&res), res.start, - st_rproc_mem_alloc, - st_rproc_mem_release, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, "%.*s", strchrnul(res.name, '@') - res.name, res.name); diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 632614013dc6..1fe4cdc0a13a 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -113,35 +113,6 @@ static int stm32_rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da) return -EINVAL; } -static int stm32_rproc_mem_alloc(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - struct device *dev = rproc->dev.parent; - void *va; - - dev_dbg(dev, "map memory: %pad+%zx\n", &mem->dma, mem->len); - va = (__force void *)ioremap_wc(mem->dma, mem->len); - if (IS_ERR_OR_NULL(va)) { - dev_err(dev, "Unable to map memory region: %pad+0x%zx\n", - &mem->dma, mem->len); - return -ENOMEM; - } - - /* Update memory entry va */ - mem->va = va; - - return 0; -} - -static int stm32_rproc_mem_release(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma); - iounmap((__force __iomem void *)mem->va); - - return 0; -} - static int stm32_rproc_of_memory_translations(struct platform_device *pdev, struct stm32_rproc *ddata) { @@ -237,8 +208,8 @@ static int stm32_rproc_prepare(struct rproc *rproc) mem = rproc_mem_entry_init(dev, NULL, (dma_addr_t)res.start, resource_size(&res), da, - stm32_rproc_mem_alloc, - stm32_rproc_mem_release, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, "%.*s", strchrnul(res.name, '@') - res.name, res.name); if (mem) @@ -263,9 +234,8 @@ static int stm32_rproc_prepare(struct rproc *rproc) static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { - if (rproc_elf_load_rsc_table(rproc, fw)) - dev_warn(&rproc->dev, "no resource table found for this firmware\n"); - + rproc_elf_load_rsc_table_optional(rproc, fw, dev_warn, + "no resource table found for this firmware\n"); return 0; } @@ -712,8 +682,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev, err = devm_request_irq(dev, irq, stm32_rproc_wdg, 0, dev_name(dev), pdev); if (err) - return dev_err_probe(dev, err, - "failed to request wdg irq\n"); + return err; ddata->wdg_irq = irq; @@ -957,4 +926,3 @@ MODULE_DESCRIPTION("STM32 Remote Processor Control Driver"); MODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>"); MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 04f23295ffc1..b1d04d082e44 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -1074,11 +1074,9 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev) } kproc->reset = devm_reset_control_get_exclusive(cdev, NULL); - if (IS_ERR_OR_NULL(kproc->reset)) { - ret = PTR_ERR_OR_ZERO(kproc->reset); - if (!ret) - ret = -ENODEV; - dev_err_probe(cdev, ret, "failed to get reset handle\n"); + if (IS_ERR(kproc->reset)) { + ret = dev_err_probe(cdev, PTR_ERR(kproc->reset), + "failed to get reset handle\n"); goto out; } diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 3349d1877751..c685bb5fa62c 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -112,6 +112,10 @@ struct rsc_tbl_data { const uintptr_t rsc_tbl; } __packed; +enum xlnx_rproc_fw_rsc { + XLNX_RPROC_FW_CRASH_REPORT = RSC_VENDOR_START, +}; + /* * Hardcoded TCM bank values. This will stay in driver to maintain backward * compatibility with device-tree that does not have TCM information. @@ -131,9 +135,27 @@ static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { {0xffe30000UL, 0x30000, 0x10000UL, PD_R5_1_BTCM, "btcm1"}, }; +#define CRASH_REASON_STR_LEN 16 + +/** + * struct xlnx_rproc_crash_report - resource to know crash status and reason + * + * @version: version of this resource + * @crashed: if true, the rproc is notifying crash, time to recover + * @crash_reason: number to describe reason of crash + * @crash_reason_str: short string description of crash reason + */ +struct xlnx_rproc_crash_report { + u8 version; + u8 crashed; + u8 crash_reason; + char crash_reason_str[CRASH_REASON_STR_LEN]; +} __packed; + /** * struct zynqmp_r5_core - remoteproc core's internal data * + * @crash_report: rproc crash state and reason * @rsc_tbl_va: resource table virtual address * @sram: Array of sram memories assigned to this core * @num_sram: number of sram for this core @@ -147,6 +169,7 @@ static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { * @ipi: pointer to mailbox information */ struct zynqmp_r5_core { + struct xlnx_rproc_crash_report *crash_report; void __iomem *rsc_tbl_va; struct zynqmp_sram_bank *sram; int num_sram; @@ -204,11 +227,27 @@ static int event_notified_idr_cb(int id, void *ptr, void *data) */ static void handle_event_notified(struct work_struct *work) { + struct xlnx_rproc_crash_report *report; + struct zynqmp_r5_core *r5_core; struct mbox_info *ipi; struct rproc *rproc; ipi = container_of(work, struct mbox_info, mbox_work); rproc = ipi->r5_core->rproc; + r5_core = ipi->r5_core; + report = r5_core->crash_report; + + /* report crash only if expected */ + if (report && report->crashed) { + if (rproc->state == RPROC_ATTACHED || rproc->state == RPROC_RUNNING) { + report->crash_reason_str[CRASH_REASON_STR_LEN - 1] = '\0'; + dev_warn(&rproc->dev, "crash reason id: %d %s\n", + report->crash_reason, report->crash_reason_str); + rproc_report_crash(rproc, RPROC_FATAL_ERROR); + report->crashed = false; + return; + } + } /* * We only use IPI for interrupt. The RPU firmware side may or may @@ -364,49 +403,12 @@ static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) static int zynqmp_r5_rproc_start(struct rproc *rproc) { struct zynqmp_r5_core *r5_core = rproc->priv; - enum rpu_boot_mem bootmem; int ret; - /* - * The exception vector pointers (EVP) refer to the base-address of - * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector - * starts at the base-address and subsequent vectors are on 4-byte - * boundaries. - * - * Exception vectors can start either from 0x0000_0000 (LOVEC) or - * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip Memory) - * - * Usually firmware will put Exception vectors at LOVEC. - * - * It is not recommend that you change the exception vector. - * Changing the EVP to HIVEC will result in increased interrupt latency - * and jitter. Also, if the OCM is secured and the Cortex-R5F processor - * is non-secured, then the Cortex-R5F processor cannot access the - * HIVEC exception vectors in the OCM. - */ - bootmem = (rproc->bootaddr >= 0xFFFC0000) ? - PM_RPU_BOOTMEM_HIVEC : PM_RPU_BOOTMEM_LOVEC; - - dev_dbg(r5_core->dev, "RPU boot addr 0x%llx from %s.", rproc->bootaddr, - bootmem == PM_RPU_BOOTMEM_HIVEC ? "OCM" : "TCM"); - - /* Request node before starting RPU core if new version of API is supported */ - if (zynqmp_pm_feature(PM_REQUEST_NODE) > 1) { - ret = zynqmp_pm_request_node(r5_core->pm_domain_id, - ZYNQMP_PM_CAPABILITY_ACCESS, 0, - ZYNQMP_PM_REQUEST_ACK_BLOCKING); - if (ret < 0) { - dev_err(r5_core->dev, "failed to request 0x%x", - r5_core->pm_domain_id); - return ret; - } - } - - ret = zynqmp_pm_request_wake(r5_core->pm_domain_id, 1, - bootmem, ZYNQMP_PM_REQUEST_ACK_NO); + ret = zynqmp_pm_start_rpu(r5_core->pm_domain_id, rproc->bootaddr); if (ret) - dev_err(r5_core->dev, - "failed to start RPU = 0x%x\n", r5_core->pm_domain_id); + dev_err(&rproc->dev, "failed to start RPU\n"); + return ret; } @@ -423,74 +425,21 @@ static int zynqmp_r5_rproc_stop(struct rproc *rproc) struct zynqmp_r5_core *r5_core = rproc->priv; int ret; - /* Use release node API to stop core if new version of API is supported */ - if (zynqmp_pm_feature(PM_RELEASE_NODE) > 1) { - ret = zynqmp_pm_release_node(r5_core->pm_domain_id); - if (ret) - dev_err(r5_core->dev, "failed to stop remoteproc RPU %d\n", ret); - return ret; - } + ret = zynqmp_pm_stop_rpu(r5_core->pm_domain_id); + if (ret) + dev_err(&rproc->dev, "failed to stop RPU\n"); /* - * Check expected version of EEMI call before calling it. This avoids - * any error or warning prints from firmware as it is expected that fw - * doesn't support it. + * Clear attach on recovery flag during stop operation. The next state + * of the remote processor is expected to be in "Running" state. In this + * state, boot recovery method must take place over attach on recovery. */ - if (zynqmp_pm_feature(PM_FORCE_POWERDOWN) != 1) { - dev_dbg(r5_core->dev, "EEMI interface %d ver 1 not supported\n", - PM_FORCE_POWERDOWN); - return -EOPNOTSUPP; - } - - /* maintain force pwr down for backward compatibility */ - ret = zynqmp_pm_force_pwrdwn(r5_core->pm_domain_id, - ZYNQMP_PM_REQUEST_ACK_BLOCKING); - if (ret) - dev_err(r5_core->dev, "core force power down failed\n"); + test_and_clear_bit(RPROC_FEAT_ATTACH_ON_RECOVERY, rproc->features); return ret; } /* - * zynqmp_r5_mem_region_map() - * @rproc: single R5 core's corresponding rproc instance - * @mem: mem descriptor to map reserved memory-regions - * - * Callback to map va for memory-region's carveout. - * - * return 0 on success, otherwise non-zero value on failure - */ -static int zynqmp_r5_mem_region_map(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - void __iomem *va; - - va = ioremap_wc(mem->dma, mem->len); - if (IS_ERR_OR_NULL(va)) - return -ENOMEM; - - mem->va = (void *)va; - - return 0; -} - -/* - * zynqmp_r5_rproc_mem_unmap - * @rproc: single R5 core's corresponding rproc instance - * @mem: mem entry to unmap - * - * Unmap memory-region carveout - * - * return: always returns 0 - */ -static int zynqmp_r5_mem_region_unmap(struct rproc *rproc, - struct rproc_mem_entry *mem) -{ - iounmap((void __iomem *)mem->va); - return 0; -} - -/* * add_mem_regions_carveout() * @rproc: single R5 core's corresponding rproc instance * @@ -526,8 +475,8 @@ static int add_mem_regions_carveout(struct rproc *rproc) rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL, (dma_addr_t)res.start, resource_size(&res), res.start, - zynqmp_r5_mem_region_map, - zynqmp_r5_mem_region_unmap, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, "%.*s", strchrnul(res.name, '@') - res.name, res.name); @@ -564,8 +513,8 @@ static int add_sram_carveouts(struct rproc *rproc) rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL, dma_addr, len, da, - zynqmp_r5_mem_region_map, - zynqmp_r5_mem_region_unmap, + rproc_mem_entry_ioremap_wc, + rproc_mem_entry_iounmap, sram->sram_res.name); if (!rproc_mem) { dev_err(&rproc->dev, "failed to add sram %s da=0x%x, size=0x%lx", @@ -718,20 +667,15 @@ release_tcm: */ static int zynqmp_r5_parse_fw(struct rproc *rproc, const struct firmware *fw) { - int ret; - - ret = rproc_elf_load_rsc_table(rproc, fw); - if (ret == -EINVAL) { - /* - * resource table only required for IPC. - * if not present, this is not necessarily an error; - * for example, loading r5 hello world application - * so simply inform user and keep going. - */ - dev_info(&rproc->dev, "no resource table found.\n"); - ret = 0; - } - return ret; + /* + * resource table only required for IPC. + * if not present, this is not necessarily an error; + * for example, loading r5 hello world application + * so simply inform user and keep going. + */ + rproc_elf_load_rsc_table_optional(rproc, fw, dev_info, + "no resource table found.\n"); + return 0; } /** @@ -886,6 +830,24 @@ static int zynqmp_r5_detach(struct rproc *rproc) return 0; } +static int zynqmp_r5_handle_rsc(struct rproc *rproc, u32 rsc_type, void *rsc, + int offset, int avail) +{ + struct zynqmp_r5_core *r5_core = rproc->priv; + void *rsc_offset = (r5_core->rsc_tbl_va + offset); + + if (rsc_type != XLNX_RPROC_FW_CRASH_REPORT) + return RSC_IGNORED; + + r5_core->crash_report = rsc_offset; + /* reset all values */ + r5_core->crash_report->crashed = false; + r5_core->crash_report->crash_reason = 0; + r5_core->crash_report->crash_reason_str[0] = '\0'; + + return RSC_HANDLED; +} + static const struct rproc_ops zynqmp_r5_rproc_ops = { .prepare = zynqmp_r5_rproc_prepare, .unprepare = zynqmp_r5_rproc_unprepare, @@ -900,6 +862,7 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = { .get_loaded_rsc_table = zynqmp_r5_get_loaded_rsc_table, .attach = zynqmp_r5_attach, .detach = zynqmp_r5_detach, + .handle_rsc = zynqmp_r5_handle_rsc, }; /** @@ -939,7 +902,7 @@ static struct zynqmp_r5_core *zynqmp_r5_alloc_rproc_core(struct device *cdev) rproc_coredump_set_elf_info(r5_rproc, ELFCLASS32, EM_ARM); - r5_rproc->recovery_disabled = true; + r5_rproc->recovery_disabled = false; r5_rproc->has_iommu = false; r5_rproc->auto_boot = false; @@ -1288,6 +1251,9 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, if (zynqmp_r5_get_rsc_table_va(r5_core)) dev_dbg(r5_core->dev, "rsc tbl not found\n"); r5_core->rproc->state = RPROC_DETACHED; + /* Enable attach on recovery method. Clear it during rproc stop. */ + rproc_set_feature(r5_core->rproc, + RPROC_FEAT_ATTACH_ON_RECOVERY); r5_core->rproc->auto_boot = true; } } diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index d9d4468e4cbd..55793fc18293 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -501,7 +501,7 @@ static int qcom_glink_send_open_req(struct qcom_glink *glink, req->cmd = cpu_to_le16(GLINK_CMD_OPEN); req->param1 = cpu_to_le16(channel->lcid); req->param2 = cpu_to_le32(name_len); - strcpy(req->data, channel->name); + strscpy(req->data, channel->name, GLINK_NAME_SIZE); trace_qcom_glink_cmd_open_tx(glink->label, channel->name, channel->lcid, channel->rcid); @@ -1418,9 +1418,6 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) channel->ept.cb = NULL; spin_unlock_irqrestore(&channel->recv_lock, flags); - /* Decouple the potential rpdev from the channel */ - qcom_glink_remove_rpmsg_device(glink, channel); - qcom_glink_send_close_req(glink, channel); } diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c index 62adc4db2317..35bb03e67ae8 100644 --- a/drivers/rpmsg/qcom_glink_smem.c +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -103,6 +103,13 @@ static void glink_smem_rx_peek(struct qcom_glink_pipe *np, if (tail >= pipe->native.length) tail -= pipe->native.length; + /* + * Order the availability (head) read in glink_smem_rx_avail() + * against the FIFO payload read below, so APPS never consumes + * stale data the remote has not yet published. + */ + rmb(); + len = min_t(size_t, count, pipe->native.length - tail); if (len) memcpy_fromio(data, pipe->fifo + tail, len); diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index 3ac863f400ec..7dbe1c6efe41 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c @@ -1460,7 +1460,7 @@ static ssize_t rpmsg_name_show(struct device *dev, { struct qcom_smd_edge *edge = to_smd_edge(dev); - return sprintf(buf, "%s\n", edge->name); + return sysfs_emit(buf, "%s\n", edge->name); } static DEVICE_ATTR_RO(rpmsg_name); diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index bff5aefee212..550fda217da4 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -79,6 +79,11 @@ int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data) struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); mutex_lock(&eptdev->ept_lock); + if (!eptdev->rpdev) { + mutex_unlock(&eptdev->ept_lock); + return 0; + } + eptdev->rpdev = NULL; if (eptdev->ept) { /* The default endpoint is released by the rpmsg core */ @@ -371,7 +376,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, { struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", eptdev->chinfo.name); + return sysfs_emit(buf, "%s\n", eptdev->chinfo.name); } static DEVICE_ATTR_RO(name); @@ -380,7 +385,7 @@ static ssize_t src_show(struct device *dev, struct device_attribute *attr, { struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", eptdev->chinfo.src); + return sysfs_emit(buf, "%d\n", eptdev->chinfo.src); } static DEVICE_ATTR_RO(src); @@ -389,7 +394,7 @@ static ssize_t dst_show(struct device *dev, struct device_attribute *attr, { struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", eptdev->chinfo.dst); + return sysfs_emit(buf, "%d\n", eptdev->chinfo.dst); } static DEVICE_ATTR_RO(dst); diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index c56f69c22e42..04bfcc75f05b 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -348,7 +348,7 @@ field##_show(struct device *dev, \ { \ struct rpmsg_device *rpdev = to_rpmsg_device(dev); \ \ - return sprintf(buf, format_string, rpdev->path); \ + return sysfs_emit(buf, format_string, rpdev->path); \ } \ static DEVICE_ATTR_RO(field); @@ -368,7 +368,7 @@ static ssize_t modalias_show(struct device *dev, if (len != -ENODEV) return len; - return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name); + return sysfs_emit(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name); } static DEVICE_ATTR_RO(modalias); @@ -598,8 +598,6 @@ EXPORT_SYMBOL(__register_rpmsg_driver); /** * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus * @rpdrv: pointer to a struct rpmsg_driver - * - * Return: 0 on success, and an appropriate error value on failure. */ void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv) { diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index c004d444d698..137992456b71 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -13,7 +13,7 @@ #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/firmware/qcom/qcom_pas.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/soc/qcom/mdt_loader.h> @@ -229,7 +229,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *ctx) + struct qcom_pas_context *ctx) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -271,7 +271,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, goto out; } - ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len, ctx); + ret = qcom_pas_init_image(pas_id, metadata, metadata_len, ctx); kfree(metadata); if (ret) { /* Invalid firmware metadata */ @@ -280,7 +280,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, } if (relocate) { - ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); + ret = qcom_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); if (ret) { /* Unable to set up relocation */ dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); @@ -472,7 +472,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load); * firmware segments (e.g., .bXX files). Authentication of the segments done * by a separate call. * - * The PAS context must be initialized using qcom_scm_pas_context_init() + * The PAS context must be initialized using devm_qcom_pas_context_alloc() * prior to invoking this function. * * @ctx: Pointer to the PAS (Peripheral Authentication Service) context @@ -483,7 +483,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load); * * Return: 0 on success or a negative error code on failure. */ -int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, +int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw, const char *firmware, void *mem_region, phys_addr_t *reloc_base) { int ret; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 7e27b0f7bf7e..347df66ee176 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -644,6 +644,8 @@ int zynqmp_pm_get_node_status(const u32 node, u32 *const status, u32 *const requirements, u32 *const usage); int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, u32 *const requirements, u32 *const usage); +int zynqmp_pm_start_rpu(const u32 node, const u64 bootaddr); +int zynqmp_pm_stop_rpu(const u32 node); int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value); int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value); @@ -960,6 +962,16 @@ static inline int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const statu return -ENODEV; } +static inline int zynqmp_pm_start_rpu(const u32 node, const u64 bootaddr) +{ + return -ENODEV; +} + +static inline int zynqmp_pm_stop_rpu(const u32 node) +{ + return -ENODEV; +} + static inline int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 7c1546d48008..ef711a5b1a7f 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -37,6 +37,7 @@ #include <linux/types.h> #include <linux/mutex.h> +#include <linux/spinlock.h> #include <linux/virtio.h> #include <linux/cdev.h> #include <linux/completion.h> @@ -145,7 +146,6 @@ struct rproc_ops { * a message. * @RPROC_RUNNING: device is up and running * @RPROC_CRASHED: device has crashed; need to start recovery - * @RPROC_DELETED: device is deleted * @RPROC_ATTACHED: device has been booted by another entity and the core * has attached to it * @RPROC_DETACHED: device has been booted by another entity and waiting @@ -163,10 +163,9 @@ enum rproc_state { RPROC_SUSPENDED = 1, RPROC_RUNNING = 2, RPROC_CRASHED = 3, - RPROC_DELETED = 4, - RPROC_ATTACHED = 5, - RPROC_DETACHED = 6, - RPROC_LAST = 7, + RPROC_ATTACHED = 4, + RPROC_DETACHED = 5, + RPROC_LAST = 6, }; /** @@ -259,7 +258,10 @@ enum rproc_features { * @subdevs: list of subdevices, to following the running state * @notifyids: idr for dynamically assigning rproc-wide unique notify ids * @index: index of this rproc device + * @attach_work: workqueue for attaching rproc * @crash_handler: workqueue for handling a crash + * @crash_handler_lock: serializes crash handler queueing and deletion + * @deleting: remoteproc deletion has begun * @crash_cnt: crash counter * @recovery_disabled: flag that state if recovery was disabled * @max_notifyid: largest allocated notify id. @@ -271,6 +273,7 @@ enum rproc_features { * @has_iommu: flag to indicate if remote processor is behind an MMU * @auto_boot: flag to indicate if remote processor should be auto-started * @sysfs_read_only: flag to make remoteproc sysfs files read only + * @subdevs_started: flag to indicate if subdevs have started * @dump_segments: list of segments in the firmware * @nb_vdev: number of vdev currently handled by rproc * @elf_class: firmware ELF class @@ -301,7 +304,10 @@ struct rproc { struct list_head subdevs; struct idr notifyids; int index; + struct work_struct attach_work; struct work_struct crash_handler; + spinlock_t crash_handler_lock; + bool deleting; unsigned int crash_cnt; bool recovery_disabled; int max_notifyid; @@ -312,6 +318,7 @@ struct rproc { bool has_iommu; bool auto_boot; bool sysfs_read_only; + bool subdevs_started; struct list_head dump_segments; int nb_vdev; u8 elf_class; diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 82372e0db0a1..142409555425 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -10,7 +10,7 @@ struct device; struct firmware; -struct qcom_scm_pas_context; +struct qcom_pas_context; #if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) @@ -20,7 +20,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base); -int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, +int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw, const char *firmware, void *mem_region, phys_addr_t *reloc_base); int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, @@ -45,7 +45,7 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, return -ENODEV; } -static inline int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, +static inline int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw, const char *firmware, void *mem_region, phys_addr_t *reloc_base) { |
