| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/25 16:39 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch series adds Multi-UserPD support for Qualcomm QCN6122 and IPQ5018 Wi-Fi devices in the ath11k AHB driver and adds a corresponding power mode SCM call in the Qualcomm SCM firmware driver. These changes require physical Qualcomm SoC hardware (remoteproc, SMEM, TrustZone SCM, and integrated Wi-Fi hardware) and are structurally unreachable in standard virtualized environments (QEMU/GCE on amd64).",
"WorthFuzzing": false
} |
| 1/1 | 2026/09/25 16:39 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit d4a45fc6d8b284ef97dac5566b059fb6d17f2819\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Sep 25 16:39:55 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml\nindex d4aa56e2f8233..10102b5ea8c81 100644\n--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml\n+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml\n@@ -17,10 +17,11 @@ description: |\n properties:\n compatible:\n enum:\n- - qcom,ipq8074-wifi\n+ - qcom,ipq5018-wifi\n - qcom,ipq6018-wifi\n+ - qcom,ipq8074-wifi\n+ - qcom,qcn6122-wifi\n - qcom,wcn6750-wifi\n- - qcom,ipq5018-wifi\n \n reg:\n maxItems: 1\n@@ -29,8 +30,13 @@ properties:\n minItems: 32\n maxItems: 52\n \n+ interrupts-extended:\n+ minItems: 17\n+ maxItems: 56\n+\n interrupt-names:\n- maxItems: 52\n+ minItems: 17\n+ maxItems: 56\n \n qcom,rproc:\n $ref: /schemas/types.yaml#/definitions/phandle\n@@ -56,11 +62,15 @@ properties:\n \n memory-region:\n minItems: 1\n- maxItems: 2\n+ maxItems: 3\n description:\n phandle to a node describing reserved memory (System RAM memory)\n used by ath11k firmware (see bindings/reserved-memory/reserved-memory.txt)\n \n+ memory-region-names:\n+ minItems: 1\n+ maxItems: 3\n+\n iommus:\n minItems: 1\n maxItems: 2\n@@ -78,20 +88,11 @@ properties:\n qcom,smem-states:\n $ref: /schemas/types.yaml#/definitions/phandle-array\n description: State bits used by the AP to signal the WLAN Q6.\n- items:\n- - description: Signal bits used to enable/disable low power mode\n- on WCN6750 in the case of WoW (Wake on Wireless).\n- items:\n- - description: Phandle to the Shared Memory Point 2 Point device\n- handling the communication with a remote processor\n- - description: Single bit index to toggle in the value sent to\n- the remote processor\n- maximum: 32\n+ minItems: 1\n+ maxItems: 3\n \n qcom,smem-state-names:\n description: The names of the state bits used for SMP2P output.\n- items:\n- - const: wlan-smp2p-out\n \n required:\n - compatible\n@@ -108,8 +109,244 @@ allOf:\n compatible:\n contains:\n enum:\n- - qcom,ipq8074-wifi\n+ - qcom,ipq5018-wifi\n+ then:\n+ properties:\n+ interrupts:\n+ items:\n+ - description: misc-pulse1 interrupt events\n+ - description: misc-latch interrupt events\n+ - description: sw exception interrupt events\n+ - description: watchdog interrupt events\n+ - description: interrupt event for ring CE0\n+ - description: interrupt event for ring CE1\n+ - description: interrupt event for ring CE2\n+ - description: interrupt event for ring CE3\n+ - description: interrupt event for ring CE4\n+ - description: interrupt event for ring CE5\n+ - description: interrupt event for ring CE6\n+ - description: interrupt event for ring CE7\n+ - description: interrupt event for ring CE8\n+ - description: interrupt event for ring CE9\n+ - description: interrupt event for ring CE10\n+ - description: interrupt event for ring CE11\n+ - description: interrupt event for ring host2wbm-desc-feed\n+ - description: interrupt event for ring host2reo-re-injection\n+ - description: interrupt event for ring host2reo-command\n+ - description: interrupt event for ring host2rxdma-monitor-ring3\n+ - description: interrupt event for ring host2rxdma-monitor-ring2\n+ - description: interrupt event for ring host2rxdma-monitor-ring1\n+ - description: interrupt event for ring reo2ost-exception\n+ - description: interrupt event for ring wbm2host-rx-release\n+ - description: interrupt event for ring reo2host-status\n+ - description: interrupt event for ring reo2host-destination-ring4\n+ - description: interrupt event for ring reo2host-destination-ring3\n+ - description: interrupt event for ring reo2host-destination-ring2\n+ - description: interrupt event for ring reo2host-destination-ring1\n+ - description: interrupt event for ring rxdma2host-monitor-destination-mac3\n+ - description: interrupt event for ring rxdma2host-monitor-destination-mac2\n+ - description: interrupt event for ring rxdma2host-monitor-destination-mac1\n+ - description: interrupt event for ring ppdu-end-interrupts-mac3\n+ - description: interrupt event for ring ppdu-end-interrupts-mac2\n+ - description: interrupt event for ring ppdu-end-interrupts-mac1\n+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac3\n+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac2\n+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac1\n+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac3\n+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac2\n+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac1\n+ - description: interrupt event for ring rxdma2host-destination-ring-mac3\n+ - description: interrupt event for ring rxdma2host-destination-ring-mac2\n+ - description: interrupt event for ring rxdma2host-destination-ring-mac1\n+ - description: interrupt event for ring host2tcl-input-ring4\n+ - description: interrupt event for ring host2tcl-input-ring3\n+ - description: interrupt event for ring host2tcl-input-ring2\n+ - description: interrupt event for ring host2tcl-input-ring1\n+ - description: interrupt event for ring wbm2host-tx-completions-ring3\n+ - description: interrupt event for ring wbm2host-tx-completions-ring2\n+ - description: interrupt event for ring wbm2host-tx-completions-ring1\n+ - description: interrupt event for ring tcl2host-status-ring\n+ interrupts-extended:\n+ items:\n+ - description: misc-pulse1 interrupt events\n+ - description: misc-latch interrupt events\n+ - description: sw exception interrupt events\n+ - description: watchdog interrupt events\n+ - description: interrupt event for ring CE0\n+ - description: interrupt event for ring CE1\n+ - description: interrupt event for ring CE2\n+ - description: interrupt event for ring CE3\n+ - description: interrupt event for ring CE4\n+ - description: interrupt event for ring CE5\n+ - description: interrupt event for ring CE6\n+ - description: interrupt event for ring CE7\n+ - description: interrupt event for ring CE8\n+ - description: interrupt event for ring CE9\n+ - description: interrupt event for ring CE10\n+ - description: interrupt event for ring CE11\n+ - description: interrupt event for ring host2wbm-desc-feed\n+ - description: interrupt event for ring host2reo-re-injection\n+ - description: interrupt event for ring host2reo-command\n+ - description: interrupt event for ring host2rxdma-monitor-ring3\n+ - description: interrupt event for ring host2rxdma-monitor-ring2\n+ - description: interrupt event for ring host2rxdma-monitor-ring1\n+ - description: interrupt event for ring reo2ost-exception\n+ - description: interrupt event for ring wbm2host-rx-release\n+ - description: interrupt event for ring reo2host-status\n+ - description: interrupt event for ring reo2host-destination-ring4\n+ - description: interrupt event for ring reo2host-destination-ring3\n+ - description: interrupt event for ring reo2host-destination-ring2\n+ - description: interrupt event for ring reo2host-destination-ring1\n+ - description: interrupt event for ring rxdma2host-monitor-destination-mac3\n+ - description: interrupt event for ring rxdma2host-monitor-destination-mac2\n+ - description: interrupt event for ring rxdma2host-monitor-destination-mac1\n+ - description: interrupt event for ring ppdu-end-interrupts-mac3\n+ - description: interrupt event for ring ppdu-end-interrupts-mac2\n+ - description: interrupt event for ring ppdu-end-interrupts-mac1\n+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac3\n+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac2\n+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac1\n+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac3\n+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac2\n+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac1\n+ - description: interrupt event for ring rxdma2host-destination-ring-mac3\n+ - description: interrupt event for ring rxdma2host-destination-ring-mac2\n+ - description: interrupt event for ring rxdma2host-destination-ring-mac1\n+ - description: interrupt event for ring host2tcl-input-ring4\n+ - description: interrupt event for ring host2tcl-input-ring3\n+ - description: interrupt event for ring host2tcl-input-ring2\n+ - description: interrupt event for ring host2tcl-input-ring1\n+ - description: interrupt event for ring wbm2host-tx-completions-ring3\n+ - description: interrupt event for ring wbm2host-tx-completions-ring2\n+ - description: interrupt event for ring wbm2host-tx-completions-ring1\n+ - description: interrupt event for ring tcl2host-status-ring\n+ - description: Q6 firmware user PD fatal interrupt event\n+ - description: Q6 firmware user PD ready interrupt event\n+ - description: Q6 firmware user PD spawn interrupt event\n+ - description: Q6 firmware user PD stop ack interrupt event\n+ interrupt-names:\n+ minItems: 52\n+ items:\n+ - const: misc-pulse1\n+ - const: misc-latch\n+ - const: sw-exception\n+ - const: watchdog\n+ - const: ce0\n+ - const: ce1\n+ - const: ce2\n+ - const: ce3\n+ - const: ce4\n+ - const: ce5\n+ - const: ce6\n+ - const: ce7\n+ - const: ce8\n+ - const: ce9\n+ - const: ce10\n+ - const: ce11\n+ - const: host2wbm-desc-feed\n+ - const: host2reo-re-injection\n+ - const: host2reo-command\n+ - const: host2rxdma-monitor-ring3\n+ - const: host2rxdma-monitor-ring2\n+ - const: host2rxdma-monitor-ring1\n+ - const: reo2ost-exception\n+ - const: wbm2host-rx-release\n+ - const: reo2host-status\n+ - const: reo2host-destination-ring4\n+ - const: reo2host-destination-ring3\n+ - const: reo2host-destination-ring2\n+ - const: reo2host-destination-ring1\n+ - const: rxdma2host-monitor-destination-mac3\n+ - const: rxdma2host-monitor-destination-mac2\n+ - const: rxdma2host-monitor-destination-mac1\n+ - const: ppdu-end-interrupts-mac3\n+ - const: ppdu-end-interrupts-mac2\n+ - const: ppdu-end-interrupts-mac1\n+ - const: rxdma2host-monitor-status-ring-mac3\n+ - const: rxdma2host-monitor-status-ring-mac2\n+ - const: rxdma2host-monitor-status-ring-mac1\n+ - const: host2rxdma-host-buf-ring-mac3\n+ - const: host2rxdma-host-buf-ring-mac2\n+ - const: host2rxdma-host-buf-ring-mac1\n+ - const: rxdma2host-destination-ring-mac3\n+ - const: rxdma2host-destination-ring-mac2\n+ - const: rxdma2host-destination-ring-mac1\n+ - const: host2tcl-input-ring4\n+ - const: host2tcl-input-ring3\n+ - const: host2tcl-input-ring2\n+ - const: host2tcl-input-ring1\n+ - const: wbm2host-tx-completions-ring3\n+ - const: wbm2host-tx-completions-ring2\n+ - const: wbm2host-tx-completions-ring1\n+ - const: tcl2host-status-ring\n+ - const: fatal\n+ - const: ready\n+ - const: spawn\n+ - const: stop-ack\n+ memory-region:\n+ items:\n+ - description: memory region for Q6 firmware\n+ - description: memory region for M3 firmware\n+ - description: memory region for M3 dump\n+ memory-region-names:\n+ items:\n+ - const: q6-region\n+ - const: m3-region\n+ - const: m3-dump\n+ qcom,smem-states:\n+ items:\n+ - description: Signal bit used to shutdown Q6 user PD\n+ items:\n+ - description: Phandle to the Shared Memory Point 2 Point device\n+ - description: Single bit index to toggle in the value sent to the remote processor\n+ maximum: 32\n+ - description: Signal bit used to stop Q6 user PD\n+ items:\n+ - description: Phandle to the Shared Memory Point 2 Point device\n+ - description: Single bit index to toggle in the value sent to the remote processor\n+ maximum: 32\n+ - description: Signal bit used to spawn Q6 user PD\n+ items:\n+ - description: Phandle to the Shared Memory Point 2 Point device\n+ - description: Single bit index to toggle in the value sent to the remote processor\n+ maximum: 32\n+ qcom,smem-state-names:\n+ items:\n+ - const: shutdown\n+ - const: stop\n+ - const: spawn\n+ allOf:\n+ - anyOf:\n+ - required:\n+ - interrupts\n+ - required:\n+ - interrupts-extended\n+ - if:\n+ required:\n+ - interrupts-extended\n+ then:\n+ properties:\n+ interrupt-names:\n+ minItems: 56\n+ required:\n+ - interrupts-extended\n+ - memory-region\n+ - memory-region-names\n+ - qcom,smem-states\n+ - qcom,smem-state-names\n+ else:\n+ properties:\n+ interrupt-names:\n+ minItems: 52\n+ maxItems: 52\n+\n+ - if:\n+ properties:\n+ compatible:\n+ contains:\n+ enum:\n - qcom,ipq6018-wifi\n+ - qcom,ipq8074-wifi\n then:\n properties:\n interrupts:\n@@ -223,6 +460,93 @@ allOf:\n required:\n - interrupt-names\n \n+ - if:\n+ properties:\n+ compatible:\n+ contains:\n+ enum:\n+ - qcom,qcn6122-wifi\n+ then:\n+ properties:\n+ interrupts-extended:\n+ items:\n+ - description: interrupt event for ring CE1\n+ - description: interrupt event for ring CE2\n+ - description: interrupt event for ring CE3\n+ - description: interrupt event for ring CE4\n+ - description: interrupt event for ring CE5\n+ - description: interrupt event for ring DP1\n+ - description: interrupt event for ring DP2\n+ - description: interrupt event for ring DP3\n+ - description: interrupt event for ring DP4\n+ - description: interrupt event for ring DP5\n+ - description: interrupt event for ring DP6\n+ - description: interrupt event for ring DP7\n+ - description: interrupt event for ring DP8\n+ - description: Q6 firmware user PD fatal interrupt event\n+ - description: Q6 firmware user PD ready interrupt event\n+ - description: Q6 firmware user PD spawn interrupt event\n+ - description: Q6 firmware user PD stop ack interrupt event\n+ interrupt-names:\n+ minItems: 17\n+ items:\n+ - const: ce1\n+ - const: ce2\n+ - const: ce3\n+ - const: ce4\n+ - const: ce5\n+ - const: dp1\n+ - const: dp2\n+ - const: dp3\n+ - const: dp4\n+ - const: dp5\n+ - const: dp6\n+ - const: dp7\n+ - const: dp8\n+ - const: fatal\n+ - const: ready\n+ - const: spawn\n+ - const: stop-ack\n+ memory-region:\n+ minItems: 1\n+ items:\n+ - description: memory region for Q6 firmware\n+ - description: memory region for M3 firmware\n+ - description: memory region for M3 dump\n+ memory-region-names:\n+ minItems: 1\n+ items:\n+ - const: q6-region\n+ - const: m3-region\n+ - const: m3-dump\n+ qcom,smem-states:\n+ items:\n+ - description: Signal bit used to shutdown Q6 user PD\n+ items:\n+ - description: Phandle to the Shared Memory Point 2 Point device\n+ - description: Single bit index to toggle in the value sent to the remote processor\n+ maximum: 32\n+ - description: Signal bit used to stop Q6 user PD\n+ items:\n+ - description: Phandle to the Shared Memory Point 2 Point device\n+ - description: Single bit index to toggle in the value sent to the remote processor\n+ maximum: 32\n+ - description: Signal bit used to spawn Q6 user PD\n+ items:\n+ - description: Phandle to the Shared Memory Point 2 Point device\n+ - description: Single bit index to toggle in the value sent to the remote processor\n+ maximum: 32\n+ qcom,smem-state-names:\n+ items:\n+ - const: shutdown\n+ - const: stop\n+ - const: spawn\n+ required:\n+ - interrupts-extended\n+ - interrupt-names\n+ - qcom,smem-states\n+ - qcom,smem-state-names\n+\n - if:\n properties:\n compatible:\n@@ -265,6 +589,19 @@ allOf:\n - description: interrupt event for ring DP20\n - description: interrupt event for ring DP21\n - description: interrupt event for ring DP22\n+ qcom,smem-states:\n+ items:\n+ - description: Signal bits used to enable/disable low power mode\n+ on WCN6750 in the case of WoW (Wake on Wireless).\n+ items:\n+ - description: Phandle to the Shared Memory Point 2 Point device\n+ handling the communication with a remote processor\n+ - description: Single bit index to toggle in the value sent to\n+ the remote processor\n+ maximum: 32\n+ qcom,smem-state-names:\n+ items:\n+ - const: wlan-smp2p-out\n \n examples:\n - |\n@@ -464,3 +801,203 @@ examples:\n iommus = \u003c\u0026apps_smmu 0x1c02 0x1\u003e;\n };\n };\n+\n+ - |\n+ #include \u003cdt-bindings/interrupt-controller/arm-gic.h\u003e\n+\n+ reserved-memory {\n+ #address-cells = \u003c2\u003e;\n+ #size-cells = \u003c2\u003e;\n+\n+ q6_region: q6-region@4b000000 {\n+ no-map;\n+ reg = \u003c0x0 0x4b000000 0x0 0x1400000\u003e;\n+ };\n+\n+ m3_dump: m3-dump@4d100000 {\n+ reg = \u003c0x0 0x4d100000 0x0 0x100000\u003e;\n+ no-map;\n+ };\n+ };\n+\n+ wifi@c000000 {\n+ compatible = \"qcom,ipq5018-wifi\";\n+ reg = \u003c0x0c000000 0x1000000\u003e;\n+\n+ interrupts-extended = \u003c\u0026intc GIC_SPI 288 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 289 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 290 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 292 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 293 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 294 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 295 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 296 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 297 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 298 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 299 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 300 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 301 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 302 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 303 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 304 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 305 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 306 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 307 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 308 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 309 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 310 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 311 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 334 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 313 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 314 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 315 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 316 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 317 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 318 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 319 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 320 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 321 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 322 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 323 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 324 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 325 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 326 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 327 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 328 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 329 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 330 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 331 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 332 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 333 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 312 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 335 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 336 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 337 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 338 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 339 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026wcss_smp2p_in 8 IRQ_TYPE_NONE\u003e,\n+ \u003c\u0026wcss_smp2p_in 9 IRQ_TYPE_NONE\u003e,\n+ \u003c\u0026wcss_smp2p_in 12 IRQ_TYPE_NONE\u003e,\n+ \u003c\u0026wcss_smp2p_in 11 IRQ_TYPE_NONE\u003e;\n+ interrupt-names = \"misc-pulse1\",\n+ \"misc-latch\",\n+ \"sw-exception\",\n+ \"watchdog\",\n+ \"ce0\",\n+ \"ce1\",\n+ \"ce2\",\n+ \"ce3\",\n+ \"ce4\",\n+ \"ce5\",\n+ \"ce6\",\n+ \"ce7\",\n+ \"ce8\",\n+ \"ce9\",\n+ \"ce10\",\n+ \"ce11\",\n+ \"host2wbm-desc-feed\",\n+ \"host2reo-re-injection\",\n+ \"host2reo-command\",\n+ \"host2rxdma-monitor-ring3\",\n+ \"host2rxdma-monitor-ring2\",\n+ \"host2rxdma-monitor-ring1\",\n+ \"reo2ost-exception\",\n+ \"wbm2host-rx-release\",\n+ \"reo2host-status\",\n+ \"reo2host-destination-ring4\",\n+ \"reo2host-destination-ring3\",\n+ \"reo2host-destination-ring2\",\n+ \"reo2host-destination-ring1\",\n+ \"rxdma2host-monitor-destination-mac3\",\n+ \"rxdma2host-monitor-destination-mac2\",\n+ \"rxdma2host-monitor-destination-mac1\",\n+ \"ppdu-end-interrupts-mac3\",\n+ \"ppdu-end-interrupts-mac2\",\n+ \"ppdu-end-interrupts-mac1\",\n+ \"rxdma2host-monitor-status-ring-mac3\",\n+ \"rxdma2host-monitor-status-ring-mac2\",\n+ \"rxdma2host-monitor-status-ring-mac1\",\n+ \"host2rxdma-host-buf-ring-mac3\",\n+ \"host2rxdma-host-buf-ring-mac2\",\n+ \"host2rxdma-host-buf-ring-mac1\",\n+ \"rxdma2host-destination-ring-mac3\",\n+ \"rxdma2host-destination-ring-mac2\",\n+ \"rxdma2host-destination-ring-mac1\",\n+ \"host2tcl-input-ring4\",\n+ \"host2tcl-input-ring3\",\n+ \"host2tcl-input-ring2\",\n+ \"host2tcl-input-ring1\",\n+ \"wbm2host-tx-completions-ring3\",\n+ \"wbm2host-tx-completions-ring2\",\n+ \"wbm2host-tx-completions-ring1\",\n+ \"tcl2host-status-ring\",\n+ \"fatal\",\n+ \"ready\",\n+ \"spawn\",\n+ \"stop-ack\";\n+\n+ qcom,smem-states = \u003c\u0026wcss_smp2p_out 8\u003e,\n+ \u003c\u0026wcss_smp2p_out 9\u003e,\n+ \u003c\u0026wcss_smp2p_out 10\u003e;\n+ qcom,smem-state-names = \"shutdown\",\n+ \"stop\",\n+ \"spawn\";\n+\n+ memory-region = \u003c\u0026q6_region\u003e, \u003c\u0026q6_region\u003e, \u003c\u0026m3_dump\u003e;\n+ memory-region-names = \"q6-region\", \"m3-region\", \"m3-dump\";\n+\n+ qcom,rproc = \u003c\u0026q6v5_wcss\u003e;\n+ };\n+\n+ wifi1: wifi@b00a040 {\n+ reg = \u003c0x0b00a040 0x0\u003e;\n+ compatible = \"qcom,qcn6122-wifi\";\n+\n+ interrupts-extended = \u003c\u0026intc GIC_SPI 416 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 417 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 418 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 419 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 420 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 421 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 422 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 423 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 424 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 425 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 426 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 427 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026intc GIC_SPI 428 IRQ_TYPE_EDGE_RISING\u003e,\n+ \u003c\u0026wcss_smp2p_in 16 IRQ_TYPE_NONE\u003e,\n+ \u003c\u0026wcss_smp2p_in 17 IRQ_TYPE_NONE\u003e,\n+ \u003c\u0026wcss_smp2p_in 20 IRQ_TYPE_NONE\u003e,\n+ \u003c\u0026wcss_smp2p_in 19 IRQ_TYPE_NONE\u003e;\n+ interrupt-names = \"ce1\",\n+ \"ce2\",\n+ \"ce3\",\n+ \"ce4\",\n+ \"ce5\",\n+ \"dp1\",\n+ \"dp2\",\n+ \"dp3\",\n+ \"dp4\",\n+ \"dp5\",\n+ \"dp6\",\n+ \"dp7\",\n+ \"dp8\",\n+ \"fatal\",\n+ \"ready\",\n+ \"spawn\",\n+ \"stop-ack\";\n+\n+ qcom,smem-states = \u003c\u0026wcss_smp2p_out 16\u003e,\n+ \u003c\u0026wcss_smp2p_out 17\u003e,\n+ \u003c\u0026wcss_smp2p_out 18\u003e;\n+ qcom,smem-state-names = \"shutdown\",\n+ \"stop\",\n+ \"spawn\";\n+\n+ memory-region = \u003c\u0026q6_region_qcn6122_1\u003e, \u003c\u0026q6_region_ipq5018\u003e, \u003c\u0026m3_dump_qcn6122_1\u003e;\n+ memory-region-names = \"q6-region\", \"m3-region\", \"m3-dump\";\n+\n+ status = \"disabled\";\n+ };\ndiff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c\nindex 3eaa4c9ccf3cc..8e5f5c235cb74 100644\n--- a/drivers/firmware/qcom/qcom_scm.c\n+++ b/drivers/firmware/qcom/qcom_scm.c\n@@ -1084,6 +1084,49 @@ static bool qcom_scm_is_pas_available(void)\n \treturn true;\n }\n \n+/**\n+ * qcom_scm_pas_set_wifi_power_mode() - Power on/off internal wifi\n+ * @peripheral:\tperipheral id\n+ * @enable:\ttrue to power up, false to power down\n+ *\n+ * Return 0 on success.\n+ */\n+int qcom_scm_pas_set_wifi_power_mode(u32 peripheral, bool enable)\n+{\n+\tstruct qcom_scm_desc desc = {\n+\t\t.svc = QCOM_SCM_SVC_PIL,\n+\t\t.cmd = enable ? QCOM_SCM_PIL_PAS_WIFI_PWR_EN :\n+\t\t QCOM_SCM_PIL_PAS_WIFI_PWR_DIS,\n+\t\t.arginfo = QCOM_SCM_ARGS(1),\n+\t\t.args[0] = peripheral,\n+\t\t.owner = ARM_SMCCC_OWNER_SIP,\n+\t};\n+\tstruct qcom_scm_res res;\n+\tint ret;\n+\n+\tif (!__qcom_scm_is_call_available(__scm-\u003edev, QCOM_SCM_SVC_PIL,\n+\t\t\t\t\t enable ? QCOM_SCM_PIL_PAS_WIFI_PWR_EN :\n+\t\t\t\t\t QCOM_SCM_PIL_PAS_WIFI_PWR_DIS))\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = qcom_scm_clk_enable();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = qcom_scm_bw_enable();\n+\tif (ret)\n+\t\tgoto disable_clk;\n+\n+\tret = qcom_scm_call(__scm-\u003edev, \u0026desc, \u0026res);\n+\tqcom_scm_bw_disable();\n+\n+disable_clk:\n+\tqcom_scm_clk_disable();\n+\n+\treturn ret ? : res.result[0];\n+}\n+EXPORT_SYMBOL_GPL(qcom_scm_pas_set_wifi_power_mode);\n+\n static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)\n {\n \tstruct qcom_scm_desc desc = {\ndiff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h\nindex cf90a565fdfbd..e1554997ca08c 100644\n--- a/drivers/firmware/qcom/qcom_scm.h\n+++ b/drivers/firmware/qcom/qcom_scm.h\n@@ -105,6 +105,8 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);\n #define QCOM_SCM_PIL_PAS_SHUTDOWN\t0x06\n #define QCOM_SCM_PIL_PAS_IS_SUPPORTED\t0x07\n #define QCOM_SCM_PIL_PAS_MSS_RESET\t0x0a\n+#define QCOM_SCM_PIL_PAS_WIFI_PWR_EN\t0x17\n+#define QCOM_SCM_PIL_PAS_WIFI_PWR_DIS\t0x18\n #define QCOM_SCM_PIL_PAS_GET_RSCTABLE\t0x21\n \n #define QCOM_SCM_SVC_IO\t\t\t0x05\ndiff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c\nindex 27d01411b6e9c..51908e779b7c8 100644\n--- a/drivers/net/wireless/ath/ath11k/ahb.c\n+++ b/drivers/net/wireless/ath/ath11k/ahb.c\n@@ -11,6 +11,7 @@\n #include \u003clinux/of.h\u003e\n #include \u003clinux/of_reserved_mem.h\u003e\n #include \u003clinux/dma-mapping.h\u003e\n+#include \u003clinux/firmware/qcom/qcom_scm.h\u003e\n #include \u003clinux/iommu.h\u003e\n #include \"ahb.h\"\n #include \"debug.h\"\n@@ -18,9 +19,17 @@\n #include \"qmi.h\"\n #include \u003clinux/remoteproc.h\u003e\n #include \"pcic.h\"\n+#include \u003clinux/soc/qcom/mdt_loader.h\u003e\n #include \u003clinux/soc/qcom/smem.h\u003e\n #include \u003clinux/soc/qcom/smem_state.h\u003e\n \n+#define ATH11K_UPD_IRQ_WRD_LEN 18\n+static const char *const ath11k_userpd_irq[ATH11K_USERPD_MAX_IRQ] = {\n+\t\"spawn\",\n+\t\"ready\",\n+\t\"stop-ack\",\n+};\n+\n static const struct of_device_id ath11k_ahb_of_match[] = {\n \t/* TODO: Should we change the compatible string to something similar\n \t * to one that ath10k uses?\n@@ -37,6 +46,9 @@ static const struct of_device_id ath11k_ahb_of_match[] = {\n \t{ .compatible = \"qcom,ipq5018-wifi\",\n \t .data = (void *)ATH11K_HW_IPQ5018_HW10,\n \t},\n+\t{ .compatible = \"qcom,qcn6122-wifi\",\n+\t .data = (void *)ATH11K_HW_QCN6122_HW10,\n+\t},\n \t{ }\n };\n \n@@ -44,6 +56,22 @@ MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match);\n \n #define ATH11K_IRQ_CE0_OFFSET 4\n \n+/*\n+ * Multi-UserPD Architecture:\n+ *\n+ * One Q6 RootPD (managed by separate rproc driver) supports multiple\n+ * ath12k UserPDs. Each UserPD represents a WiFi radio instance.\n+ *\n+ * Lifecycle:\n+ * - RootPD boots when first UserPD probes\n+ * - All UserPDs share RootPD's SSR notifier\n+ *\n+ * Global Driver-level Locking:\n+ * - ath11k_rproc_info_lock: Protects rproc_info allocation/free\n+ */\n+static struct ath11k_ahb_rproc_info *g_rproc_info;\n+static DEFINE_MUTEX(ath11k_rproc_info_lock);\n+\n static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {\n \t\"misc-pulse1\",\n \t\"misc-latch\",\n@@ -406,23 +434,92 @@ static void ath11k_ahb_stop(struct ath11k_base *ab)\n \tath11k_ce_cleanup_pipes(ab);\n }\n \n+static int ath11k_ahb_boot_user_pd(struct ath11k_base *ab)\n+{\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\tunsigned long time_left;\n+\tint ret;\n+\n+\tif (ab-\u003ehw_rev == ATH11K_HW_IPQ5018_HW10) {\n+\t\tret = qcom_scm_pas_set_wifi_power_mode(MPD_WCNSS_PAS_ID, true);\n+\t\tif (ret) {\n+\t\t\tath11k_err(ab, \"failed to power up wifi: %d\\n\", ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tret = qcom_smem_state_update_bits(ab_ahb-\u003espawn_state, BIT(ab_ahb-\u003espawn_bit),\n+\t\t\t\t\t BIT(ab_ahb-\u003espawn_bit));\n+\tif (ret) {\n+\t\tath11k_err(ab, \"Failed to update spawn state %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\ttime_left = wait_for_completion_timeout(\u0026ab_ahb-\u003euserpd_spawned,\n+\t\t\t\t\t\tATH11K_USERPD_SPAWN_TIMEOUT);\n+\tif (!time_left) {\n+\t\tath11k_err(ab, \"UserPD spawn wait timed out\\n\");\n+\t\treturn -ETIMEDOUT;\n+\t}\n+\n+\ttime_left = wait_for_completion_timeout(\u0026ab_ahb-\u003euserpd_ready,\n+\t\t\t\t\t\tATH11K_USERPD_READY_TIMEOUT);\n+\tif (!time_left) {\n+\t\tath11k_err(ab, \"UserPD ready wait timed out\\n\");\n+\t\treturn -ETIMEDOUT;\n+\t}\n+\n+\tqcom_smem_state_update_bits(ab_ahb-\u003espawn_state, BIT(ab_ahb-\u003espawn_bit), 0);\n+\n+\tath11k_dbg(ab, ATH11K_DBG_AHB, \"UserPD%d is now UP\\n\", ab_ahb-\u003euserpd_id);\n+\n+\treturn 0;\n+}\n+\n static int ath11k_ahb_power_up(struct ath11k_base *ab)\n {\n \tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n \tint ret;\n \n-\tret = rproc_boot(ab_ahb-\u003etgt_rproc);\n-\tif (ret)\n-\t\tath11k_err(ab, \"failed to boot the remote processor Q6\\n\");\n+\tif (ab_ahb-\u003euserpd_id \u003e 0 \u0026\u0026\n+\t ab_ahb-\u003euserpd_id \u003c ATH11K_AHB_USERPD_ID_MAX) {\n+\t\tret = ath11k_ahb_boot_user_pd(ab);\n+\t\tif (ret) {\n+\t\t\tath11k_err(ab, \"failed to boot userPD%d: %d\\n\",\n+\t\t\t\t ab_ahb-\u003euserpd_id, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n \n-\treturn ret;\n+\treturn 0;\n+}\n+\n+static void ath11k_ahb_stop_user_pd(struct ath11k_base *ab)\n+{\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\tunsigned long time_left;\n+\n+\tqcom_smem_state_update_bits(ab_ahb-\u003estop_state, BIT(ab_ahb-\u003estop_bit),\n+\t\t\t\t BIT(ab_ahb-\u003estop_bit));\n+\n+\ttime_left = wait_for_completion_timeout(\u0026ab_ahb-\u003euserpd_stopped,\n+\t\t\t\t\t\tATH11K_USERPD_STOP_TIMEOUT);\n+\tif (!time_left)\n+\t\tath11k_warn(ab, \"UserPD stop wait timed out\\n\");\n+\n+\tqcom_smem_state_update_bits(ab_ahb-\u003estop_state, BIT(ab_ahb-\u003estop_bit), 0);\n+\n+\tif (ab-\u003ehw_rev == ATH11K_HW_IPQ5018_HW10)\n+\t\tqcom_scm_pas_set_wifi_power_mode(MPD_WCNSS_PAS_ID, false);\n }\n \n static void ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend)\n {\n \tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n \n-\trproc_shutdown(ab_ahb-\u003etgt_rproc);\n+\tif (ab_ahb-\u003euserpd_id \u003e 0 \u0026\u0026\n+\t ab_ahb-\u003euserpd_id \u003c ATH11K_AHB_USERPD_ID_MAX)\n+\t\tath11k_ahb_stop_user_pd(ab);\n }\n \n static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab)\n@@ -798,6 +895,18 @@ static int ath11k_ahb_hif_resume(struct ath11k_base *ab)\n \treturn 0;\n }\n \n+static void ath11k_ahb_config_static_window_qcn6122(struct ath11k_base *ab)\n+{\n+\tu32 umac_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET);\n+\tu32 ce_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE);\n+\tu32 window;\n+\n+\twindow = (umac_window) | (ce_window \u003c\u003c 6);\n+\n+\tiowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window,\n+\t\t ab-\u003emem + ATH11K_PCI_WINDOW_REG_ADDRESS);\n+}\n+\n static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = {\n \t.start = ath11k_ahb_start,\n \t.stop = ath11k_ahb_stop,\n@@ -813,6 +922,24 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = {\n \t.ce_irq_disable = ath11k_ahb_ce_irq_disable_sync,\n };\n \n+static const struct ath11k_hif_ops ath11k_ahb_hif_ops_qcn6122 = {\n+\t.start = ath11k_pcic_start,\n+\t.stop = ath11k_pcic_stop,\n+\t.read32 = ath11k_pcic_read32,\n+\t.write32 = ath11k_pcic_write32,\n+\t.read = NULL,\n+\t.irq_enable = ath11k_pcic_ext_irq_enable,\n+\t.irq_disable = ath11k_pcic_ext_irq_disable,\n+\t.get_msi_address = ath11k_pcic_get_msi_address,\n+\t.get_user_msi_vector = ath11k_pcic_get_user_msi_assignment,\n+\t.map_service_to_pipe = ath11k_pcic_map_service_to_pipe,\n+\t.power_down = ath11k_ahb_power_down,\n+\t.power_up = ath11k_ahb_power_up,\n+\t.ce_irq_enable = ath11k_pci_enable_ce_irqs_except_wake_irq,\n+\t.ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq,\n+\t.config_static_window = ath11k_ahb_config_static_window_qcn6122,\n+};\n+\n static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = {\n \t.start = ath11k_pcic_start,\n \t.stop = ath11k_pcic_stop,\n@@ -832,24 +959,411 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = {\n \t.ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq,\n };\n \n+static int ath11k_ahb_init_userpd(struct ath11k_base *ab, int userpd_id)\n+{\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\n+\tlockdep_assert_held(\u0026ath11k_rproc_info_lock);\n+\n+\tswitch (ab-\u003ehw_rev) {\n+\tcase ATH11K_HW_IPQ5018_HW10:\n+\t\tif (userpd_id != ATH11K_AHB_USERPD_ID_1)\n+\t\t\treturn -EINVAL;\n+\n+\t\tab_ahb-\u003euserpd_id = userpd_id;\n+\t\tbreak;\n+\tcase ATH11K_HW_QCN6122_HW10:\n+\t\tif (userpd_id != ATH11K_AHB_USERPD_ID_2 \u0026\u0026\n+\t\t userpd_id != ATH11K_AHB_USERPD_ID_3)\n+\t\t\treturn -EINVAL;\n+\n+\t\tab_ahb-\u003euserpd_id = userpd_id;\n+\t\tab-\u003eqmi.service_ins_id += (ab_ahb-\u003euserpd_id - 1);\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tg_rproc_info-\u003euserpd[userpd_id - 1] = ab_ahb;\n+\tg_rproc_info-\u003enum_userpd++;\n+\n+\treturn 0;\n+}\n+\n+static irqreturn_t ath11k_userpd_irq_handler(int irq, void *data)\n+{\n+\tstruct ath11k_base *ab = data;\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\n+\tif (irq == ab_ahb-\u003euserpd_irq_num[ATH11K_USERPD_SPAWN_IRQ]) {\n+\t\tcomplete(\u0026ab_ahb-\u003euserpd_spawned);\n+\t} else if (irq == ab_ahb-\u003euserpd_irq_num[ATH11K_USERPD_READY_IRQ]) {\n+\t\tcomplete(\u0026ab_ahb-\u003euserpd_ready);\n+\t} else if (irq == ab_ahb-\u003euserpd_irq_num[ATH11K_USERPD_STOP_ACK_IRQ])\t{\n+\t\tcomplete(\u0026ab_ahb-\u003euserpd_stopped);\n+\t} else {\n+\t\tath11k_err(ab, \"Invalid userpd interrupt\\n\");\n+\t\treturn IRQ_NONE;\n+\t}\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static void ath11k_ahb_cleanup_userpd(struct ath11k_base *ab)\n+{\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\tstruct ath11k_ahb_rproc_info *rproc_info = ab_ahb-\u003erproc_info;\n+\n+\tlockdep_assert_held(\u0026ath11k_rproc_info_lock);\n+\n+\tif (!rproc_info)\n+\t\treturn;\n+\n+\tif (rproc_info \u0026\u0026 ab_ahb-\u003euserpd_id \u003e 0 \u0026\u0026\n+\t ab_ahb-\u003euserpd_id \u003c ATH11K_AHB_USERPD_ID_MAX) {\n+\t\trproc_info-\u003euserpd[ab_ahb-\u003euserpd_id - 1] = NULL;\n+\t\trproc_info-\u003enum_userpd--;\n+\t\tab_ahb-\u003erproc_info = NULL;\n+\t}\n+}\n+\n+static int ath11k_ahb_config_userpd_irq(struct ath11k_base *ab)\n+{\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\tchar *upd_irq_name;\n+\tint userpd_id;\n+\tint i, ret;\n+\n+\tab_ahb-\u003espawn_state = devm_qcom_smem_state_get(\u0026ab-\u003epdev-\u003edev, \"spawn\",\n+\t\t\t\t\t\t \u0026ab_ahb-\u003espawn_bit);\n+\tif (IS_ERR(ab_ahb-\u003espawn_state))\n+\t\treturn dev_err_probe(\u0026ab-\u003epdev-\u003edev, PTR_ERR(ab_ahb-\u003espawn_state),\n+\t\t\t\t \"Failed to acquire spawn state\\n\");\n+\n+\tab_ahb-\u003estop_state = devm_qcom_smem_state_get(\u0026ab-\u003epdev-\u003edev, \"stop\",\n+\t\t\t\t\t\t \u0026ab_ahb-\u003estop_bit);\n+\tif (IS_ERR(ab_ahb-\u003estop_state))\n+\t\treturn dev_err_probe(\u0026ab-\u003epdev-\u003edev, PTR_ERR(ab_ahb-\u003estop_state),\n+\t\t\t\t \"Failed to acquire stop state\\n\");\n+\n+\tmutex_lock(\u0026ath11k_rproc_info_lock);\n+\n+\tuserpd_id = ab_ahb-\u003espawn_bit / 8;\n+\tret = ath11k_ahb_init_userpd(ab, userpd_id);\n+\tif (ret) {\n+\t\tmutex_unlock(\u0026ath11k_rproc_info_lock);\n+\t\treturn ret;\n+\t}\n+\n+\tmutex_unlock(\u0026ath11k_rproc_info_lock);\n+\n+\tfor (i = 0; i \u003c ATH11K_USERPD_MAX_IRQ; i++) {\n+\t\tab_ahb-\u003euserpd_irq_num[i] = platform_get_irq_byname_optional(ab-\u003epdev,\n+\t\t\t\t\t\t\t\t\t ath11k_userpd_irq[i]);\n+\t\tif (ab_ahb-\u003euserpd_irq_num[i] \u003c 0)\n+\t\t\treturn ab_ahb-\u003euserpd_irq_num[i];\n+\n+\t\tupd_irq_name = devm_kzalloc(\u0026ab-\u003epdev-\u003edev, ATH11K_UPD_IRQ_WRD_LEN,\n+\t\t\t\t\t GFP_KERNEL);\n+\t\tif (!upd_irq_name)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tscnprintf(upd_irq_name, ATH11K_UPD_IRQ_WRD_LEN, \"UserPD%u-%s\",\n+\t\t\t ab_ahb-\u003euserpd_id, ath11k_userpd_irq[i]);\n+\t\tret = devm_request_threaded_irq(\u0026ab-\u003epdev-\u003edev, ab_ahb-\u003euserpd_irq_num[i],\n+\t\t\t\t\t\tNULL, ath11k_userpd_irq_handler,\n+\t\t\t\t\t\tIRQF_TRIGGER_RISING | IRQF_ONESHOT,\n+\t\t\t\t\t\tupd_irq_name, ab);\n+\t\tif (ret)\n+\t\t\treturn dev_err_probe(\u0026ab-\u003epdev-\u003edev, ret,\n+\t\t\t\t\t \"Request %s irq failed: %d\\n\",\n+\t\t\t\t\t ath11k_userpd_irq[i], ret);\n+\t}\n+\n+\tinit_completion(\u0026ab_ahb-\u003euserpd_spawned);\n+\tinit_completion(\u0026ab_ahb-\u003euserpd_ready);\n+\tinit_completion(\u0026ab_ahb-\u003euserpd_stopped);\n+\n+\treturn 0;\n+}\n+\n+static int ath11k_ahb_root_pd_state_notifier(struct notifier_block *nb,\n+\t\t\t\t\t const unsigned long event, void *data)\n+{\n+\tstruct ath11k_ahb_rproc_info *rproc_info =\n+\t\tcontainer_of(nb, struct ath11k_ahb_rproc_info, root_pd_nb);\n+\n+\tif (event == ATH11K_RPROC_AFTER_POWERUP) {\n+\t\tath11k_dbg(NULL, ATH11K_DBG_AHB, \"Root PD is UP\\n\");\n+\t\tcomplete(\u0026rproc_info-\u003erootpd_ready);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int ath11k_ahb_register_rproc_notifier(void)\n+{\n+\tint ret;\n+\n+\tlockdep_assert_held(\u0026ath11k_rproc_info_lock);\n+\n+\tif (g_rproc_info-\u003eroot_pd_notifier)\n+\t\treturn 0;\n+\n+\tg_rproc_info-\u003eroot_pd_nb.notifier_call = ath11k_ahb_root_pd_state_notifier;\n+\n+\tg_rproc_info-\u003eroot_pd_notifier = qcom_register_ssr_notifier(g_rproc_info-\u003etgt_rproc-\u003ename,\n+\t\t\t\t\t\t\t\t \u0026g_rproc_info-\u003eroot_pd_nb);\n+\tif (IS_ERR(g_rproc_info-\u003eroot_pd_notifier)) {\n+\t\tret = PTR_ERR(g_rproc_info-\u003eroot_pd_notifier);\n+\t\tg_rproc_info-\u003eroot_pd_notifier = NULL;\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void ath11k_ahb_unregister_rproc_notifier(void)\n+{\n+\tlockdep_assert_held(\u0026ath11k_rproc_info_lock);\n+\n+\tif (!g_rproc_info-\u003eroot_pd_notifier)\n+\t\treturn;\n+\n+\tqcom_unregister_ssr_notifier(g_rproc_info-\u003eroot_pd_notifier,\n+\t\t\t\t \u0026g_rproc_info-\u003eroot_pd_nb);\n+\tg_rproc_info-\u003eroot_pd_notifier = NULL;\n+}\n+\n+static struct ath11k_ahb_rproc_info *ath11k_ahb_rproc_info_alloc(struct ath11k_base *ab)\n+{\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\tstruct ath11k_ahb_rproc_info *rproc_info;\n+\n+\tlockdep_assert_held(\u0026ath11k_rproc_info_lock);\n+\n+\trproc_info = kzalloc_obj(*rproc_info);\n+\tif (!rproc_info)\n+\t\treturn NULL;\n+\n+\trproc_info-\u003eroot_pd_booted = false;\n+\trproc_info-\u003em3_loaded = 0;\n+\tinit_completion(\u0026rproc_info-\u003erootpd_ready);\n+\tab_ahb-\u003erproc_info = rproc_info;\n+\n+\treturn rproc_info;\n+}\n+\n static int ath11k_core_get_rproc(struct ath11k_base *ab)\n {\n \tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n \tstruct device *dev = ab-\u003edev;\n \tstruct rproc *prproc;\n \tphandle rproc_phandle;\n+\tint ret;\n+\n+\tlockdep_assert_held(\u0026ath11k_rproc_info_lock);\n+\n+\tif (g_rproc_info) {\n+\t\tab_ahb-\u003erproc_info = g_rproc_info;\n+\t\treturn 0;\n+\t}\n+\n+\tg_rproc_info = ath11k_ahb_rproc_info_alloc(ab);\n+\tif (!g_rproc_info)\n+\t\treturn -ENOMEM;\n \n \tif (of_property_read_u32(dev-\u003eof_node, \"qcom,rproc\", \u0026rproc_phandle)) {\n \t\tath11k_err(ab, \"failed to get q6_rproc handle\\n\");\n-\t\treturn -ENOENT;\n+\t\tret = -ENOENT;\n+\t\tgoto err_free_rproc_info;\n \t}\n \n \tprproc = rproc_get_by_phandle(rproc_phandle);\n-\tif (!prproc)\n-\t\treturn dev_err_probe(\u0026ab-\u003epdev-\u003edev, -EPROBE_DEFER, \"failed to get rproc\\n\");\n-\tab_ahb-\u003etgt_rproc = prproc;\n+\tif (!prproc) {\n+\t\tret = dev_err_probe(dev, -EPROBE_DEFER,\n+\t\t\t\t \"failed to get rproc\\n\");\n+\t\tgoto err_free_rproc_info;\n+\t}\n+\tg_rproc_info-\u003etgt_rproc = prproc;\n+\n+\treturn 0;\n+\n+err_free_rproc_info:\n+\tab_ahb-\u003erproc_info = NULL;\n+\tkfree(g_rproc_info);\n+\tg_rproc_info = NULL;\n+\treturn ret;\n+}\n+\n+static int ath11k_ahb_boot_root_pd(struct ath11k_base *ab)\n+{\n+\tunsigned long time_left;\n+\tint ret;\n+\n+\tlockdep_assert_held(\u0026ath11k_rproc_info_lock);\n+\treinit_completion(\u0026g_rproc_info-\u003erootpd_ready);\n+\n+\tret = rproc_boot(g_rproc_info-\u003etgt_rproc);\n+\tif (ret \u003c 0) {\n+\t\tath11k_err(ab, \"RootPD boot failed\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\ttime_left = wait_for_completion_timeout(\u0026g_rproc_info-\u003erootpd_ready,\n+\t\t\t\t\t\tATH11K_ROOTPD_READY_TIMEOUT);\n+\tif (!time_left) {\n+\t\tath11k_err(ab, \"RootPD ready wait timed out\\n\");\n+\t\treturn -ETIMEDOUT;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int ath11k_ahb_load_m3_firmware(struct ath11k_base *ab)\n+{\n+\tchar m3_fw_name[ATH11K_FW_NAME_LEN];\n+\tconst struct firmware *m3_fw = NULL;\n+\tstruct device_node *node;\n+\tstruct resource res = {};\n+\tphys_addr_t mem_phys;\n+\tvoid *mem_region;\n+\tsize_t mem_size;\n+\tint ret;\n+\n+\tret = of_reserved_mem_region_to_resource_byname(ab-\u003edev-\u003eof_node,\n+\t\t\t\t\t\t\t\"m3-region\", \u0026res);\n+\t/* If m3 region isn't found, fall back to the mem region of the rproc */\n+\tif (ret \u0026\u0026 ret == -EINVAL) {\n+\t\tnode = g_rproc_info-\u003etgt_rproc-\u003edev.parent-\u003eof_node;\n+\t\tret = of_reserved_mem_region_to_resource(node, 0, \u0026res);\n+\t}\n+\tif (ret) {\n+\t\tath11k_err(ab, \"Failed to get m3 memory resource: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\tmem_phys = res.start;\n+\tmem_size = resource_size(\u0026res);\n+\tmem_region = devm_memremap(ab-\u003edev, mem_phys, mem_size, MEMREMAP_WC);\n+\n+\tsnprintf(m3_fw_name, sizeof(m3_fw_name), \"%s/%s/%s\",\n+\t\t ATH11K_FW_DIR, ab-\u003ehw_params.fw.dir, ATH11K_M3_MBN_FILE);\n+\n+\tret = request_firmware(\u0026m3_fw, m3_fw_name, ab-\u003edev);\n+\tif (ret \u003c 0) {\n+\t\tath11k_err(ab, \"Failed to request firmware: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (!m3_fw-\u003esize || m3_fw-\u003esize \u003e mem_size) {\n+\t\tath11k_err(ab, \"Invalid firmware size\\n\");\n+\t\trelease_firmware(m3_fw);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = qcom_mdt_load_no_init(ab-\u003edev, m3_fw, m3_fw_name, mem_region,\n+\t\t\t\t mem_phys, mem_size, \u0026mem_phys);\n+\tif (ret)\n+\t\tath11k_err(ab, \"Failed to load MDT segments: %d\\n\", ret);\n+\n+\trelease_firmware(m3_fw);\n+\n+\treturn ret;\n+}\n+\n+static int ath11k_ahb_configure_rproc(struct ath11k_base *ab)\n+{\n+\tint ret;\n+\n+\tmutex_lock(\u0026ath11k_rproc_info_lock);\n+\n+\tret = ath11k_core_get_rproc(ab);\n+\tif (ret \u003c 0) {\n+\t\tmutex_unlock(\u0026ath11k_rproc_info_lock);\n+\t\treturn ret;\n+\t}\n+\n+\tret = ath11k_ahb_register_rproc_notifier();\n+\tif (ret \u003c 0) {\n+\t\tret = dev_err_probe(\u0026ab-\u003epdev-\u003edev, ret,\n+\t\t\t\t \"failed to register rproc notifier\\n\");\n+\t\tgoto err_cleanup_userpd;\n+\t}\n+\n+\tif (ab-\u003ehw_params.m3_fw_support \u0026\u0026\n+\t ab-\u003ehw_params.fw.m3_loader == ath11k_m3_fw_loader_ahb \u0026\u0026\n+\t !(g_rproc_info-\u003em3_loaded \u0026 BIT(ab-\u003ehw_params.hw_rev))) {\n+\t\tret = ath11k_ahb_load_m3_firmware(ab);\n+\t\tif (ret) {\n+\t\t\tath11k_err(ab, \"failed to load m3 firmware: %d\\n\", ret);\n+\t\t\tgoto err_unreg_notifier;\n+\t\t}\n+\t\tg_rproc_info-\u003em3_loaded |= BIT(ab-\u003ehw_params.hw_rev);\n+\t}\n+\n+\tif (g_rproc_info-\u003etgt_rproc-\u003estate != RPROC_RUNNING) {\n+\t\tret = ath11k_ahb_boot_root_pd(ab);\n+\t\tif (ret) {\n+\t\t\tath11k_err(ab, \"failed to boot the remote processor Q6\\n\");\n+\t\t\tgoto err_unreg_notifier;\n+\t\t}\n+\t}\n+\tg_rproc_info-\u003eroot_pd_booted = true;\n+\n+\tmutex_unlock(\u0026ath11k_rproc_info_lock);\n+\n+\t/*\n+\t * UserPD interrupts are specific to multi-PD configs/firmware only.\n+\t * If interrupts aren't found, continue execution for non-MPD platforms.\n+\t */\n+\tret = ath11k_ahb_config_userpd_irq(ab);\n+\tif (ret \u0026\u0026 ret != -EINVAL \u0026\u0026 ret != -ENXIO)\n+\t\treturn dev_err_probe(\u0026ab-\u003epdev-\u003edev, ret,\n+\t\t\t\t \"failed to configure userpd interrupts\\n\");\n \n \treturn 0;\n+\n+err_unreg_notifier:\n+\tath11k_ahb_unregister_rproc_notifier();\n+\n+err_cleanup_userpd:\n+\tath11k_ahb_cleanup_userpd(ab);\n+\tif (g_rproc_info \u0026\u0026 !g_rproc_info-\u003enum_userpd) {\n+\t\trproc_put(g_rproc_info-\u003etgt_rproc);\n+\t\tkfree(g_rproc_info);\n+\t\tg_rproc_info = NULL;\n+\t}\n+\n+\tmutex_unlock(\u0026ath11k_rproc_info_lock);\n+\treturn ret;\n+}\n+\n+static void ath11k_ahb_deconfigure_rproc(struct ath11k_base *ab)\n+{\n+\tstruct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);\n+\tstruct ath11k_ahb_rproc_info *rproc_info = ab_ahb-\u003erproc_info;\n+\n+\tif (!rproc_info || !g_rproc_info)\n+\t\treturn;\n+\n+\tmutex_lock(\u0026ath11k_rproc_info_lock);\n+\n+\tath11k_ahb_cleanup_userpd(ab);\n+\n+\tif (!g_rproc_info-\u003enum_userpd) {\n+\t\tath11k_ahb_unregister_rproc_notifier();\n+\n+\t\tif (g_rproc_info-\u003eroot_pd_booted \u0026\u0026\n+\t\t g_rproc_info-\u003etgt_rproc-\u003estate == RPROC_RUNNING)\n+\t\t\trproc_shutdown(g_rproc_info-\u003etgt_rproc);\n+\n+\t\trproc_put(g_rproc_info-\u003etgt_rproc);\n+\t\tkfree(g_rproc_info);\n+\t\tg_rproc_info = NULL;\n+\t}\n+\n+\tmutex_unlock(\u0026ath11k_rproc_info_lock);\n }\n \n static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab)\n@@ -884,6 +1398,10 @@ static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab)\n \tab-\u003epci.msi.addr_hi = upper_32_bits(msi_addr_iova);\n \n \tret = of_property_read_u32_index(ab-\u003edev-\u003eof_node, \"interrupts\", 1, \u0026int_prop);\n+\tif (ret == -EINVAL)\n+\t\tret = of_property_read_u32_index(ab-\u003edev-\u003eof_node,\n+\t\t\t\t\t\t \"interrupts-extended\", 2,\n+\t\t\t\t\t\t \u0026int_prop);\n \tif (ret)\n \t\treturn ret;\n \n@@ -1148,6 +1666,7 @@ static int ath11k_ahb_probe(struct platform_device *pdev)\n \tstruct ath11k_base *ab;\n \tconst struct ath11k_hif_ops *hif_ops;\n \tconst struct ath11k_pci_ops *pci_ops;\n+\tstruct ath11k_ahb *ab_ahb;\n \tenum ath11k_hw_rev hw_rev;\n \tint ret;\n \n@@ -1164,6 +1683,10 @@ static int ath11k_ahb_probe(struct platform_device *pdev)\n \t\thif_ops = \u0026ath11k_ahb_hif_ops_wcn6750;\n \t\tpci_ops = \u0026ath11k_ahb_pci_ops_wcn6750;\n \t\tbreak;\n+\tcase ATH11K_HW_QCN6122_HW10:\n+\t\thif_ops = \u0026ath11k_ahb_hif_ops_qcn6122;\n+\t\tpci_ops = \u0026ath11k_ahb_pci_ops_wcn6750;\n+\t\tbreak;\n \tdefault:\n \t\tdev_err(\u0026pdev-\u003edev, \"unsupported device type %d\\n\", hw_rev);\n \t\treturn -EOPNOTSUPP;\n@@ -1187,6 +1710,9 @@ static int ath11k_ahb_probe(struct platform_device *pdev)\n \tab-\u003ehw_rev = hw_rev;\n \tab-\u003efw_mode = ATH11K_FIRMWARE_MODE_NORMAL;\n \tplatform_set_drvdata(pdev, ab);\n+\tab_ahb = ath11k_ahb_priv(ab);\n+\tab_ahb-\u003eab = ab;\n+\tab_ahb-\u003euserpd_id = 0;\n \n \tret = ath11k_pcic_register_pci_ops(ab, pci_ops);\n \tif (ret) {\n@@ -1226,26 +1752,29 @@ static int ath11k_ahb_probe(struct platform_device *pdev)\n \n \tath11k_ahb_init_qmi_ce_config(ab);\n \n-\tret = ath11k_core_get_rproc(ab);\n+\tret = ath11k_ahb_configure_rproc(ab);\n \tif (ret)\n \t\tgoto err_ce_free;\n \n \tret = ath11k_core_init(ab);\n \tif (ret) {\n \t\tath11k_err(ab, \"failed to init core: %d\\n\", ret);\n-\t\tgoto err_ce_free;\n+\t\tgoto err_rproc_deconfigure;\n \t}\n \n \tret = ath11k_ahb_config_irq(ab);\n \tif (ret) {\n \t\tath11k_err(ab, \"failed to configure irq: %d\\n\", ret);\n-\t\tgoto err_ce_free;\n+\t\tgoto err_rproc_deconfigure;\n \t}\n \n \tath11k_qmi_fwreset_from_cold_boot(ab);\n \n \treturn 0;\n \n+err_rproc_deconfigure:\n+\tath11k_ahb_deconfigure_rproc(ab);\n+\n err_ce_free:\n \tath11k_ce_free_pipes(ab);\n \n@@ -1294,7 +1823,7 @@ static void ath11k_ahb_free_resources(struct ath11k_base *ab)\n \tath11k_ahb_fw_resource_deinit(ab);\n \tath11k_ce_free_pipes(ab);\n \tath11k_ahb_ce_unmap(ab);\n-\n+\tath11k_ahb_deconfigure_rproc(ab);\n \tath11k_core_free(ab);\n \tplatform_set_drvdata(pdev, NULL);\n }\ndiff --git a/drivers/net/wireless/ath/ath11k/ahb.h b/drivers/net/wireless/ath/ath11k/ahb.h\nindex 8c1eb1e8e6b13..ec89e0e89ca00 100644\n--- a/drivers/net/wireless/ath/ath11k/ahb.h\n+++ b/drivers/net/wireless/ath/ath11k/ahb.h\n@@ -6,6 +6,8 @@\n #ifndef ATH11K_AHB_H\n #define ATH11K_AHB_H\n \n+#include \u003clinux/remoteproc/qcom_rproc.h\u003e\n+\n #include \"core.h\"\n \n #define ATH11K_AHB_RECOVERY_TIMEOUT (3 * HZ)\n@@ -14,6 +16,23 @@\n #define ATH11K_AHB_SMP2P_SMEM_SEQ_NO\t\tGENMASK(31, 16)\n #define ATH11K_AHB_SMP2P_SMEM_VALUE_MASK\t0xFFFFFFFF\n \n+#define ATH11K_ROOTPD_READY_TIMEOUT\t\t(5 * HZ)\n+#define ATH11K_RPROC_AFTER_POWERUP\t\tQCOM_SSR_AFTER_POWERUP\n+\n+#define ATH11K_USERPD_SPAWN_TIMEOUT\t\t(5 * HZ)\n+#define ATH11K_USERPD_READY_TIMEOUT\t\t(10 * HZ)\n+#define ATH11K_USERPD_STOP_TIMEOUT\t\t(5 * HZ)\n+#define ATH11K_USERPD_FW_NAME_LEN\t\t35\n+\n+#define MPD_WCNSS_PAS_ID\t\t\t0xD\n+\n+enum ath11k_ahb_userpd_id {\n+\tATH11K_AHB_USERPD_ID_1 = 1,\n+\tATH11K_AHB_USERPD_ID_2,\n+\tATH11K_AHB_USERPD_ID_3,\n+\tATH11K_AHB_USERPD_ID_MAX,\n+};\n+\n enum ath11k_ahb_smp2p_msg_id {\n \tATH11K_AHB_POWER_SAVE_ENTER = 1,\n \tATH11K_AHB_POWER_SAVE_EXIT,\n@@ -21,8 +40,30 @@ enum ath11k_ahb_smp2p_msg_id {\n \n struct ath11k_base;\n \n-struct ath11k_ahb {\n+struct ath11k_ahb_rproc_info {\n \tstruct rproc *tgt_rproc;\n+\n+\tstruct completion rootpd_ready;\n+\tstruct notifier_block root_pd_nb;\n+\tvoid *root_pd_notifier;\n+\tbool root_pd_booted;\n+\n+\tu8 num_userpd;\n+\tstruct ath11k_ahb *userpd[ATH11K_AHB_USERPD_ID_MAX];\n+\n+\t/* Bitmap of loaded M3 firmwares indexed by hardware revision */\n+\tu32 m3_loaded;\n+};\n+\n+enum ath11k_ahb_userpd_irq {\n+\tATH11K_USERPD_SPAWN_IRQ,\n+\tATH11K_USERPD_READY_IRQ,\n+\tATH11K_USERPD_STOP_ACK_IRQ,\n+\tATH11K_USERPD_MAX_IRQ,\n+};\n+\n+struct ath11k_ahb {\n+\tstruct ath11k_base *ab;\n \tstruct {\n \t\tstruct device *dev;\n \t\tstruct iommu_domain *iommu_domain;\n@@ -37,6 +78,17 @@ struct ath11k_ahb {\n \t\tunsigned int smem_bit;\n \t\tstruct qcom_smem_state *smem_state;\n \t} smp2p_info;\n+\tstruct ath11k_ahb_rproc_info *rproc_info;\n+\n+\tstruct qcom_smem_state *spawn_state;\n+\tstruct qcom_smem_state *stop_state;\n+\tstruct completion userpd_spawned;\n+\tstruct completion userpd_ready;\n+\tstruct completion userpd_stopped;\n+\tu32 userpd_id;\n+\tu32 spawn_bit;\n+\tu32 stop_bit;\n+\tint userpd_irq_num[ATH11K_USERPD_MAX_IRQ];\n };\n \n static inline struct ath11k_ahb *ath11k_ahb_priv(struct ath11k_base *ab)\ndiff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c\nindex d2ed6a0ea7e36..8fa1171b6bdd6 100644\n--- a/drivers/net/wireless/ath/ath11k/core.c\n+++ b/drivers/net/wireless/ath/ath11k/core.c\n@@ -46,6 +46,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"IPQ8074/hw2.0\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_ahb,\n \t\t},\n \t\t.max_radios = 3,\n \t\t.bdf_addr = 0x4B0C0000,\n@@ -135,6 +136,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"IPQ6018/hw1.0\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_ahb,\n \t\t},\n \t\t.max_radios = 2,\n \t\t.bdf_addr = 0x4ABC0000,\n@@ -222,6 +224,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"QCA6390/hw2.0\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_qmi,\n \t\t},\n \t\t.max_radios = 3,\n \t\t.bdf_addr = 0x4B0C0000,\n@@ -314,6 +317,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"QCN9074/hw1.0\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_qmi,\n \t\t},\n \t\t.max_radios = 1,\n \t\t.single_pdev_only = false,\n@@ -400,6 +404,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"WCN6855/hw2.0\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_qmi,\n \t\t},\n \t\t.max_radios = 3,\n \t\t.bdf_addr = 0x4B0C0000,\n@@ -492,6 +497,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"WCN6855/hw2.1\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_qmi,\n \t\t},\n \t\t.max_radios = 3,\n \t\t.bdf_addr = 0x4B0C0000,\n@@ -582,6 +588,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"WCN6750/hw1.0\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_qmi,\n \t\t},\n \t\t.max_radios = 1,\n \t\t.bdf_addr = 0x4B0C0000,\n@@ -667,6 +674,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"IPQ5018/hw1.0\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_ahb,\n \t\t},\n \t\t.max_radios = MAX_RADIOS_5018,\n \t\t.bdf_addr = 0x4BA00000,\n@@ -724,7 +732,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t.dbr_debug_support = true,\n \t\t.global_reset = false,\n \t\t.bios_sar_capa = NULL,\n-\t\t.m3_fw_support = false,\n+\t\t.m3_fw_support = true,\n \t\t.fixed_bdf_addr = true,\n \t\t.fixed_mem_region = true,\n \t\t.static_window_map = false,\n@@ -752,6 +760,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"QCA2066/hw2.1\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_qmi,\n \t\t},\n \t\t.max_radios = 3,\n \t\t.bdf_addr = 0x4B0C0000,\n@@ -843,6 +852,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t\t.dir = \"QCA6698AQ/hw2.1\",\n \t\t\t.board_size = 256 * 1024,\n \t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_qmi,\n \t\t},\n \t\t.max_radios = 3,\n \t\t.bdf_addr = 0x4B0C0000,\n@@ -926,6 +936,93 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {\n \t\t.cfr_num_stream_bufs = 255,\n \t\t.cfr_stream_buf_size = 8200,\n \t},\n+\t{\n+\t\t.hw_rev = ATH11K_HW_QCN6122_HW10,\n+\t\t.name = \"qcn6122 hw1.0\",\n+\t\t.fw = {\n+\t\t\t.dir = \"QCN6122/hw1.0\",\n+\t\t\t.board_size = 256 * 1024,\n+\t\t\t.cal_offset = 128 * 1024,\n+\t\t\t.m3_loader = ath11k_m3_fw_loader_ahb,\n+\t\t},\n+\t\t.max_radios = MAX_RADIOS_5018,\n+\t\t.bdf_addr = 0x4D200000,\n+\t\t.hw_ops = \u0026ipq5018_ops,\n+\t\t.ring_mask = \u0026ath11k_hw_ring_mask_qcn6122,\n+\t\t.internal_sleep_clock = false,\n+\t\t.regs = \u0026qcn6122_regs,\n+\t\t.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122,\n+\t\t.host_ce_config = ath11k_host_ce_config_qcn9074,\n+\t\t.ce_count = CE_CNT_5018,\n+\t\t.target_ce_config = ath11k_target_ce_config_wlan_ipq5018,\n+\t\t.target_ce_count = TARGET_CE_CNT_5018,\n+\t\t.svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq5018,\n+\t\t.svc_to_ce_map_len = SVC_CE_MAP_LEN_5018,\n+\t\t.single_pdev_only = false,\n+\t\t.rxdma1_enable = true,\n+\t\t.num_rxdma_per_pdev = RXDMA_PER_PDEV_5018,\n+\t\t.rx_mac_buf_ring = false,\n+\t\t.vdev_start_delay = false,\n+\t\t.htt_peer_map_v2 = true,\n+\n+\t\t.spectral = {\n+\t\t\t.fft_sz = 2,\n+\t\t\t.fft_pad_sz = 0,\n+\t\t\t.summary_pad_sz = 16,\n+\t\t\t.fft_hdr_len = 24,\n+\t\t\t.max_fft_bins = 1024,\n+\t\t\t.fragment_160mhz = false,\n+\t\t},\n+\n+\t\t.interface_modes = BIT(NL80211_IFTYPE_STATION) |\n+\t\t\t\t\tBIT(NL80211_IFTYPE_AP) |\n+\t\t\t\t\tBIT(NL80211_IFTYPE_MESH_POINT),\n+\t\t.supports_monitor = true,\n+\t\t.full_monitor_mode = false,\n+\t\t.supports_shadow_regs = false,\n+\t\t.idle_ps = false,\n+\t\t.supports_sta_ps = false,\n+\t\t.coldboot_cal_mm = false,\n+\t\t.coldboot_cal_ftm = false,\n+\t\t.cbcal_restart_fw = true,\n+\t\t.fw_mem_mode = 1,\n+\t\t.num_vdevs = 16 + 1,\n+\t\t.num_peers = 512,\n+\t\t.supports_suspend = false,\n+\t\t.hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),\n+\t\t.supports_regdb = false,\n+\t\t.fix_l1ss = true,\n+\t\t.credit_flow = false,\n+\t\t.hal_params = \u0026ath11k_hw_hal_params_ipq5018,\n+\t\t.supports_dynamic_smps_6ghz = false,\n+\t\t.alloc_cacheable_memory = false,\n+\t\t.supports_rssi_stats = false,\n+\t\t.fw_wmi_diag_event = false,\n+\t\t.current_cc_support = false,\n+\t\t.dbr_debug_support = true,\n+\t\t.global_reset = false,\n+\t\t.bios_sar_capa = NULL,\n+\t\t.m3_fw_support = true,\n+\t\t.fixed_bdf_addr = true,\n+\t\t.fixed_mem_region = true,\n+\t\t.static_window_map = true,\n+\t\t.hybrid_bus_type = true,\n+\t\t.fixed_fw_mem = false,\n+\t\t.support_off_channel_tx = false,\n+\t\t.supports_multi_bssid = false,\n+\n+\t\t.sram_dump = {},\n+\n+\t\t.tcl_ring_retry = true,\n+\t\t.tx_ring_size = DP_TCL_DATA_RING_SIZE,\n+\t\t.smp2p_wow_exit = false,\n+\t\t.support_fw_mac_sequence = false,\n+\t\t.support_dual_stations = false,\n+\t\t.pdev_suspend = false,\n+\t\t.cfr_support = false,\n+\t\t.cfr_num_stream_bufs = 0,\n+\t\t.cfr_stream_buf_size = 0,\n+\t},\n };\n \n static const struct dmi_system_id ath11k_pm_quirk_table[] = {\ndiff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h\nindex a0d725923ef24..e92ec95dd3842 100644\n--- a/drivers/net/wireless/ath/ath11k/core.h\n+++ b/drivers/net/wireless/ath/ath11k/core.h\n@@ -153,6 +153,7 @@ enum ath11k_hw_rev {\n \tATH11K_HW_IPQ5018_HW10,\n \tATH11K_HW_QCA2066_HW21,\n \tATH11K_HW_QCA6698AQ_HW21,\n+\tATH11K_HW_QCN6122_HW10,\n };\n \n enum ath11k_firmware_mode {\ndiff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h\nindex 017fed1b4bd15..2e1a4fedf1391 100644\n--- a/drivers/net/wireless/ath/ath11k/hif.h\n+++ b/drivers/net/wireless/ath/ath11k/hif.h\n@@ -32,6 +32,7 @@ struct ath11k_hif_ops {\n \tvoid (*ce_irq_disable)(struct ath11k_base *ab);\n \tvoid (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);\n \tvoid (*coredump_download)(struct ath11k_base *ab);\n+\tvoid (*config_static_window)(struct ath11k_base *ab);\n };\n \n static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab)\n@@ -159,4 +160,12 @@ static inline void ath11k_hif_coredump_download(struct ath11k_base *ab)\n \t\tab-\u003ehif.ops-\u003ecoredump_download(ab);\n }\n \n+static inline void ath11k_hif_config_static_window(struct ath11k_base *ab)\n+{\n+\tif (!ab-\u003ehw_params.static_window_map || !ab-\u003ehif.ops-\u003econfig_static_window)\n+\t\treturn;\n+\n+\tab-\u003ehif.ops-\u003econfig_static_window(ab);\n+}\n+\n #endif /* _HIF_H_ */\ndiff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c\nindex 93f9a03c48dc0..bb375efd50fed 100644\n--- a/drivers/net/wireless/ath/ath11k/hw.c\n+++ b/drivers/net/wireless/ath/ath11k/hw.c\n@@ -2070,6 +2070,43 @@ const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750 = {\n \t},\n };\n \n+const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122 = {\n+\t.tx = {\n+\t\tATH11K_TX_RING_MASK_0,\n+\t\tATH11K_TX_RING_MASK_1,\n+\t\tATH11K_TX_RING_MASK_2,\n+\t},\n+\t.rx_mon_status = {\n+\t\t0, 0, 0,\n+\t\tATH11K_RX_MON_STATUS_RING_MASK_0,\n+\t},\n+\t.rx = {\n+\t\t0, 0, 0, 0,\n+\t\tATH11K_RX_RING_MASK_0,\n+\t\tATH11K_RX_RING_MASK_1,\n+\t\tATH11K_RX_RING_MASK_2,\n+\t\tATH11K_RX_RING_MASK_3,\n+\t},\n+\t.rx_err = {\n+\t\t0, 0, 0, 0, 0, 0, 0, 0,\n+\t\tATH11K_RX_ERR_RING_MASK_0,\n+\t},\n+\t.rx_wbm_rel = {\n+\t\t0, 0, 0, 0, 0, 0, 0, 0, 0,\n+\t\tATH11K_RX_WBM_REL_RING_MASK_0,\n+\t},\n+\t.reo_status = {\n+\t\t0, 0, 0,\n+\t\tATH11K_REO_STATUS_RING_MASK_0,\n+\t},\n+\t.rxdma2host = {\n+\t\tATH11K_RXDMA2HOST_RING_MASK_0,\n+\t},\n+\t.host2rxdma = {\n+\t\tATH11K_HOST2RXDMA_RING_MASK_0,\n+\t},\n+};\n+\n /* Target firmware's Copy Engine configuration for IPQ5018 */\n const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq5018[] = {\n \t/* CE0: host-\u003etarget HTC control and raw streams */\n@@ -2836,6 +2873,81 @@ const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq5018 = {\n \t.num_tx_rings = ARRAY_SIZE(ath11k_hw_tcl2wbm_rbm_map_ipq5018),\n };\n \n+const struct ath11k_hw_regs qcn6122_regs = {\n+\t/* SW2TCL(x) R0 ring configuration address */\n+\t.hal_tcl1_ring_base_lsb = 0x00000694,\n+\t.hal_tcl1_ring_base_msb = 0x00000698,\n+\t.hal_tcl1_ring_id = 0x0000069c,\n+\t.hal_tcl1_ring_misc = 0x000006a4,\n+\t.hal_tcl1_ring_tp_addr_lsb = 0x000006b0,\n+\t.hal_tcl1_ring_tp_addr_msb = 0x000006b4,\n+\t.hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c4,\n+\t.hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c8,\n+\t.hal_tcl1_ring_msi1_base_lsb = 0x000006dc,\n+\t.hal_tcl1_ring_msi1_base_msb = 0x000006e0,\n+\t.hal_tcl1_ring_msi1_data = 0x000006e4,\n+\t.hal_tcl2_ring_base_lsb = 0x000006ec,\n+\t.hal_tcl_ring_base_lsb = 0x0000079c,\n+\n+\t/* TCL STATUS ring address */\n+\t.hal_tcl_status_ring_base_lsb = 0x000008a4,\n+\n+\t/* REO2SW(x) R0 ring configuration address */\n+\t.hal_reo1_ring_base_lsb = 0x000001ec,\n+\t.hal_reo1_ring_base_msb = 0x000001f0,\n+\t.hal_reo1_ring_id = 0x000001f4,\n+\t.hal_reo1_ring_misc = 0x000001fc,\n+\t.hal_reo1_ring_hp_addr_lsb = 0x00000200,\n+\t.hal_reo1_ring_hp_addr_msb = 0x00000204,\n+\t.hal_reo1_ring_producer_int_setup = 0x00000210,\n+\t.hal_reo1_ring_msi1_base_lsb = 0x00000234,\n+\t.hal_reo1_ring_msi1_base_msb = 0x00000238,\n+\t.hal_reo1_ring_msi1_data = 0x0000023c,\n+\t.hal_reo2_ring_base_lsb = 0x00000244,\n+\t.hal_reo1_aging_thresh_ix_0 = 0x00000564,\n+\t.hal_reo1_aging_thresh_ix_1 = 0x00000568,\n+\t.hal_reo1_aging_thresh_ix_2 = 0x0000056c,\n+\t.hal_reo1_aging_thresh_ix_3 = 0x00000570,\n+\n+\t/* REO2SW(x) R2 ring pointers (head/tail) address */\n+\t.hal_reo1_ring_hp = 0x00003028,\n+\t.hal_reo1_ring_tp = 0x0000302c,\n+\t.hal_reo2_ring_hp = 0x00003030,\n+\n+\t/* REO2TCL R0 ring configuration address */\n+\t.hal_reo_tcl_ring_base_lsb = 0x000003fc,\n+\t.hal_reo_tcl_ring_hp = 0x00003058,\n+\n+\t/* SW2REO ring address */\n+\t.hal_sw2reo_ring_base_lsb = 0x0000013c,\n+\t.hal_sw2reo_ring_hp = 0x00003018,\n+\n+\t/* REO CMD ring address */\n+\t.hal_reo_cmd_ring_base_lsb = 0x000000e4,\n+\t.hal_reo_cmd_ring_hp = 0x00003010,\n+\n+\t/* REO status address */\n+\t.hal_reo_status_ring_base_lsb = 0x00000504,\n+\t.hal_reo_status_hp = 0x00003070,\n+\n+\t/* WCSS relative address */\n+\t.hal_seq_wcss_umac_ce0_src_reg = 0x01b80000,\n+\t.hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000,\n+\t.hal_seq_wcss_umac_ce1_src_reg = 0x01b82000,\n+\t.hal_seq_wcss_umac_ce1_dst_reg = 0x01b83000,\n+\n+\t/* WBM Idle address */\n+\t.hal_wbm_idle_link_ring_base_lsb = 0x00000874,\n+\t.hal_wbm_idle_link_ring_misc = 0x00000884,\n+\n+\t/* SW2WBM release address */\n+\t.hal_wbm_release_ring_base_lsb = 0x000001ec,\n+\n+\t/* WBM2SW release address */\n+\t.hal_wbm0_release_ring_base_lsb = 0x00000924,\n+\t.hal_wbm1_release_ring_base_lsb = 0x0000097c,\n+};\n+\n const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = {\n \t.rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM,\n \t.tcl2wbm_rbm_map = ath11k_hw_tcl2wbm_rbm_map_ipq8074,\ndiff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h\nindex 4996536fbd14f..2bcfdae9519bc 100644\n--- a/drivers/net/wireless/ath/ath11k/hw.h\n+++ b/drivers/net/wireless/ath/ath11k/hw.h\n@@ -72,6 +72,7 @@\n #define ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4\n \n #define ATH11K_FW_DIR\t\t\t\"ath11k\"\n+#define ATH11K_FW_NAME_LEN\t\t35\n \n #define ATH11K_BOARD_MAGIC\t\t\"QCA-ATH11K-BOARD\"\n #define ATH11K_BOARD_API2_FILE\t\t\"board-2.bin\"\n@@ -79,6 +80,7 @@\n #define ATH11K_DEFAULT_CAL_FILE\t\t\"caldata.bin\"\n #define ATH11K_AMSS_FILE\t\t\"amss.bin\"\n #define ATH11K_M3_FILE\t\t\t\"m3.bin\"\n+#define ATH11K_M3_MBN_FILE\t\t\"m3_fw.mbn\"\n #define ATH11K_REGDB_FILE_NAME\t\t\"regdb.bin\"\n \n #define ATH11K_CE_OFFSET(ab)\t(ab-\u003emem_ce - ab-\u003emem)\n@@ -137,6 +139,11 @@ struct ath11k_hw_hal_params {\n \tsize_t num_tx_rings;\n };\n \n+enum ath11k_m3_fw_loaders {\n+\tath11k_m3_fw_loader_ahb,\n+\tath11k_m3_fw_loader_qmi,\n+};\n+\n struct ath11k_hw_params {\n \tconst char *name;\n \tu16 hw_rev;\n@@ -147,6 +154,7 @@ struct ath11k_hw_params {\n \t\tconst char *dir;\n \t\tsize_t board_size;\n \t\tsize_t cal_offset;\n+\t\tenum ath11k_m3_fw_loaders m3_loader;\n \t} fw;\n \n \tconst struct ath11k_hw_ops *hw_ops;\n@@ -288,6 +296,7 @@ extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074;\n extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390;\n extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074;\n extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750;\n+extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122;\n \n extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq8074;\n extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018;\n@@ -425,6 +434,7 @@ extern const struct ath11k_hw_regs qcn9074_regs;\n extern const struct ath11k_hw_regs wcn6855_regs;\n extern const struct ath11k_hw_regs wcn6750_regs;\n extern const struct ath11k_hw_regs ipq5018_regs;\n+extern const struct ath11k_hw_regs qcn6122_regs;\n \n static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type)\n {\ndiff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c\nindex 2259adc3bbdc6..cdcf3df26438c 100644\n--- a/drivers/net/wireless/ath/ath11k/pcic.c\n+++ b/drivers/net/wireless/ath/ath11k/pcic.c\n@@ -138,6 +138,15 @@ static const struct ath11k_msi_config ath11k_msi_config[] = {\n \t\t},\n \t\t.hw_rev = ATH11K_HW_QCA6698AQ_HW21,\n \t},\n+\t{\n+\t\t.total_vectors = 13,\n+\t\t.total_users = 2,\n+\t\t.users = (struct ath11k_msi_user[]) {\n+\t\t\t{ .name = \"CE\", .num_vectors = 5, .base_vector = 0 },\n+\t\t\t{ .name = \"DP\", .num_vectors = 8, .base_vector = 5 },\n+\t\t},\n+\t\t.hw_rev = ATH11K_HW_QCN6122_HW10,\n+\t},\n };\n \n int ath11k_pcic_init_msi_config(struct ath11k_base *ab)\ndiff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c\nindex 7dc07339b9579..7d77f5c8c609c 100644\n--- a/drivers/net/wireless/ath/ath11k/qmi.c\n+++ b/drivers/net/wireless/ath/ath11k/qmi.c\n@@ -1724,7 +1724,8 @@ int ath11k_qmi_host_cap_send(struct ath11k_base *ab)\n \treq.bdf_support_valid = 1;\n \treq.bdf_support = 1;\n \n-\tif (ab-\u003ehw_params.m3_fw_support) {\n+\tif (ab-\u003ehw_params.m3_fw_support \u0026\u0026\n+\t ab-\u003ehw_params.fw.m3_loader == ath11k_m3_fw_loader_qmi) {\n \t\treq.m3_support_valid = 1;\n \t\treq.m3_support = 1;\n \t\treq.m3_cache_support_valid = 1;\n@@ -2036,6 +2037,45 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)\n \treturn 0;\n }\n \n+static const char *ath11k_qmi_get_mem_reg_name(int mem_type)\n+{\n+\tswitch (mem_type) {\n+\tcase HOST_DDR_REGION_TYPE:\n+\tcase BDF_MEM_REGION_TYPE:\n+\t\treturn \"q6-region\";\n+\tcase M3_DUMP_REGION_TYPE:\n+\t\treturn \"m3-dump\";\n+\tdefault:\n+\t\treturn NULL;\n+\t}\n+}\n+\n+static int ath11k_qmi_reserved_memory_to_resource(struct ath11k_base *ab,\n+\t\t\t\t\t\t int mem_type,\n+\t\t\t\t\t\t struct resource *res)\n+{\n+\tconst char *rname;\n+\tint ret;\n+\n+\trname = ath11k_qmi_get_mem_reg_name(mem_type);\n+\tif (!rname) {\n+\t\tath11k_dbg(ab, ATH11K_DBG_QMI,\n+\t\t\t \"invalid memory type %d\\n\", mem_type);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = of_reserved_mem_region_to_resource_byname(ab-\u003edev-\u003eof_node,\n+\t\t\t\t\t\t\trname, res);\n+\tif (ret) {\n+\t\tath11k_dbg(ab, ATH11K_DBG_QMI,\n+\t\t\t \"failed to get reserved memory region for %s\\n\",\n+\t\t\t rname);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)\n {\n \tstruct device *dev = ab-\u003edev;\n@@ -2072,7 +2112,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)\n \t\t\tidx++;\n \t\t\tbreak;\n \t\tcase BDF_MEM_REGION_TYPE:\n-\t\t\tab-\u003eqmi.target_mem[idx].paddr = ab-\u003ehw_params.bdf_addr;\n+\t\t\tret = ath11k_qmi_reserved_memory_to_resource(ab, BDF_MEM_REGION_TYPE, \u0026res);\n+\t\t\tif (!ret \u0026\u0026 !(ab-\u003eqmi.target_mem[i].size \u003e resource_size(\u0026res)))\n+\t\t\t\tab-\u003eqmi.target_mem[idx].paddr = res.start;\n+\t\t\telse\n+\t\t\t\tab-\u003eqmi.target_mem[idx].paddr = ab-\u003ehw_params.bdf_addr;\n \t\t\tab-\u003eqmi.target_mem[idx].iaddr = NULL;\n \t\t\tab-\u003eqmi.target_mem[idx].size = ab-\u003eqmi.target_mem[i].size;\n \t\t\tab-\u003eqmi.target_mem[idx].type = ab-\u003eqmi.target_mem[i].type;\n@@ -2106,6 +2150,16 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)\n \t\t\tab-\u003eqmi.target_mem[idx].type = ab-\u003eqmi.target_mem[i].type;\n \t\t\tidx++;\n \t\t\tbreak;\n+\t\tcase M3_DUMP_REGION_TYPE:\n+\t\t\tret = ath11k_qmi_reserved_memory_to_resource(ab, M3_DUMP_REGION_TYPE, \u0026res);\n+\t\t\tif (!ret \u0026\u0026 !(ab-\u003eqmi.target_mem[i].size \u003e resource_size(\u0026res))) {\n+\t\t\t\tab-\u003eqmi.target_mem[idx].paddr = res.start;\n+\t\t\t\tab-\u003eqmi.target_mem[idx].iaddr = NULL;\n+\t\t\t\tab-\u003eqmi.target_mem[idx].size = ab-\u003eqmi.target_mem[i].size;\n+\t\t\t\tab-\u003eqmi.target_mem[idx].type = ab-\u003eqmi.target_mem[i].type;\n+\t\t\t\tidx++;\n+\t\t\t}\n+\t\t\tbreak;\n \t\tdefault:\n \t\t\tath11k_warn(ab, \"qmi ignore invalid mem req type %d\\n\",\n \t\t\t\t ab-\u003eqmi.target_mem[i].type);\n@@ -2189,6 +2243,8 @@ static int ath11k_qmi_request_device_info(struct ath11k_base *ab)\n \tif (!ab-\u003ehw_params.ce_remap)\n \t\tab-\u003emem_ce = ab-\u003emem;\n \n+\tath11k_hif_config_static_window(ab);\n+\n \treturn 0;\n out:\n \treturn ret;\n@@ -2299,6 +2355,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab,\n {\n \tstruct qmi_wlanfw_bdf_download_req_msg_v01 *req;\n \tstruct qmi_wlanfw_bdf_download_resp_msg_v01 resp;\n+\tstruct resource res = {};\n \tstruct qmi_txn txn;\n \tconst u8 *temp = data;\n \tvoid __iomem *bdf_addr = NULL;\n@@ -2312,7 +2369,11 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab,\n \tmemset(\u0026resp, 0, sizeof(resp));\n \n \tif (ab-\u003ehw_params.fixed_bdf_addr) {\n-\t\tbdf_addr = ioremap(ab-\u003ehw_params.bdf_addr, ab-\u003ehw_params.fw.board_size);\n+\t\tret = ath11k_qmi_reserved_memory_to_resource(ab, BDF_MEM_REGION_TYPE, \u0026res);\n+\t\tif (!ret \u0026\u0026 !(ab-\u003ehw_params.fw.board_size \u003e resource_size(\u0026res)))\n+\t\t\tbdf_addr = ioremap(res.start, ab-\u003ehw_params.fw.board_size);\n+\t\telse\n+\t\t\tbdf_addr = ioremap(ab-\u003ehw_params.bdf_addr, ab-\u003ehw_params.fw.board_size);\n \t\tif (!bdf_addr) {\n \t\t\tath11k_warn(ab, \"qmi ioremap error for bdf_addr\\n\");\n \t\t\tret = -EIO;\n@@ -2589,7 +2650,8 @@ int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab)\n \tmemset(\u0026req, 0, sizeof(req));\n \tmemset(\u0026resp, 0, sizeof(resp));\n \n-\tif (ab-\u003ehw_params.m3_fw_support) {\n+\tif (ab-\u003ehw_params.m3_fw_support \u0026\u0026\n+\t ab-\u003ehw_params.fw.m3_loader == ath11k_m3_fw_loader_qmi) {\n \t\tret = ath11k_qmi_m3_load(ab);\n \t\tif (ret) {\n \t\t\tath11k_err(ab, \"failed to load m3 firmware: %d\", ret);\ndiff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h\nindex 350816c81ee54..f5442a04f079d 100644\n--- a/drivers/net/wireless/ath/ath11k/qmi.h\n+++ b/drivers/net/wireless/ath/ath11k/qmi.h\n@@ -21,10 +21,11 @@\n #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074\t0x02\n #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074\t0x07\n #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750\t0x03\n+#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122\t0x40\n #define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01\t32\n #define ATH11K_QMI_RESP_LEN_MAX\t\t\t8192\n #define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01\t52\n-#define ATH11K_QMI_CALDB_SIZE\t\t\t0x480000\n+#define ATH11K_QMI_CALDB_SIZE\t\t\t0x500000\n #define ATH11K_QMI_BDF_EXT_STR_LENGTH\t\t0x20\n #define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT\t5\n \ndiff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h\nindex 5747bd191bf15..9258839ae7462 100644\n--- a/include/linux/firmware/qcom/qcom_scm.h\n+++ b/include/linux/firmware/qcom/qcom_scm.h\n@@ -93,6 +93,7 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c\n \t\t\t\t\t\t size_t *output_rt_size);\n \n int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx);\n+int qcom_scm_pas_set_wifi_power_mode(u32 peripheral, bool enable);\n \n int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);\n int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);\n"
} |
| 2/1 | 2026/09/25 16:39 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch series adds Multi-UserPD support for Qualcomm QCN6122 and IPQ5018 Wi-Fi devices in the ath11k AHB driver and adds a corresponding power mode SCM call in the Qualcomm SCM firmware driver. These changes require physical Qualcomm SoC hardware (remoteproc, SMEM, TrustZone SCM, and integrated Wi-Fi hardware) and are structurally unreachable in standard virtualized environments (QEMU/GCE on amd64).",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit d4a45fc6d8b284ef97dac5566b059fb6d17f2819
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Sep 25 16:39:55 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
index d4aa56e2f8233..10102b5ea8c81 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
@@ -17,10 +17,11 @@ description: |
properties:
compatible:
enum:
- - qcom,ipq8074-wifi
+ - qcom,ipq5018-wifi
- qcom,ipq6018-wifi
+ - qcom,ipq8074-wifi
+ - qcom,qcn6122-wifi
- qcom,wcn6750-wifi
- - qcom,ipq5018-wifi
reg:
maxItems: 1
@@ -29,8 +30,13 @@ properties:
minItems: 32
maxItems: 52
+ interrupts-extended:
+ minItems: 17
+ maxItems: 56
+
interrupt-names:
- maxItems: 52
+ minItems: 17
+ maxItems: 56
qcom,rproc:
$ref: /schemas/types.yaml#/definitions/phandle
@@ -56,11 +62,15 @@ properties:
memory-region:
minItems: 1
- maxItems: 2
+ maxItems: 3
description:
phandle to a node describing reserved memory (System RAM memory)
used by ath11k firmware (see bindings/reserved-memory/reserved-memory.txt)
+ memory-region-names:
+ minItems: 1
+ maxItems: 3
+
iommus:
minItems: 1
maxItems: 2
@@ -78,20 +88,11 @@ properties:
qcom,smem-states:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: State bits used by the AP to signal the WLAN Q6.
- items:
- - description: Signal bits used to enable/disable low power mode
- on WCN6750 in the case of WoW (Wake on Wireless).
- 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
+ minItems: 1
+ maxItems: 3
qcom,smem-state-names:
description: The names of the state bits used for SMP2P output.
- items:
- - const: wlan-smp2p-out
required:
- compatible
@@ -108,8 +109,244 @@ allOf:
compatible:
contains:
enum:
- - qcom,ipq8074-wifi
+ - qcom,ipq5018-wifi
+ then:
+ properties:
+ interrupts:
+ items:
+ - description: misc-pulse1 interrupt events
+ - description: misc-latch interrupt events
+ - description: sw exception interrupt events
+ - description: watchdog interrupt events
+ - description: interrupt event for ring CE0
+ - description: interrupt event for ring CE1
+ - description: interrupt event for ring CE2
+ - description: interrupt event for ring CE3
+ - description: interrupt event for ring CE4
+ - description: interrupt event for ring CE5
+ - description: interrupt event for ring CE6
+ - description: interrupt event for ring CE7
+ - description: interrupt event for ring CE8
+ - description: interrupt event for ring CE9
+ - description: interrupt event for ring CE10
+ - description: interrupt event for ring CE11
+ - description: interrupt event for ring host2wbm-desc-feed
+ - description: interrupt event for ring host2reo-re-injection
+ - description: interrupt event for ring host2reo-command
+ - description: interrupt event for ring host2rxdma-monitor-ring3
+ - description: interrupt event for ring host2rxdma-monitor-ring2
+ - description: interrupt event for ring host2rxdma-monitor-ring1
+ - description: interrupt event for ring reo2ost-exception
+ - description: interrupt event for ring wbm2host-rx-release
+ - description: interrupt event for ring reo2host-status
+ - description: interrupt event for ring reo2host-destination-ring4
+ - description: interrupt event for ring reo2host-destination-ring3
+ - description: interrupt event for ring reo2host-destination-ring2
+ - description: interrupt event for ring reo2host-destination-ring1
+ - description: interrupt event for ring rxdma2host-monitor-destination-mac3
+ - description: interrupt event for ring rxdma2host-monitor-destination-mac2
+ - description: interrupt event for ring rxdma2host-monitor-destination-mac1
+ - description: interrupt event for ring ppdu-end-interrupts-mac3
+ - description: interrupt event for ring ppdu-end-interrupts-mac2
+ - description: interrupt event for ring ppdu-end-interrupts-mac1
+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac3
+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac2
+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac1
+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac3
+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac2
+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac1
+ - description: interrupt event for ring rxdma2host-destination-ring-mac3
+ - description: interrupt event for ring rxdma2host-destination-ring-mac2
+ - description: interrupt event for ring rxdma2host-destination-ring-mac1
+ - description: interrupt event for ring host2tcl-input-ring4
+ - description: interrupt event for ring host2tcl-input-ring3
+ - description: interrupt event for ring host2tcl-input-ring2
+ - description: interrupt event for ring host2tcl-input-ring1
+ - description: interrupt event for ring wbm2host-tx-completions-ring3
+ - description: interrupt event for ring wbm2host-tx-completions-ring2
+ - description: interrupt event for ring wbm2host-tx-completions-ring1
+ - description: interrupt event for ring tcl2host-status-ring
+ interrupts-extended:
+ items:
+ - description: misc-pulse1 interrupt events
+ - description: misc-latch interrupt events
+ - description: sw exception interrupt events
+ - description: watchdog interrupt events
+ - description: interrupt event for ring CE0
+ - description: interrupt event for ring CE1
+ - description: interrupt event for ring CE2
+ - description: interrupt event for ring CE3
+ - description: interrupt event for ring CE4
+ - description: interrupt event for ring CE5
+ - description: interrupt event for ring CE6
+ - description: interrupt event for ring CE7
+ - description: interrupt event for ring CE8
+ - description: interrupt event for ring CE9
+ - description: interrupt event for ring CE10
+ - description: interrupt event for ring CE11
+ - description: interrupt event for ring host2wbm-desc-feed
+ - description: interrupt event for ring host2reo-re-injection
+ - description: interrupt event for ring host2reo-command
+ - description: interrupt event for ring host2rxdma-monitor-ring3
+ - description: interrupt event for ring host2rxdma-monitor-ring2
+ - description: interrupt event for ring host2rxdma-monitor-ring1
+ - description: interrupt event for ring reo2ost-exception
+ - description: interrupt event for ring wbm2host-rx-release
+ - description: interrupt event for ring reo2host-status
+ - description: interrupt event for ring reo2host-destination-ring4
+ - description: interrupt event for ring reo2host-destination-ring3
+ - description: interrupt event for ring reo2host-destination-ring2
+ - description: interrupt event for ring reo2host-destination-ring1
+ - description: interrupt event for ring rxdma2host-monitor-destination-mac3
+ - description: interrupt event for ring rxdma2host-monitor-destination-mac2
+ - description: interrupt event for ring rxdma2host-monitor-destination-mac1
+ - description: interrupt event for ring ppdu-end-interrupts-mac3
+ - description: interrupt event for ring ppdu-end-interrupts-mac2
+ - description: interrupt event for ring ppdu-end-interrupts-mac1
+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac3
+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac2
+ - description: interrupt event for ring rxdma2host-monitor-status-ring-mac1
+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac3
+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac2
+ - description: interrupt event for ring host2rxdma-host-buf-ring-mac1
+ - description: interrupt event for ring rxdma2host-destination-ring-mac3
+ - description: interrupt event for ring rxdma2host-destination-ring-mac2
+ - description: interrupt event for ring rxdma2host-destination-ring-mac1
+ - description: interrupt event for ring host2tcl-input-ring4
+ - description: interrupt event for ring host2tcl-input-ring3
+ - description: interrupt event for ring host2tcl-input-ring2
+ - description: interrupt event for ring host2tcl-input-ring1
+ - description: interrupt event for ring wbm2host-tx-completions-ring3
+ - description: interrupt event for ring wbm2host-tx-completions-ring2
+ - description: interrupt event for ring wbm2host-tx-completions-ring1
+ - description: interrupt event for ring tcl2host-status-ring
+ - description: Q6 firmware user PD fatal interrupt event
+ - description: Q6 firmware user PD ready interrupt event
+ - description: Q6 firmware user PD spawn interrupt event
+ - description: Q6 firmware user PD stop ack interrupt event
+ interrupt-names:
+ minItems: 52
+ items:
+ - const: misc-pulse1
+ - const: misc-latch
+ - const: sw-exception
+ - const: watchdog
+ - const: ce0
+ - const: ce1
+ - const: ce2
+ - const: ce3
+ - const: ce4
+ - const: ce5
+ - const: ce6
+ - const: ce7
+ - const: ce8
+ - const: ce9
+ - const: ce10
+ - const: ce11
+ - const: host2wbm-desc-feed
+ - const: host2reo-re-injection
+ - const: host2reo-command
+ - const: host2rxdma-monitor-ring3
+ - const: host2rxdma-monitor-ring2
+ - const: host2rxdma-monitor-ring1
+ - const: reo2ost-exception
+ - const: wbm2host-rx-release
+ - const: reo2host-status
+ - const: reo2host-destination-ring4
+ - const: reo2host-destination-ring3
+ - const: reo2host-destination-ring2
+ - const: reo2host-destination-ring1
+ - const: rxdma2host-monitor-destination-mac3
+ - const: rxdma2host-monitor-destination-mac2
+ - const: rxdma2host-monitor-destination-mac1
+ - const: ppdu-end-interrupts-mac3
+ - const: ppdu-end-interrupts-mac2
+ - const: ppdu-end-interrupts-mac1
+ - const: rxdma2host-monitor-status-ring-mac3
+ - const: rxdma2host-monitor-status-ring-mac2
+ - const: rxdma2host-monitor-status-ring-mac1
+ - const: host2rxdma-host-buf-ring-mac3
+ - const: host2rxdma-host-buf-ring-mac2
+ - const: host2rxdma-host-buf-ring-mac1
+ - const: rxdma2host-destination-ring-mac3
+ - const: rxdma2host-destination-ring-mac2
+ - const: rxdma2host-destination-ring-mac1
+ - const: host2tcl-input-ring4
+ - const: host2tcl-input-ring3
+ - const: host2tcl-input-ring2
+ - const: host2tcl-input-ring1
+ - const: wbm2host-tx-completions-ring3
+ - const: wbm2host-tx-completions-ring2
+ - const: wbm2host-tx-completions-ring1
+ - const: tcl2host-status-ring
+ - const: fatal
+ - const: ready
+ - const: spawn
+ - const: stop-ack
+ memory-region:
+ items:
+ - description: memory region for Q6 firmware
+ - description: memory region for M3 firmware
+ - description: memory region for M3 dump
+ memory-region-names:
+ items:
+ - const: q6-region
+ - const: m3-region
+ - const: m3-dump
+ qcom,smem-states:
+ items:
+ - description: Signal bit used to shutdown Q6 user PD
+ items:
+ - description: Phandle to the Shared Memory Point 2 Point device
+ - description: Single bit index to toggle in the value sent to the remote processor
+ maximum: 32
+ - description: Signal bit used to stop Q6 user PD
+ items:
+ - description: Phandle to the Shared Memory Point 2 Point device
+ - description: Single bit index to toggle in the value sent to the remote processor
+ maximum: 32
+ - description: Signal bit used to spawn Q6 user PD
+ items:
+ - description: Phandle to the Shared Memory Point 2 Point device
+ - description: Single bit index to toggle in the value sent to the remote processor
+ maximum: 32
+ qcom,smem-state-names:
+ items:
+ - const: shutdown
+ - const: stop
+ - const: spawn
+ allOf:
+ - anyOf:
+ - required:
+ - interrupts
+ - required:
+ - interrupts-extended
+ - if:
+ required:
+ - interrupts-extended
+ then:
+ properties:
+ interrupt-names:
+ minItems: 56
+ required:
+ - interrupts-extended
+ - memory-region
+ - memory-region-names
+ - qcom,smem-states
+ - qcom,smem-state-names
+ else:
+ properties:
+ interrupt-names:
+ minItems: 52
+ maxItems: 52
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- qcom,ipq6018-wifi
+ - qcom,ipq8074-wifi
then:
properties:
interrupts:
@@ -223,6 +460,93 @@ allOf:
required:
- interrupt-names
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,qcn6122-wifi
+ then:
+ properties:
+ interrupts-extended:
+ items:
+ - description: interrupt event for ring CE1
+ - description: interrupt event for ring CE2
+ - description: interrupt event for ring CE3
+ - description: interrupt event for ring CE4
+ - description: interrupt event for ring CE5
+ - description: interrupt event for ring DP1
+ - description: interrupt event for ring DP2
+ - description: interrupt event for ring DP3
+ - description: interrupt event for ring DP4
+ - description: interrupt event for ring DP5
+ - description: interrupt event for ring DP6
+ - description: interrupt event for ring DP7
+ - description: interrupt event for ring DP8
+ - description: Q6 firmware user PD fatal interrupt event
+ - description: Q6 firmware user PD ready interrupt event
+ - description: Q6 firmware user PD spawn interrupt event
+ - description: Q6 firmware user PD stop ack interrupt event
+ interrupt-names:
+ minItems: 17
+ items:
+ - const: ce1
+ - const: ce2
+ - const: ce3
+ - const: ce4
+ - const: ce5
+ - const: dp1
+ - const: dp2
+ - const: dp3
+ - const: dp4
+ - const: dp5
+ - const: dp6
+ - const: dp7
+ - const: dp8
+ - const: fatal
+ - const: ready
+ - const: spawn
+ - const: stop-ack
+ memory-region:
+ minItems: 1
+ items:
+ - description: memory region for Q6 firmware
+ - description: memory region for M3 firmware
+ - description: memory region for M3 dump
+ memory-region-names:
+ minItems: 1
+ items:
+ - const: q6-region
+ - const: m3-region
+ - const: m3-dump
+ qcom,smem-states:
+ items:
+ - description: Signal bit used to shutdown Q6 user PD
+ items:
+ - description: Phandle to the Shared Memory Point 2 Point device
+ - description: Single bit index to toggle in the value sent to the remote processor
+ maximum: 32
+ - description: Signal bit used to stop Q6 user PD
+ items:
+ - description: Phandle to the Shared Memory Point 2 Point device
+ - description: Single bit index to toggle in the value sent to the remote processor
+ maximum: 32
+ - description: Signal bit used to spawn Q6 user PD
+ items:
+ - description: Phandle to the Shared Memory Point 2 Point device
+ - description: Single bit index to toggle in the value sent to the remote processor
+ maximum: 32
+ qcom,smem-state-names:
+ items:
+ - const: shutdown
+ - const: stop
+ - const: spawn
+ required:
+ - interrupts-extended
+ - interrupt-names
+ - qcom,smem-states
+ - qcom,smem-state-names
+
- if:
properties:
compatible:
@@ -265,6 +589,19 @@ allOf:
- description: interrupt event for ring DP20
- description: interrupt event for ring DP21
- description: interrupt event for ring DP22
+ qcom,smem-states:
+ items:
+ - description: Signal bits used to enable/disable low power mode
+ on WCN6750 in the case of WoW (Wake on Wireless).
+ 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
+ qcom,smem-state-names:
+ items:
+ - const: wlan-smp2p-out
examples:
- |
@@ -464,3 +801,203 @@ examples:
iommus = <&apps_smmu 0x1c02 0x1>;
};
};
+
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ q6_region: q6-region@4b000000 {
+ no-map;
+ reg = <0x0 0x4b000000 0x0 0x1400000>;
+ };
+
+ m3_dump: m3-dump@4d100000 {
+ reg = <0x0 0x4d100000 0x0 0x100000>;
+ no-map;
+ };
+ };
+
+ wifi@c000000 {
+ compatible = "qcom,ipq5018-wifi";
+ reg = <0x0c000000 0x1000000>;
+
+ interrupts-extended = <&intc GIC_SPI 288 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 289 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 290 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 292 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 293 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 294 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 295 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 296 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 297 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 298 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 299 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 300 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 301 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 302 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 303 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 304 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 305 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 306 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 307 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 308 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 309 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 310 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 311 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 334 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 313 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 314 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 315 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 316 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 317 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 318 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 319 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 320 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 321 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 322 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 323 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 324 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 325 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 326 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 327 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 328 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 329 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 330 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 331 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 332 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 333 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 312 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 335 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 336 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 337 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 338 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 339 IRQ_TYPE_EDGE_RISING>,
+ <&wcss_smp2p_in 8 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 9 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 12 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 11 IRQ_TYPE_NONE>;
+ interrupt-names = "misc-pulse1",
+ "misc-latch",
+ "sw-exception",
+ "watchdog",
+ "ce0",
+ "ce1",
+ "ce2",
+ "ce3",
+ "ce4",
+ "ce5",
+ "ce6",
+ "ce7",
+ "ce8",
+ "ce9",
+ "ce10",
+ "ce11",
+ "host2wbm-desc-feed",
+ "host2reo-re-injection",
+ "host2reo-command",
+ "host2rxdma-monitor-ring3",
+ "host2rxdma-monitor-ring2",
+ "host2rxdma-monitor-ring1",
+ "reo2ost-exception",
+ "wbm2host-rx-release",
+ "reo2host-status",
+ "reo2host-destination-ring4",
+ "reo2host-destination-ring3",
+ "reo2host-destination-ring2",
+ "reo2host-destination-ring1",
+ "rxdma2host-monitor-destination-mac3",
+ "rxdma2host-monitor-destination-mac2",
+ "rxdma2host-monitor-destination-mac1",
+ "ppdu-end-interrupts-mac3",
+ "ppdu-end-interrupts-mac2",
+ "ppdu-end-interrupts-mac1",
+ "rxdma2host-monitor-status-ring-mac3",
+ "rxdma2host-monitor-status-ring-mac2",
+ "rxdma2host-monitor-status-ring-mac1",
+ "host2rxdma-host-buf-ring-mac3",
+ "host2rxdma-host-buf-ring-mac2",
+ "host2rxdma-host-buf-ring-mac1",
+ "rxdma2host-destination-ring-mac3",
+ "rxdma2host-destination-ring-mac2",
+ "rxdma2host-destination-ring-mac1",
+ "host2tcl-input-ring4",
+ "host2tcl-input-ring3",
+ "host2tcl-input-ring2",
+ "host2tcl-input-ring1",
+ "wbm2host-tx-completions-ring3",
+ "wbm2host-tx-completions-ring2",
+ "wbm2host-tx-completions-ring1",
+ "tcl2host-status-ring",
+ "fatal",
+ "ready",
+ "spawn",
+ "stop-ack";
+
+ qcom,smem-states = <&wcss_smp2p_out 8>,
+ <&wcss_smp2p_out 9>,
+ <&wcss_smp2p_out 10>;
+ qcom,smem-state-names = "shutdown",
+ "stop",
+ "spawn";
+
+ memory-region = <&q6_region>, <&q6_region>, <&m3_dump>;
+ memory-region-names = "q6-region", "m3-region", "m3-dump";
+
+ qcom,rproc = <&q6v5_wcss>;
+ };
+
+ wifi1: wifi@b00a040 {
+ reg = <0x0b00a040 0x0>;
+ compatible = "qcom,qcn6122-wifi";
+
+ interrupts-extended = <&intc GIC_SPI 416 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 417 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 418 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 419 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 420 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 421 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 422 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 423 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 424 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 425 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 426 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 427 IRQ_TYPE_EDGE_RISING>,
+ <&intc GIC_SPI 428 IRQ_TYPE_EDGE_RISING>,
+ <&wcss_smp2p_in 16 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 17 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 20 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 19 IRQ_TYPE_NONE>;
+ interrupt-names = "ce1",
+ "ce2",
+ "ce3",
+ "ce4",
+ "ce5",
+ "dp1",
+ "dp2",
+ "dp3",
+ "dp4",
+ "dp5",
+ "dp6",
+ "dp7",
+ "dp8",
+ "fatal",
+ "ready",
+ "spawn",
+ "stop-ack";
+
+ qcom,smem-states = <&wcss_smp2p_out 16>,
+ <&wcss_smp2p_out 17>,
+ <&wcss_smp2p_out 18>;
+ qcom,smem-state-names = "shutdown",
+ "stop",
+ "spawn";
+
+ memory-region = <&q6_region_qcn6122_1>, <&q6_region_ipq5018>, <&m3_dump_qcn6122_1>;
+ memory-region-names = "q6-region", "m3-region", "m3-dump";
+
+ status = "disabled";
+ };
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 3eaa4c9ccf3cc..8e5f5c235cb74 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -1084,6 +1084,49 @@ static bool qcom_scm_is_pas_available(void)
return true;
}
+/**
+ * qcom_scm_pas_set_wifi_power_mode() - Power on/off internal wifi
+ * @peripheral: peripheral id
+ * @enable: true to power up, false to power down
+ *
+ * Return 0 on success.
+ */
+int qcom_scm_pas_set_wifi_power_mode(u32 peripheral, bool enable)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = enable ? QCOM_SCM_PIL_PAS_WIFI_PWR_EN :
+ QCOM_SCM_PIL_PAS_WIFI_PWR_DIS,
+ .arginfo = QCOM_SCM_ARGS(1),
+ .args[0] = peripheral,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ int ret;
+
+ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ enable ? QCOM_SCM_PIL_PAS_WIFI_PWR_EN :
+ QCOM_SCM_PIL_PAS_WIFI_PWR_DIS))
+ return -EOPNOTSUPP;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ goto disable_clk;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
+
+disable_clk:
+ qcom_scm_clk_disable();
+
+ return ret ? : res.result[0];
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_set_wifi_power_mode);
+
static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
{
struct qcom_scm_desc desc = {
diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
index cf90a565fdfbd..e1554997ca08c 100644
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -105,6 +105,8 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+#define QCOM_SCM_PIL_PAS_WIFI_PWR_EN 0x17
+#define QCOM_SCM_PIL_PAS_WIFI_PWR_DIS 0x18
#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21
#define QCOM_SCM_SVC_IO 0x05
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 27d01411b6e9c..51908e779b7c8 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/of_reserved_mem.h>
#include <linux/dma-mapping.h>
+#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/iommu.h>
#include "ahb.h"
#include "debug.h"
@@ -18,9 +19,17 @@
#include "qmi.h"
#include <linux/remoteproc.h>
#include "pcic.h"
+#include <linux/soc/qcom/mdt_loader.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
+#define ATH11K_UPD_IRQ_WRD_LEN 18
+static const char *const ath11k_userpd_irq[ATH11K_USERPD_MAX_IRQ] = {
+ "spawn",
+ "ready",
+ "stop-ack",
+};
+
static const struct of_device_id ath11k_ahb_of_match[] = {
/* TODO: Should we change the compatible string to something similar
* to one that ath10k uses?
@@ -37,6 +46,9 @@ static const struct of_device_id ath11k_ahb_of_match[] = {
{ .compatible = "qcom,ipq5018-wifi",
.data = (void *)ATH11K_HW_IPQ5018_HW10,
},
+ { .compatible = "qcom,qcn6122-wifi",
+ .data = (void *)ATH11K_HW_QCN6122_HW10,
+ },
{ }
};
@@ -44,6 +56,22 @@ MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match);
#define ATH11K_IRQ_CE0_OFFSET 4
+/*
+ * Multi-UserPD Architecture:
+ *
+ * One Q6 RootPD (managed by separate rproc driver) supports multiple
+ * ath12k UserPDs. Each UserPD represents a WiFi radio instance.
+ *
+ * Lifecycle:
+ * - RootPD boots when first UserPD probes
+ * - All UserPDs share RootPD's SSR notifier
+ *
+ * Global Driver-level Locking:
+ * - ath11k_rproc_info_lock: Protects rproc_info allocation/free
+ */
+static struct ath11k_ahb_rproc_info *g_rproc_info;
+static DEFINE_MUTEX(ath11k_rproc_info_lock);
+
static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {
"misc-pulse1",
"misc-latch",
@@ -406,23 +434,92 @@ static void ath11k_ahb_stop(struct ath11k_base *ab)
ath11k_ce_cleanup_pipes(ab);
}
+static int ath11k_ahb_boot_user_pd(struct ath11k_base *ab)
+{
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+ unsigned long time_left;
+ int ret;
+
+ if (ab->hw_rev == ATH11K_HW_IPQ5018_HW10) {
+ ret = qcom_scm_pas_set_wifi_power_mode(MPD_WCNSS_PAS_ID, true);
+ if (ret) {
+ ath11k_err(ab, "failed to power up wifi: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit),
+ BIT(ab_ahb->spawn_bit));
+ if (ret) {
+ ath11k_err(ab, "Failed to update spawn state %d\n", ret);
+ return ret;
+ }
+
+ time_left = wait_for_completion_timeout(&ab_ahb->userpd_spawned,
+ ATH11K_USERPD_SPAWN_TIMEOUT);
+ if (!time_left) {
+ ath11k_err(ab, "UserPD spawn wait timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ time_left = wait_for_completion_timeout(&ab_ahb->userpd_ready,
+ ATH11K_USERPD_READY_TIMEOUT);
+ if (!time_left) {
+ ath11k_err(ab, "UserPD ready wait timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit), 0);
+
+ ath11k_dbg(ab, ATH11K_DBG_AHB, "UserPD%d is now UP\n", ab_ahb->userpd_id);
+
+ return 0;
+}
+
static int ath11k_ahb_power_up(struct ath11k_base *ab)
{
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
int ret;
- ret = rproc_boot(ab_ahb->tgt_rproc);
- if (ret)
- ath11k_err(ab, "failed to boot the remote processor Q6\n");
+ if (ab_ahb->userpd_id > 0 &&
+ ab_ahb->userpd_id < ATH11K_AHB_USERPD_ID_MAX) {
+ ret = ath11k_ahb_boot_user_pd(ab);
+ if (ret) {
+ ath11k_err(ab, "failed to boot userPD%d: %d\n",
+ ab_ahb->userpd_id, ret);
+ return ret;
+ }
+ }
- return ret;
+ return 0;
+}
+
+static void ath11k_ahb_stop_user_pd(struct ath11k_base *ab)
+{
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+ unsigned long time_left;
+
+ qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit),
+ BIT(ab_ahb->stop_bit));
+
+ time_left = wait_for_completion_timeout(&ab_ahb->userpd_stopped,
+ ATH11K_USERPD_STOP_TIMEOUT);
+ if (!time_left)
+ ath11k_warn(ab, "UserPD stop wait timed out\n");
+
+ qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), 0);
+
+ if (ab->hw_rev == ATH11K_HW_IPQ5018_HW10)
+ qcom_scm_pas_set_wifi_power_mode(MPD_WCNSS_PAS_ID, false);
}
static void ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend)
{
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
- rproc_shutdown(ab_ahb->tgt_rproc);
+ if (ab_ahb->userpd_id > 0 &&
+ ab_ahb->userpd_id < ATH11K_AHB_USERPD_ID_MAX)
+ ath11k_ahb_stop_user_pd(ab);
}
static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab)
@@ -798,6 +895,18 @@ static int ath11k_ahb_hif_resume(struct ath11k_base *ab)
return 0;
}
+static void ath11k_ahb_config_static_window_qcn6122(struct ath11k_base *ab)
+{
+ u32 umac_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET);
+ u32 ce_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE);
+ u32 window;
+
+ window = (umac_window) | (ce_window << 6);
+
+ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window,
+ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
+}
+
static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = {
.start = ath11k_ahb_start,
.stop = ath11k_ahb_stop,
@@ -813,6 +922,24 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = {
.ce_irq_disable = ath11k_ahb_ce_irq_disable_sync,
};
+static const struct ath11k_hif_ops ath11k_ahb_hif_ops_qcn6122 = {
+ .start = ath11k_pcic_start,
+ .stop = ath11k_pcic_stop,
+ .read32 = ath11k_pcic_read32,
+ .write32 = ath11k_pcic_write32,
+ .read = NULL,
+ .irq_enable = ath11k_pcic_ext_irq_enable,
+ .irq_disable = ath11k_pcic_ext_irq_disable,
+ .get_msi_address = ath11k_pcic_get_msi_address,
+ .get_user_msi_vector = ath11k_pcic_get_user_msi_assignment,
+ .map_service_to_pipe = ath11k_pcic_map_service_to_pipe,
+ .power_down = ath11k_ahb_power_down,
+ .power_up = ath11k_ahb_power_up,
+ .ce_irq_enable = ath11k_pci_enable_ce_irqs_except_wake_irq,
+ .ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq,
+ .config_static_window = ath11k_ahb_config_static_window_qcn6122,
+};
+
static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = {
.start = ath11k_pcic_start,
.stop = ath11k_pcic_stop,
@@ -832,24 +959,411 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = {
.ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq,
};
+static int ath11k_ahb_init_userpd(struct ath11k_base *ab, int userpd_id)
+{
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+
+ lockdep_assert_held(&ath11k_rproc_info_lock);
+
+ switch (ab->hw_rev) {
+ case ATH11K_HW_IPQ5018_HW10:
+ if (userpd_id != ATH11K_AHB_USERPD_ID_1)
+ return -EINVAL;
+
+ ab_ahb->userpd_id = userpd_id;
+ break;
+ case ATH11K_HW_QCN6122_HW10:
+ if (userpd_id != ATH11K_AHB_USERPD_ID_2 &&
+ userpd_id != ATH11K_AHB_USERPD_ID_3)
+ return -EINVAL;
+
+ ab_ahb->userpd_id = userpd_id;
+ ab->qmi.service_ins_id += (ab_ahb->userpd_id - 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ g_rproc_info->userpd[userpd_id - 1] = ab_ahb;
+ g_rproc_info->num_userpd++;
+
+ return 0;
+}
+
+static irqreturn_t ath11k_userpd_irq_handler(int irq, void *data)
+{
+ struct ath11k_base *ab = data;
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+
+ if (irq == ab_ahb->userpd_irq_num[ATH11K_USERPD_SPAWN_IRQ]) {
+ complete(&ab_ahb->userpd_spawned);
+ } else if (irq == ab_ahb->userpd_irq_num[ATH11K_USERPD_READY_IRQ]) {
+ complete(&ab_ahb->userpd_ready);
+ } else if (irq == ab_ahb->userpd_irq_num[ATH11K_USERPD_STOP_ACK_IRQ]) {
+ complete(&ab_ahb->userpd_stopped);
+ } else {
+ ath11k_err(ab, "Invalid userpd interrupt\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void ath11k_ahb_cleanup_userpd(struct ath11k_base *ab)
+{
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+ struct ath11k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info;
+
+ lockdep_assert_held(&ath11k_rproc_info_lock);
+
+ if (!rproc_info)
+ return;
+
+ if (rproc_info && ab_ahb->userpd_id > 0 &&
+ ab_ahb->userpd_id < ATH11K_AHB_USERPD_ID_MAX) {
+ rproc_info->userpd[ab_ahb->userpd_id - 1] = NULL;
+ rproc_info->num_userpd--;
+ ab_ahb->rproc_info = NULL;
+ }
+}
+
+static int ath11k_ahb_config_userpd_irq(struct ath11k_base *ab)
+{
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+ char *upd_irq_name;
+ int userpd_id;
+ int i, ret;
+
+ ab_ahb->spawn_state = devm_qcom_smem_state_get(&ab->pdev->dev, "spawn",
+ &ab_ahb->spawn_bit);
+ if (IS_ERR(ab_ahb->spawn_state))
+ return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->spawn_state),
+ "Failed to acquire spawn state\n");
+
+ ab_ahb->stop_state = devm_qcom_smem_state_get(&ab->pdev->dev, "stop",
+ &ab_ahb->stop_bit);
+ if (IS_ERR(ab_ahb->stop_state))
+ return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->stop_state),
+ "Failed to acquire stop state\n");
+
+ mutex_lock(&ath11k_rproc_info_lock);
+
+ userpd_id = ab_ahb->spawn_bit / 8;
+ ret = ath11k_ahb_init_userpd(ab, userpd_id);
+ if (ret) {
+ mutex_unlock(&ath11k_rproc_info_lock);
+ return ret;
+ }
+
+ mutex_unlock(&ath11k_rproc_info_lock);
+
+ for (i = 0; i < ATH11K_USERPD_MAX_IRQ; i++) {
+ ab_ahb->userpd_irq_num[i] = platform_get_irq_byname_optional(ab->pdev,
+ ath11k_userpd_irq[i]);
+ if (ab_ahb->userpd_irq_num[i] < 0)
+ return ab_ahb->userpd_irq_num[i];
+
+ upd_irq_name = devm_kzalloc(&ab->pdev->dev, ATH11K_UPD_IRQ_WRD_LEN,
+ GFP_KERNEL);
+ if (!upd_irq_name)
+ return -ENOMEM;
+
+ scnprintf(upd_irq_name, ATH11K_UPD_IRQ_WRD_LEN, "UserPD%u-%s",
+ ab_ahb->userpd_id, ath11k_userpd_irq[i]);
+ ret = devm_request_threaded_irq(&ab->pdev->dev, ab_ahb->userpd_irq_num[i],
+ NULL, ath11k_userpd_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ upd_irq_name, ab);
+ if (ret)
+ return dev_err_probe(&ab->pdev->dev, ret,
+ "Request %s irq failed: %d\n",
+ ath11k_userpd_irq[i], ret);
+ }
+
+ init_completion(&ab_ahb->userpd_spawned);
+ init_completion(&ab_ahb->userpd_ready);
+ init_completion(&ab_ahb->userpd_stopped);
+
+ return 0;
+}
+
+static int ath11k_ahb_root_pd_state_notifier(struct notifier_block *nb,
+ const unsigned long event, void *data)
+{
+ struct ath11k_ahb_rproc_info *rproc_info =
+ container_of(nb, struct ath11k_ahb_rproc_info, root_pd_nb);
+
+ if (event == ATH11K_RPROC_AFTER_POWERUP) {
+ ath11k_dbg(NULL, ATH11K_DBG_AHB, "Root PD is UP\n");
+ complete(&rproc_info->rootpd_ready);
+ }
+
+ return 0;
+}
+
+static int ath11k_ahb_register_rproc_notifier(void)
+{
+ int ret;
+
+ lockdep_assert_held(&ath11k_rproc_info_lock);
+
+ if (g_rproc_info->root_pd_notifier)
+ return 0;
+
+ g_rproc_info->root_pd_nb.notifier_call = ath11k_ahb_root_pd_state_notifier;
+
+ g_rproc_info->root_pd_notifier = qcom_register_ssr_notifier(g_rproc_info->tgt_rproc->name,
+ &g_rproc_info->root_pd_nb);
+ if (IS_ERR(g_rproc_info->root_pd_notifier)) {
+ ret = PTR_ERR(g_rproc_info->root_pd_notifier);
+ g_rproc_info->root_pd_notifier = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath11k_ahb_unregister_rproc_notifier(void)
+{
+ lockdep_assert_held(&ath11k_rproc_info_lock);
+
+ if (!g_rproc_info->root_pd_notifier)
+ return;
+
+ qcom_unregister_ssr_notifier(g_rproc_info->root_pd_notifier,
+ &g_rproc_info->root_pd_nb);
+ g_rproc_info->root_pd_notifier = NULL;
+}
+
+static struct ath11k_ahb_rproc_info *ath11k_ahb_rproc_info_alloc(struct ath11k_base *ab)
+{
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+ struct ath11k_ahb_rproc_info *rproc_info;
+
+ lockdep_assert_held(&ath11k_rproc_info_lock);
+
+ rproc_info = kzalloc_obj(*rproc_info);
+ if (!rproc_info)
+ return NULL;
+
+ rproc_info->root_pd_booted = false;
+ rproc_info->m3_loaded = 0;
+ init_completion(&rproc_info->rootpd_ready);
+ ab_ahb->rproc_info = rproc_info;
+
+ return rproc_info;
+}
+
static int ath11k_core_get_rproc(struct ath11k_base *ab)
{
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
struct device *dev = ab->dev;
struct rproc *prproc;
phandle rproc_phandle;
+ int ret;
+
+ lockdep_assert_held(&ath11k_rproc_info_lock);
+
+ if (g_rproc_info) {
+ ab_ahb->rproc_info = g_rproc_info;
+ return 0;
+ }
+
+ g_rproc_info = ath11k_ahb_rproc_info_alloc(ab);
+ if (!g_rproc_info)
+ return -ENOMEM;
if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) {
ath11k_err(ab, "failed to get q6_rproc handle\n");
- return -ENOENT;
+ ret = -ENOENT;
+ goto err_free_rproc_info;
}
prproc = rproc_get_by_phandle(rproc_phandle);
- if (!prproc)
- return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER, "failed to get rproc\n");
- ab_ahb->tgt_rproc = prproc;
+ if (!prproc) {
+ ret = dev_err_probe(dev, -EPROBE_DEFER,
+ "failed to get rproc\n");
+ goto err_free_rproc_info;
+ }
+ g_rproc_info->tgt_rproc = prproc;
+
+ return 0;
+
+err_free_rproc_info:
+ ab_ahb->rproc_info = NULL;
+ kfree(g_rproc_info);
+ g_rproc_info = NULL;
+ return ret;
+}
+
+static int ath11k_ahb_boot_root_pd(struct ath11k_base *ab)
+{
+ unsigned long time_left;
+ int ret;
+
+ lockdep_assert_held(&ath11k_rproc_info_lock);
+ reinit_completion(&g_rproc_info->rootpd_ready);
+
+ ret = rproc_boot(g_rproc_info->tgt_rproc);
+ if (ret < 0) {
+ ath11k_err(ab, "RootPD boot failed\n");
+ return ret;
+ }
+
+ time_left = wait_for_completion_timeout(&g_rproc_info->rootpd_ready,
+ ATH11K_ROOTPD_READY_TIMEOUT);
+ if (!time_left) {
+ ath11k_err(ab, "RootPD ready wait timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ath11k_ahb_load_m3_firmware(struct ath11k_base *ab)
+{
+ char m3_fw_name[ATH11K_FW_NAME_LEN];
+ const struct firmware *m3_fw = NULL;
+ struct device_node *node;
+ struct resource res = {};
+ phys_addr_t mem_phys;
+ void *mem_region;
+ size_t mem_size;
+ int ret;
+
+ ret = of_reserved_mem_region_to_resource_byname(ab->dev->of_node,
+ "m3-region", &res);
+ /* If m3 region isn't found, fall back to the mem region of the rproc */
+ if (ret && ret == -EINVAL) {
+ node = g_rproc_info->tgt_rproc->dev.parent->of_node;
+ ret = of_reserved_mem_region_to_resource(node, 0, &res);
+ }
+ if (ret) {
+ ath11k_err(ab, "Failed to get m3 memory resource: %d\n", ret);
+ return ret;
+ }
+
+ mem_phys = res.start;
+ mem_size = resource_size(&res);
+ mem_region = devm_memremap(ab->dev, mem_phys, mem_size, MEMREMAP_WC);
+
+ snprintf(m3_fw_name, sizeof(m3_fw_name), "%s/%s/%s",
+ ATH11K_FW_DIR, ab->hw_params.fw.dir, ATH11K_M3_MBN_FILE);
+
+ ret = request_firmware(&m3_fw, m3_fw_name, ab->dev);
+ if (ret < 0) {
+ ath11k_err(ab, "Failed to request firmware: %d\n", ret);
+ return ret;
+ }
+
+ if (!m3_fw->size || m3_fw->size > mem_size) {
+ ath11k_err(ab, "Invalid firmware size\n");
+ release_firmware(m3_fw);
+ return -EINVAL;
+ }
+
+ ret = qcom_mdt_load_no_init(ab->dev, m3_fw, m3_fw_name, mem_region,
+ mem_phys, mem_size, &mem_phys);
+ if (ret)
+ ath11k_err(ab, "Failed to load MDT segments: %d\n", ret);
+
+ release_firmware(m3_fw);
+
+ return ret;
+}
+
+static int ath11k_ahb_configure_rproc(struct ath11k_base *ab)
+{
+ int ret;
+
+ mutex_lock(&ath11k_rproc_info_lock);
+
+ ret = ath11k_core_get_rproc(ab);
+ if (ret < 0) {
+ mutex_unlock(&ath11k_rproc_info_lock);
+ return ret;
+ }
+
+ ret = ath11k_ahb_register_rproc_notifier();
+ if (ret < 0) {
+ ret = dev_err_probe(&ab->pdev->dev, ret,
+ "failed to register rproc notifier\n");
+ goto err_cleanup_userpd;
+ }
+
+ if (ab->hw_params.m3_fw_support &&
+ ab->hw_params.fw.m3_loader == ath11k_m3_fw_loader_ahb &&
+ !(g_rproc_info->m3_loaded & BIT(ab->hw_params.hw_rev))) {
+ ret = ath11k_ahb_load_m3_firmware(ab);
+ if (ret) {
+ ath11k_err(ab, "failed to load m3 firmware: %d\n", ret);
+ goto err_unreg_notifier;
+ }
+ g_rproc_info->m3_loaded |= BIT(ab->hw_params.hw_rev);
+ }
+
+ if (g_rproc_info->tgt_rproc->state != RPROC_RUNNING) {
+ ret = ath11k_ahb_boot_root_pd(ab);
+ if (ret) {
+ ath11k_err(ab, "failed to boot the remote processor Q6\n");
+ goto err_unreg_notifier;
+ }
+ }
+ g_rproc_info->root_pd_booted = true;
+
+ mutex_unlock(&ath11k_rproc_info_lock);
+
+ /*
+ * UserPD interrupts are specific to multi-PD configs/firmware only.
+ * If interrupts aren't found, continue execution for non-MPD platforms.
+ */
+ ret = ath11k_ahb_config_userpd_irq(ab);
+ if (ret && ret != -EINVAL && ret != -ENXIO)
+ return dev_err_probe(&ab->pdev->dev, ret,
+ "failed to configure userpd interrupts\n");
return 0;
+
+err_unreg_notifier:
+ ath11k_ahb_unregister_rproc_notifier();
+
+err_cleanup_userpd:
+ ath11k_ahb_cleanup_userpd(ab);
+ if (g_rproc_info && !g_rproc_info->num_userpd) {
+ rproc_put(g_rproc_info->tgt_rproc);
+ kfree(g_rproc_info);
+ g_rproc_info = NULL;
+ }
+
+ mutex_unlock(&ath11k_rproc_info_lock);
+ return ret;
+}
+
+static void ath11k_ahb_deconfigure_rproc(struct ath11k_base *ab)
+{
+ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
+ struct ath11k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info;
+
+ if (!rproc_info || !g_rproc_info)
+ return;
+
+ mutex_lock(&ath11k_rproc_info_lock);
+
+ ath11k_ahb_cleanup_userpd(ab);
+
+ if (!g_rproc_info->num_userpd) {
+ ath11k_ahb_unregister_rproc_notifier();
+
+ if (g_rproc_info->root_pd_booted &&
+ g_rproc_info->tgt_rproc->state == RPROC_RUNNING)
+ rproc_shutdown(g_rproc_info->tgt_rproc);
+
+ rproc_put(g_rproc_info->tgt_rproc);
+ kfree(g_rproc_info);
+ g_rproc_info = NULL;
+ }
+
+ mutex_unlock(&ath11k_rproc_info_lock);
}
static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab)
@@ -884,6 +1398,10 @@ static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab)
ab->pci.msi.addr_hi = upper_32_bits(msi_addr_iova);
ret = of_property_read_u32_index(ab->dev->of_node, "interrupts", 1, &int_prop);
+ if (ret == -EINVAL)
+ ret = of_property_read_u32_index(ab->dev->of_node,
+ "interrupts-extended", 2,
+ &int_prop);
if (ret)
return ret;
@@ -1148,6 +1666,7 @@ static int ath11k_ahb_probe(struct platform_device *pdev)
struct ath11k_base *ab;
const struct ath11k_hif_ops *hif_ops;
const struct ath11k_pci_ops *pci_ops;
+ struct ath11k_ahb *ab_ahb;
enum ath11k_hw_rev hw_rev;
int ret;
@@ -1164,6 +1683,10 @@ static int ath11k_ahb_probe(struct platform_device *pdev)
hif_ops = &ath11k_ahb_hif_ops_wcn6750;
pci_ops = &ath11k_ahb_pci_ops_wcn6750;
break;
+ case ATH11K_HW_QCN6122_HW10:
+ hif_ops = &ath11k_ahb_hif_ops_qcn6122;
+ pci_ops = &ath11k_ahb_pci_ops_wcn6750;
+ break;
default:
dev_err(&pdev->dev, "unsupported device type %d\n", hw_rev);
return -EOPNOTSUPP;
@@ -1187,6 +1710,9 @@ static int ath11k_ahb_probe(struct platform_device *pdev)
ab->hw_rev = hw_rev;
ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
platform_set_drvdata(pdev, ab);
+ ab_ahb = ath11k_ahb_priv(ab);
+ ab_ahb->ab = ab;
+ ab_ahb->userpd_id = 0;
ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
if (ret) {
@@ -1226,26 +1752,29 @@ static int ath11k_ahb_probe(struct platform_device *pdev)
ath11k_ahb_init_qmi_ce_config(ab);
- ret = ath11k_core_get_rproc(ab);
+ ret = ath11k_ahb_configure_rproc(ab);
if (ret)
goto err_ce_free;
ret = ath11k_core_init(ab);
if (ret) {
ath11k_err(ab, "failed to init core: %d\n", ret);
- goto err_ce_free;
+ goto err_rproc_deconfigure;
}
ret = ath11k_ahb_config_irq(ab);
if (ret) {
ath11k_err(ab, "failed to configure irq: %d\n", ret);
- goto err_ce_free;
+ goto err_rproc_deconfigure;
}
ath11k_qmi_fwreset_from_cold_boot(ab);
return 0;
+err_rproc_deconfigure:
+ ath11k_ahb_deconfigure_rproc(ab);
+
err_ce_free:
ath11k_ce_free_pipes(ab);
@@ -1294,7 +1823,7 @@ static void ath11k_ahb_free_resources(struct ath11k_base *ab)
ath11k_ahb_fw_resource_deinit(ab);
ath11k_ce_free_pipes(ab);
ath11k_ahb_ce_unmap(ab);
-
+ ath11k_ahb_deconfigure_rproc(ab);
ath11k_core_free(ab);
platform_set_drvdata(pdev, NULL);
}
diff --git a/drivers/net/wireless/ath/ath11k/ahb.h b/drivers/net/wireless/ath/ath11k/ahb.h
index 8c1eb1e8e6b13..ec89e0e89ca00 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.h
+++ b/drivers/net/wireless/ath/ath11k/ahb.h
@@ -6,6 +6,8 @@
#ifndef ATH11K_AHB_H
#define ATH11K_AHB_H
+#include <linux/remoteproc/qcom_rproc.h>
+
#include "core.h"
#define ATH11K_AHB_RECOVERY_TIMEOUT (3 * HZ)
@@ -14,6 +16,23 @@
#define ATH11K_AHB_SMP2P_SMEM_SEQ_NO GENMASK(31, 16)
#define ATH11K_AHB_SMP2P_SMEM_VALUE_MASK 0xFFFFFFFF
+#define ATH11K_ROOTPD_READY_TIMEOUT (5 * HZ)
+#define ATH11K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP
+
+#define ATH11K_USERPD_SPAWN_TIMEOUT (5 * HZ)
+#define ATH11K_USERPD_READY_TIMEOUT (10 * HZ)
+#define ATH11K_USERPD_STOP_TIMEOUT (5 * HZ)
+#define ATH11K_USERPD_FW_NAME_LEN 35
+
+#define MPD_WCNSS_PAS_ID 0xD
+
+enum ath11k_ahb_userpd_id {
+ ATH11K_AHB_USERPD_ID_1 = 1,
+ ATH11K_AHB_USERPD_ID_2,
+ ATH11K_AHB_USERPD_ID_3,
+ ATH11K_AHB_USERPD_ID_MAX,
+};
+
enum ath11k_ahb_smp2p_msg_id {
ATH11K_AHB_POWER_SAVE_ENTER = 1,
ATH11K_AHB_POWER_SAVE_EXIT,
@@ -21,8 +40,30 @@ enum ath11k_ahb_smp2p_msg_id {
struct ath11k_base;
-struct ath11k_ahb {
+struct ath11k_ahb_rproc_info {
struct rproc *tgt_rproc;
+
+ struct completion rootpd_ready;
+ struct notifier_block root_pd_nb;
+ void *root_pd_notifier;
+ bool root_pd_booted;
+
+ u8 num_userpd;
+ struct ath11k_ahb *userpd[ATH11K_AHB_USERPD_ID_MAX];
+
+ /* Bitmap of loaded M3 firmwares indexed by hardware revision */
+ u32 m3_loaded;
+};
+
+enum ath11k_ahb_userpd_irq {
+ ATH11K_USERPD_SPAWN_IRQ,
+ ATH11K_USERPD_READY_IRQ,
+ ATH11K_USERPD_STOP_ACK_IRQ,
+ ATH11K_USERPD_MAX_IRQ,
+};
+
+struct ath11k_ahb {
+ struct ath11k_base *ab;
struct {
struct device *dev;
struct iommu_domain *iommu_domain;
@@ -37,6 +78,17 @@ struct ath11k_ahb {
unsigned int smem_bit;
struct qcom_smem_state *smem_state;
} smp2p_info;
+ struct ath11k_ahb_rproc_info *rproc_info;
+
+ struct qcom_smem_state *spawn_state;
+ struct qcom_smem_state *stop_state;
+ struct completion userpd_spawned;
+ struct completion userpd_ready;
+ struct completion userpd_stopped;
+ u32 userpd_id;
+ u32 spawn_bit;
+ u32 stop_bit;
+ int userpd_irq_num[ATH11K_USERPD_MAX_IRQ];
};
static inline struct ath11k_ahb *ath11k_ahb_priv(struct ath11k_base *ab)
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index d2ed6a0ea7e36..8fa1171b6bdd6 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -46,6 +46,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "IPQ8074/hw2.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_ahb,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
@@ -135,6 +136,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "IPQ6018/hw1.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_ahb,
},
.max_radios = 2,
.bdf_addr = 0x4ABC0000,
@@ -222,6 +224,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "QCA6390/hw2.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_qmi,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
@@ -314,6 +317,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "QCN9074/hw1.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_qmi,
},
.max_radios = 1,
.single_pdev_only = false,
@@ -400,6 +404,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "WCN6855/hw2.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_qmi,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
@@ -492,6 +497,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "WCN6855/hw2.1",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_qmi,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
@@ -582,6 +588,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "WCN6750/hw1.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_qmi,
},
.max_radios = 1,
.bdf_addr = 0x4B0C0000,
@@ -667,6 +674,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "IPQ5018/hw1.0",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_ahb,
},
.max_radios = MAX_RADIOS_5018,
.bdf_addr = 0x4BA00000,
@@ -724,7 +732,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dbr_debug_support = true,
.global_reset = false,
.bios_sar_capa = NULL,
- .m3_fw_support = false,
+ .m3_fw_support = true,
.fixed_bdf_addr = true,
.fixed_mem_region = true,
.static_window_map = false,
@@ -752,6 +760,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "QCA2066/hw2.1",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_qmi,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
@@ -843,6 +852,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.dir = "QCA6698AQ/hw2.1",
.board_size = 256 * 1024,
.cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_qmi,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
@@ -926,6 +936,93 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.cfr_num_stream_bufs = 255,
.cfr_stream_buf_size = 8200,
},
+ {
+ .hw_rev = ATH11K_HW_QCN6122_HW10,
+ .name = "qcn6122 hw1.0",
+ .fw = {
+ .dir = "QCN6122/hw1.0",
+ .board_size = 256 * 1024,
+ .cal_offset = 128 * 1024,
+ .m3_loader = ath11k_m3_fw_loader_ahb,
+ },
+ .max_radios = MAX_RADIOS_5018,
+ .bdf_addr = 0x4D200000,
+ .hw_ops = &ipq5018_ops,
+ .ring_mask = &ath11k_hw_ring_mask_qcn6122,
+ .internal_sleep_clock = false,
+ .regs = &qcn6122_regs,
+ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122,
+ .host_ce_config = ath11k_host_ce_config_qcn9074,
+ .ce_count = CE_CNT_5018,
+ .target_ce_config = ath11k_target_ce_config_wlan_ipq5018,
+ .target_ce_count = TARGET_CE_CNT_5018,
+ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq5018,
+ .svc_to_ce_map_len = SVC_CE_MAP_LEN_5018,
+ .single_pdev_only = false,
+ .rxdma1_enable = true,
+ .num_rxdma_per_pdev = RXDMA_PER_PDEV_5018,
+ .rx_mac_buf_ring = false,
+ .vdev_start_delay = false,
+ .htt_peer_map_v2 = true,
+
+ .spectral = {
+ .fft_sz = 2,
+ .fft_pad_sz = 0,
+ .summary_pad_sz = 16,
+ .fft_hdr_len = 24,
+ .max_fft_bins = 1024,
+ .fragment_160mhz = false,
+ },
+
+ .interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT),
+ .supports_monitor = true,
+ .full_monitor_mode = false,
+ .supports_shadow_regs = false,
+ .idle_ps = false,
+ .supports_sta_ps = false,
+ .coldboot_cal_mm = false,
+ .coldboot_cal_ftm = false,
+ .cbcal_restart_fw = true,
+ .fw_mem_mode = 1,
+ .num_vdevs = 16 + 1,
+ .num_peers = 512,
+ .supports_suspend = false,
+ .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
+ .supports_regdb = false,
+ .fix_l1ss = true,
+ .credit_flow = false,
+ .hal_params = &ath11k_hw_hal_params_ipq5018,
+ .supports_dynamic_smps_6ghz = false,
+ .alloc_cacheable_memory = false,
+ .supports_rssi_stats = false,
+ .fw_wmi_diag_event = false,
+ .current_cc_support = false,
+ .dbr_debug_support = true,
+ .global_reset = false,
+ .bios_sar_capa = NULL,
+ .m3_fw_support = true,
+ .fixed_bdf_addr = true,
+ .fixed_mem_region = true,
+ .static_window_map = true,
+ .hybrid_bus_type = true,
+ .fixed_fw_mem = false,
+ .support_off_channel_tx = false,
+ .supports_multi_bssid = false,
+
+ .sram_dump = {},
+
+ .tcl_ring_retry = true,
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
+ .smp2p_wow_exit = false,
+ .support_fw_mac_sequence = false,
+ .support_dual_stations = false,
+ .pdev_suspend = false,
+ .cfr_support = false,
+ .cfr_num_stream_bufs = 0,
+ .cfr_stream_buf_size = 0,
+ },
};
static const struct dmi_system_id ath11k_pm_quirk_table[] = {
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index a0d725923ef24..e92ec95dd3842 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -153,6 +153,7 @@ enum ath11k_hw_rev {
ATH11K_HW_IPQ5018_HW10,
ATH11K_HW_QCA2066_HW21,
ATH11K_HW_QCA6698AQ_HW21,
+ ATH11K_HW_QCN6122_HW10,
};
enum ath11k_firmware_mode {
diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h
index 017fed1b4bd15..2e1a4fedf1391 100644
--- a/drivers/net/wireless/ath/ath11k/hif.h
+++ b/drivers/net/wireless/ath/ath11k/hif.h
@@ -32,6 +32,7 @@ struct ath11k_hif_ops {
void (*ce_irq_disable)(struct ath11k_base *ab);
void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
void (*coredump_download)(struct ath11k_base *ab);
+ void (*config_static_window)(struct ath11k_base *ab);
};
static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab)
@@ -159,4 +160,12 @@ static inline void ath11k_hif_coredump_download(struct ath11k_base *ab)
ab->hif.ops->coredump_download(ab);
}
+static inline void ath11k_hif_config_static_window(struct ath11k_base *ab)
+{
+ if (!ab->hw_params.static_window_map || !ab->hif.ops->config_static_window)
+ return;
+
+ ab->hif.ops->config_static_window(ab);
+}
+
#endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c
index 93f9a03c48dc0..bb375efd50fed 100644
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -2070,6 +2070,43 @@ const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750 = {
},
};
+const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122 = {
+ .tx = {
+ ATH11K_TX_RING_MASK_0,
+ ATH11K_TX_RING_MASK_1,
+ ATH11K_TX_RING_MASK_2,
+ },
+ .rx_mon_status = {
+ 0, 0, 0,
+ ATH11K_RX_MON_STATUS_RING_MASK_0,
+ },
+ .rx = {
+ 0, 0, 0, 0,
+ ATH11K_RX_RING_MASK_0,
+ ATH11K_RX_RING_MASK_1,
+ ATH11K_RX_RING_MASK_2,
+ ATH11K_RX_RING_MASK_3,
+ },
+ .rx_err = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ ATH11K_RX_ERR_RING_MASK_0,
+ },
+ .rx_wbm_rel = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ATH11K_RX_WBM_REL_RING_MASK_0,
+ },
+ .reo_status = {
+ 0, 0, 0,
+ ATH11K_REO_STATUS_RING_MASK_0,
+ },
+ .rxdma2host = {
+ ATH11K_RXDMA2HOST_RING_MASK_0,
+ },
+ .host2rxdma = {
+ ATH11K_HOST2RXDMA_RING_MASK_0,
+ },
+};
+
/* Target firmware's Copy Engine configuration for IPQ5018 */
const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq5018[] = {
/* CE0: host->target HTC control and raw streams */
@@ -2836,6 +2873,81 @@ const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq5018 = {
.num_tx_rings = ARRAY_SIZE(ath11k_hw_tcl2wbm_rbm_map_ipq5018),
};
+const struct ath11k_hw_regs qcn6122_regs = {
+ /* SW2TCL(x) R0 ring configuration address */
+ .hal_tcl1_ring_base_lsb = 0x00000694,
+ .hal_tcl1_ring_base_msb = 0x00000698,
+ .hal_tcl1_ring_id = 0x0000069c,
+ .hal_tcl1_ring_misc = 0x000006a4,
+ .hal_tcl1_ring_tp_addr_lsb = 0x000006b0,
+ .hal_tcl1_ring_tp_addr_msb = 0x000006b4,
+ .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c4,
+ .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c8,
+ .hal_tcl1_ring_msi1_base_lsb = 0x000006dc,
+ .hal_tcl1_ring_msi1_base_msb = 0x000006e0,
+ .hal_tcl1_ring_msi1_data = 0x000006e4,
+ .hal_tcl2_ring_base_lsb = 0x000006ec,
+ .hal_tcl_ring_base_lsb = 0x0000079c,
+
+ /* TCL STATUS ring address */
+ .hal_tcl_status_ring_base_lsb = 0x000008a4,
+
+ /* REO2SW(x) R0 ring configuration address */
+ .hal_reo1_ring_base_lsb = 0x000001ec,
+ .hal_reo1_ring_base_msb = 0x000001f0,
+ .hal_reo1_ring_id = 0x000001f4,
+ .hal_reo1_ring_misc = 0x000001fc,
+ .hal_reo1_ring_hp_addr_lsb = 0x00000200,
+ .hal_reo1_ring_hp_addr_msb = 0x00000204,
+ .hal_reo1_ring_producer_int_setup = 0x00000210,
+ .hal_reo1_ring_msi1_base_lsb = 0x00000234,
+ .hal_reo1_ring_msi1_base_msb = 0x00000238,
+ .hal_reo1_ring_msi1_data = 0x0000023c,
+ .hal_reo2_ring_base_lsb = 0x00000244,
+ .hal_reo1_aging_thresh_ix_0 = 0x00000564,
+ .hal_reo1_aging_thresh_ix_1 = 0x00000568,
+ .hal_reo1_aging_thresh_ix_2 = 0x0000056c,
+ .hal_reo1_aging_thresh_ix_3 = 0x00000570,
+
+ /* REO2SW(x) R2 ring pointers (head/tail) address */
+ .hal_reo1_ring_hp = 0x00003028,
+ .hal_reo1_ring_tp = 0x0000302c,
+ .hal_reo2_ring_hp = 0x00003030,
+
+ /* REO2TCL R0 ring configuration address */
+ .hal_reo_tcl_ring_base_lsb = 0x000003fc,
+ .hal_reo_tcl_ring_hp = 0x00003058,
+
+ /* SW2REO ring address */
+ .hal_sw2reo_ring_base_lsb = 0x0000013c,
+ .hal_sw2reo_ring_hp = 0x00003018,
+
+ /* REO CMD ring address */
+ .hal_reo_cmd_ring_base_lsb = 0x000000e4,
+ .hal_reo_cmd_ring_hp = 0x00003010,
+
+ /* REO status address */
+ .hal_reo_status_ring_base_lsb = 0x00000504,
+ .hal_reo_status_hp = 0x00003070,
+
+ /* WCSS relative address */
+ .hal_seq_wcss_umac_ce0_src_reg = 0x01b80000,
+ .hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000,
+ .hal_seq_wcss_umac_ce1_src_reg = 0x01b82000,
+ .hal_seq_wcss_umac_ce1_dst_reg = 0x01b83000,
+
+ /* WBM Idle address */
+ .hal_wbm_idle_link_ring_base_lsb = 0x00000874,
+ .hal_wbm_idle_link_ring_misc = 0x00000884,
+
+ /* SW2WBM release address */
+ .hal_wbm_release_ring_base_lsb = 0x000001ec,
+
+ /* WBM2SW release address */
+ .hal_wbm0_release_ring_base_lsb = 0x00000924,
+ .hal_wbm1_release_ring_base_lsb = 0x0000097c,
+};
+
const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = {
.rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM,
.tcl2wbm_rbm_map = ath11k_hw_tcl2wbm_rbm_map_ipq8074,
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index 4996536fbd14f..2bcfdae9519bc 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -72,6 +72,7 @@
#define ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4
#define ATH11K_FW_DIR "ath11k"
+#define ATH11K_FW_NAME_LEN 35
#define ATH11K_BOARD_MAGIC "QCA-ATH11K-BOARD"
#define ATH11K_BOARD_API2_FILE "board-2.bin"
@@ -79,6 +80,7 @@
#define ATH11K_DEFAULT_CAL_FILE "caldata.bin"
#define ATH11K_AMSS_FILE "amss.bin"
#define ATH11K_M3_FILE "m3.bin"
+#define ATH11K_M3_MBN_FILE "m3_fw.mbn"
#define ATH11K_REGDB_FILE_NAME "regdb.bin"
#define ATH11K_CE_OFFSET(ab) (ab->mem_ce - ab->mem)
@@ -137,6 +139,11 @@ struct ath11k_hw_hal_params {
size_t num_tx_rings;
};
+enum ath11k_m3_fw_loaders {
+ ath11k_m3_fw_loader_ahb,
+ ath11k_m3_fw_loader_qmi,
+};
+
struct ath11k_hw_params {
const char *name;
u16 hw_rev;
@@ -147,6 +154,7 @@ struct ath11k_hw_params {
const char *dir;
size_t board_size;
size_t cal_offset;
+ enum ath11k_m3_fw_loaders m3_loader;
} fw;
const struct ath11k_hw_ops *hw_ops;
@@ -288,6 +296,7 @@ extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750;
+extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122;
extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq8074;
extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018;
@@ -425,6 +434,7 @@ extern const struct ath11k_hw_regs qcn9074_regs;
extern const struct ath11k_hw_regs wcn6855_regs;
extern const struct ath11k_hw_regs wcn6750_regs;
extern const struct ath11k_hw_regs ipq5018_regs;
+extern const struct ath11k_hw_regs qcn6122_regs;
static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type)
{
diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c
index 2259adc3bbdc6..cdcf3df26438c 100644
--- a/drivers/net/wireless/ath/ath11k/pcic.c
+++ b/drivers/net/wireless/ath/ath11k/pcic.c
@@ -138,6 +138,15 @@ static const struct ath11k_msi_config ath11k_msi_config[] = {
},
.hw_rev = ATH11K_HW_QCA6698AQ_HW21,
},
+ {
+ .total_vectors = 13,
+ .total_users = 2,
+ .users = (struct ath11k_msi_user[]) {
+ { .name = "CE", .num_vectors = 5, .base_vector = 0 },
+ { .name = "DP", .num_vectors = 8, .base_vector = 5 },
+ },
+ .hw_rev = ATH11K_HW_QCN6122_HW10,
+ },
};
int ath11k_pcic_init_msi_config(struct ath11k_base *ab)
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index 7dc07339b9579..7d77f5c8c609c 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -1724,7 +1724,8 @@ int ath11k_qmi_host_cap_send(struct ath11k_base *ab)
req.bdf_support_valid = 1;
req.bdf_support = 1;
- if (ab->hw_params.m3_fw_support) {
+ if (ab->hw_params.m3_fw_support &&
+ ab->hw_params.fw.m3_loader == ath11k_m3_fw_loader_qmi) {
req.m3_support_valid = 1;
req.m3_support = 1;
req.m3_cache_support_valid = 1;
@@ -2036,6 +2037,45 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
return 0;
}
+static const char *ath11k_qmi_get_mem_reg_name(int mem_type)
+{
+ switch (mem_type) {
+ case HOST_DDR_REGION_TYPE:
+ case BDF_MEM_REGION_TYPE:
+ return "q6-region";
+ case M3_DUMP_REGION_TYPE:
+ return "m3-dump";
+ default:
+ return NULL;
+ }
+}
+
+static int ath11k_qmi_reserved_memory_to_resource(struct ath11k_base *ab,
+ int mem_type,
+ struct resource *res)
+{
+ const char *rname;
+ int ret;
+
+ rname = ath11k_qmi_get_mem_reg_name(mem_type);
+ if (!rname) {
+ ath11k_dbg(ab, ATH11K_DBG_QMI,
+ "invalid memory type %d\n", mem_type);
+ return -EINVAL;
+ }
+
+ ret = of_reserved_mem_region_to_resource_byname(ab->dev->of_node,
+ rname, res);
+ if (ret) {
+ ath11k_dbg(ab, ATH11K_DBG_QMI,
+ "failed to get reserved memory region for %s\n",
+ rname);
+ return ret;
+ }
+
+ return 0;
+}
+
static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)
{
struct device *dev = ab->dev;
@@ -2072,7 +2112,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)
idx++;
break;
case BDF_MEM_REGION_TYPE:
- ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr;
+ ret = ath11k_qmi_reserved_memory_to_resource(ab, BDF_MEM_REGION_TYPE, &res);
+ if (!ret && !(ab->qmi.target_mem[i].size > resource_size(&res)))
+ ab->qmi.target_mem[idx].paddr = res.start;
+ else
+ ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr;
ab->qmi.target_mem[idx].iaddr = NULL;
ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
@@ -2106,6 +2150,16 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)
ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
idx++;
break;
+ case M3_DUMP_REGION_TYPE:
+ ret = ath11k_qmi_reserved_memory_to_resource(ab, M3_DUMP_REGION_TYPE, &res);
+ if (!ret && !(ab->qmi.target_mem[i].size > resource_size(&res))) {
+ ab->qmi.target_mem[idx].paddr = res.start;
+ ab->qmi.target_mem[idx].iaddr = NULL;
+ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+ idx++;
+ }
+ break;
default:
ath11k_warn(ab, "qmi ignore invalid mem req type %d\n",
ab->qmi.target_mem[i].type);
@@ -2189,6 +2243,8 @@ static int ath11k_qmi_request_device_info(struct ath11k_base *ab)
if (!ab->hw_params.ce_remap)
ab->mem_ce = ab->mem;
+ ath11k_hif_config_static_window(ab);
+
return 0;
out:
return ret;
@@ -2299,6 +2355,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab,
{
struct qmi_wlanfw_bdf_download_req_msg_v01 *req;
struct qmi_wlanfw_bdf_download_resp_msg_v01 resp;
+ struct resource res = {};
struct qmi_txn txn;
const u8 *temp = data;
void __iomem *bdf_addr = NULL;
@@ -2312,7 +2369,11 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab,
memset(&resp, 0, sizeof(resp));
if (ab->hw_params.fixed_bdf_addr) {
- bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size);
+ ret = ath11k_qmi_reserved_memory_to_resource(ab, BDF_MEM_REGION_TYPE, &res);
+ if (!ret && !(ab->hw_params.fw.board_size > resource_size(&res)))
+ bdf_addr = ioremap(res.start, ab->hw_params.fw.board_size);
+ else
+ bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size);
if (!bdf_addr) {
ath11k_warn(ab, "qmi ioremap error for bdf_addr\n");
ret = -EIO;
@@ -2589,7 +2650,8 @@ int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab)
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
- if (ab->hw_params.m3_fw_support) {
+ if (ab->hw_params.m3_fw_support &&
+ ab->hw_params.fw.m3_loader == ath11k_m3_fw_loader_qmi) {
ret = ath11k_qmi_m3_load(ab);
if (ret) {
ath11k_err(ab, "failed to load m3 firmware: %d", ret);
diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h
index 350816c81ee54..f5442a04f079d 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.h
+++ b/drivers/net/wireless/ath/ath11k/qmi.h
@@ -21,10 +21,11 @@
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074 0x02
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074 0x07
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750 0x03
+#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122 0x40
#define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32
#define ATH11K_QMI_RESP_LEN_MAX 8192
#define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52
-#define ATH11K_QMI_CALDB_SIZE 0x480000
+#define ATH11K_QMI_CALDB_SIZE 0x500000
#define ATH11K_QMI_BDF_EXT_STR_LENGTH 0x20
#define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT 5
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index 5747bd191bf15..9258839ae7462 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -93,6 +93,7 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
size_t *output_rt_size);
int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx);
+int qcom_scm_pas_set_wifi_power_mode(u32 peripheral, bool enable);
int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);
|
| 3/2 | 2026/09/25 16:39 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33685 output: 37 thoughts: 579 Thoughts: **Analyzing a Qualcomm Driver Patch** Okay, so this patch is clearly making modifications to the Qualcomm driver ecosystem. Let's break down where the changes are occurring. I see it's touching quite a few files: the device tree binding documentation for `qcom,ath11k`, some `qcom_scm` code – specifically adding a `qcom_scm_pas_set_wifi_power_mode` function – and then a whole slew of `ath11k` files within the network wireless directory. This is interesting. The specific files modified are: `ahb.c`, `core.c`, `hw.c`, `pcic.c`, and `qmi.c`. It's also modifying related header files. Let me think about the architecture and reachability... the target architecture is amd64. Hold on, what is `ath11k` again? Ah, right, it's the Qualcomm Atheros 802.11ax driver. Okay, that's important context. Now, what about the bus types and associated hardware? This is focusing on the `ath11k AHB driver` specifically, which is an OF (Open Firmware) / platform driver for Qualcomm SoC Wi-Fi chips. Looking at the compatible strings, it's targeting devices like `qcom,ipq5018-wifi`, `qcom,qcn6122-wifi`, `qcom,ipq8074-wifi`, `qcom,ipq6018-wifi`, and `qcom,wcn6750-wifi`. Now, the crucial question: can this `ath11k AHB` driver realistically run on a standard virtualized environment like amd64 QEMU or GCE? We need to determine if the hardware it's intended for is truly accessible in those environments. Given the compatible strings and the AHB focus, I'm leaning toward "no" off the top of my head. I should probably check the device tree compatibility and look for how it's being matched within the driver code. It's using `of_match_table = ath11k_ahb_of_match`. We should double check `Kconfig` for the `ATH11K` and `ATH11K_AHB` configurations to determine if we can compile and execute it on a standard virtualized setup. |
| 4/2 | 2026/09/25 16:40 | tool | grepper |
0mArgs:{
"Expression": "config ATH11K",
"PathPrefix": "drivers/net/wireless/ath/ath11k"
}Results: {
"Output": "drivers/net/wireless/ath/ath11k/Kconfig-1-# SPDX-License-Identifier: BSD-3-Clause-Clear\ndrivers/net/wireless/ath/ath11k/Kconfig:2:config ATH11K\ndrivers/net/wireless/ath/ath11k/Kconfig-3-\ttristate \"Qualcomm 802.11ax chipset support\"\n--\ndrivers/net/wireless/ath/ath11k/Kconfig-12-\ndrivers/net/wireless/ath/ath11k/Kconfig:13:config ATH11K_AHB\ndrivers/net/wireless/ath/ath11k/Kconfig-14-\ttristate \"Atheros ath11k AHB support\"\n--\ndrivers/net/wireless/ath/ath11k/Kconfig-22-\ndrivers/net/wireless/ath/ath11k/Kconfig:23:config ATH11K_PCI\ndrivers/net/wireless/ath/ath11k/Kconfig-24-\ttristate \"Atheros ath11k PCI support\"\n--\ndrivers/net/wireless/ath/ath11k/Kconfig-32-\ndrivers/net/wireless/ath/ath11k/Kconfig:33:config ATH11K_DEBUG\ndrivers/net/wireless/ath/ath11k/Kconfig-34-\tbool \"QCA ath11k debugging\"\n--\ndrivers/net/wireless/ath/ath11k/Kconfig-40-\ndrivers/net/wireless/ath/ath11k/Kconfig:41:config ATH11K_DEBUGFS\ndrivers/net/wireless/ath/ath11k/Kconfig-42-\tbool \"QCA ath11k debugfs support\"\n--\ndrivers/net/wireless/ath/ath11k/Kconfig-48-\ndrivers/net/wireless/ath/ath11k/Kconfig:49:config ATH11K_TRACING\ndrivers/net/wireless/ath/ath11k/Kconfig-50-\tbool \"ath11k tracing support\"\n--\ndrivers/net/wireless/ath/ath11k/Kconfig-54-\ndrivers/net/wireless/ath/ath11k/Kconfig:55:config ATH11K_SPECTRAL\ndrivers/net/wireless/ath/ath11k/Kconfig-56-\tbool \"QCA ath11k spectral scan support\"\n--\ndrivers/net/wireless/ath/ath11k/Kconfig-63-\ndrivers/net/wireless/ath/ath11k/Kconfig:64:config ATH11K_CFR\ndrivers/net/wireless/ath/ath11k/Kconfig-65-\tbool \"ath11k channel frequency response support\"\n"
} |
| 5/2 | 2026/09/25 16:40 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35499 output: 41 thoughts: 29 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of kernel development and specifically working with ath11k drivers: **Analyzing the ath11k Kconfig File** Alright, I need to get my bearings on the ath11k configuration. Specifically, I'm thinking I should jump into the `drivers/net/wireless/ath/ath11k/Kconfig` file, and take a look at lines 1 through 30. That's usually where the top-level configuration options for a driver are defined. I'm going to start there to re-familiarize myself with the main build configuration options for the ath11k driver. Gotta get a good overview of the available features and their dependencies. This is often where I start when I'm trying to understand how a particular feature is enabled or disabled, or just trying to get a feel for the overall structure of the configuration. Let's see what we've got in that section, I'm expecting to get a better handle on the top-level configuration options from those lines. |
| 6/2 | 2026/09/25 16:40 | tool | read-file |
0mArgs:{
"File": "drivers/net/wireless/ath/ath11k/Kconfig",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: BSD-3-Clause-Clear\n 2:\tconfig ATH11K\n 3:\t\ttristate \"Qualcomm 802.11ax chipset support\"\n 4:\t\tdepends on MAC80211 \u0026\u0026 HAS_DMA\n 5:\t\tselect ATH_COMMON\n 6:\t\tselect QCOM_QMI_HELPERS\n 7:\t\thelp\n 8:\t\t This module adds support for Qualcomm Technologies 802.11ax family of\n 9:\t\t chipsets.\n 10:\t\n 11:\t\t If you choose to build a module, it'll be called ath11k.\n 12:\t\n 13:\tconfig ATH11K_AHB\n 14:\t\ttristate \"Atheros ath11k AHB support\"\n 15:\t\tdepends on ATH11K\n 16:\t\tdepends on REMOTEPROC\n 17:\t\tselect RPMSG\n 18:\t\tselect QRTR\n 19:\t\tselect QRTR_SMD\n 20:\t\thelp\n 21:\t\t This module adds support for AHB bus\n 22:\t\n 23:\tconfig ATH11K_PCI\n 24:\t\ttristate \"Atheros ath11k PCI support\"\n 25:\t\tdepends on ATH11K \u0026\u0026 PCI\n 26:\t\tselect MHI_BUS\n 27:\t\tselect QRTR\n 28:\t\tselect QRTR_MHI\n 29:\t\tselect PCI_PWRCTRL_PWRSEQ if HAVE_PWRCTRL\n 30:\t\thelp\n 31:\t\t This module adds support for PCIE bus\n 32:\t\n 33:\tconfig ATH11K_DEBUG\n 34:\t\tbool \"QCA ath11k debugging\"\n 35:\t\tdepends on ATH11K\n 36:\t\thelp\n 37:\t\t Enables debug support\n 38:\t\n 39:\t\t If unsure, say Y to make it easier to debug problems.\n 40:\t\n"
} |
| 7/2 | 2026/09/25 16:40 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36313 output: 30 thoughts: 122 Thoughts: **Analyzing this Patch: A Quick Dive** Alright, let's dissect this patch. My initial focus is to understand the scope of the modifications. It seems we have two files touched, and the changes are likely related to Qualcomm's wireless technology. First, `Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml` is the Devicetree binding documentation. It's good to see that's being updated; clear, up-to-date documentation is critical. The second file, `drivers/firmware/qcom/qcom_scm.c`, is where things get interesting. Specifically, `qcom_scm_pas_set_wifi_power_mode` catches my eye. This function interacts with the SCM – the Qualcomm Secure Channel Manager – which indicates secure system calls via SMC, following the ARM SMC calling convention. Given that SCM often handles sensitive operations, I should verify the context of this call. Before diving deep, let's quickly check the Kconfig. I'll need to know whether `QCOM_SCM` is enabled and configured, because it will be vital to understanding how this whole mechanism works. |
| 8/2 | 2026/09/25 16:40 | tool | grepper |
0mArgs:{
"Expression": "config QCOM_SCM",
"PathPrefix": "drivers/firmware"
}Results: {
"Output": "drivers/firmware/qcom/Kconfig=17=config QCOM_PAS_TEE\n--\ndrivers/firmware/qcom/Kconfig-26-\ndrivers/firmware/qcom/Kconfig:27:config QCOM_SCM\ndrivers/firmware/qcom/Kconfig-28-\ttristate \"Qualcomm PAS SCM interface driver\"\n"
} |
| 9/2 | 2026/09/25 16:40 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36819 output: 37 thoughts: 22 Thoughts: Here's a summary of my thought process, as requested: **Navigating the QCOM Firmware Configuration** Okay, let's dive into this. My initial thought is to zero in on the `drivers/firmware/qcom/Kconfig` file. I'm specifically targeting the area around line 27. It's a strategic starting point for examining the configuration options related to QCOM firmware. This file is often where the core decisions about enabling and configuring QCOM-specific functionalities are made within the broader kernel build process. I'm expecting to find conditional statements, default settings, and perhaps some dependencies that will provide a good foundation to understand the present situation. I'll be looking for clues about the current build configuration and what's enabled or disabled in the QCOM firmware area. |
| 10/2 | 2026/09/25 16:40 | tool | read-file |
0mArgs:{
"File": "drivers/firmware/qcom/Kconfig",
"FirstLine": 25,
"LineCount": 25
}Results: {
"Contents": " 25:\t\t by the firmware TEE implementation as the backend.\n 26:\t\n 27:\tconfig QCOM_SCM\n 28:\t\ttristate \"Qualcomm PAS SCM interface driver\"\n 29:\t\tselect QCOM_PAS\n 30:\t\tselect QCOM_TZMEM\n 31:\t\tdefault y if ARCH_QCOM\n 32:\t\n 33:\tconfig QCOM_TZMEM\n 34:\t\ttristate\n 35:\t\tselect GENERIC_ALLOCATOR\n 36:\t\n 37:\tchoice\n 38:\t\tprompt \"TrustZone interface memory allocator mode\"\n 39:\t\tdepends on QCOM_TZMEM\n 40:\t\tdefault QCOM_TZMEM_MODE_GENERIC\n 41:\t\thelp\n 42:\t\t Selects the mode of the memory allocator providing memory buffers of\n 43:\t\t suitable format for sharing with the TrustZone. If in doubt, select\n 44:\t\t 'Generic'.\n 45:\t\n 46:\tconfig QCOM_TZMEM_MODE_GENERIC\n 47:\t\tbool \"Generic\"\n 48:\t\thelp\n 49:\t\t Use the generic allocator mode. The memory is page-aligned, non-cachable\n"
} |
| 11/2 | 2026/09/25 16:40 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37366 output: 34 thoughts: 84 Thoughts: Okay, let me break this down. **Diving into the QCOM_SCM and Wi-Fi Power Mode** Right, so I'm looking at `qcom_scm_pas_set_wifi_power_mode` and the first thing that jumps out is that it's using ARM's SMCCC. Specifically, I see `desc.owner = ARM_SMCCC_OWNER_SIP`. That triggers an immediate thought: Can the QCOM_SCM even function, or be probed at all, on an amd64 architecture? My gut says "unlikely" without specific support, but let's not assume anything. The next logical step is to confirm how the QCOM_SCM is initialized, and that would be by examining the probe function within the `drivers/firmware/qcom/qcom_scm.c` file. I need to understand how this driver determines if it's running on a supported platform before I can draw any concrete conclusions about this Wi-Fi power mode functionality. This will tell me if the code path is even relevant to my current platform. |
| 12/2 | 2026/09/25 16:40 | tool | grepper |
0mArgs:{
"Expression": "qcom_scm_probe",
"PathPrefix": "drivers/firmware/qcom/"
}Results: {
"Output": "drivers/firmware/qcom/qcom_scm.c=2617=bool qcom_scm_is_available(void)\ndrivers/firmware/qcom/qcom_scm.c-2618-{\ndrivers/firmware/qcom/qcom_scm.c:2619:\t/* Paired with smp_store_release() in qcom_scm_probe */\ndrivers/firmware/qcom/qcom_scm.c-2620-\treturn !!smp_load_acquire(\u0026__scm);\n--\ndrivers/firmware/qcom/qcom_scm.c=2761=static int set_download_mode(const char *val, const struct kernel_param *kp)\n--\ndrivers/firmware/qcom/qcom_scm.c-2778-\tdownload_mode = ret;\ndrivers/firmware/qcom/qcom_scm.c:2779:\t/* Pairs with smp_store_release() in qcom_scm_probe(). */\ndrivers/firmware/qcom/qcom_scm.c-2780-\tscm = smp_load_acquire(\u0026__scm);\n--\ndrivers/firmware/qcom/qcom_scm.c=2806=static int set_minidump_dest(const char *val, const struct kernel_param *kp)\n--\ndrivers/firmware/qcom/qcom_scm.c-2819-\ndrivers/firmware/qcom/qcom_scm.c:2820:\t/* Pairs with smp_store_release() in qcom_scm_probe(). */\ndrivers/firmware/qcom/qcom_scm.c-2821-\tscm = smp_load_acquire(\u0026__scm);\n--\ndrivers/firmware/qcom/qcom_scm.c=2834=MODULE_PARM_DESC(minidump_dest, \"Minidump SRAM destination: usb (default) or storage\");\ndrivers/firmware/qcom/qcom_scm.c-2835-\ndrivers/firmware/qcom/qcom_scm.c:2836:static int qcom_scm_probe(struct platform_device *pdev)\ndrivers/firmware/qcom/qcom_scm.c-2837-{\n--\ndrivers/firmware/qcom/qcom_scm.c=3015=static struct platform_driver qcom_scm_driver = {\n--\ndrivers/firmware/qcom/qcom_scm.c-3020-\t},\ndrivers/firmware/qcom/qcom_scm.c:3021:\t.probe = qcom_scm_probe,\ndrivers/firmware/qcom/qcom_scm.c-3022-\t.shutdown = qcom_scm_shutdown,\n"
} |
| 13/2 | 2026/09/25 16:40 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38407 output: 47 thoughts: 1437 Thoughts: **Analyzing the ath11k WiFi Driver Changes**
Okay, let's break this down. First, I noticed `qcom_scm_probe` uses Qualcomm DT compatible strings, which suggests it's a platform driver dealing with ARM SMC/HVC calls on Qualcomm SoCs. I needed to figure out which code paths call the function `qcom_scm_pas_set_wifi_power_mode`. I traced this to `ath11k_ahb_boot_user_pd` and `ath11k_ahb_stop_user_pd` within `ath11k/ahb.c`.
Aha! `ath11k/ahb.c` is the AHB platform driver for Qualcomm's integrated WiFi solutions, specifically the IPQ and WCN series, indicating Qualcomm SoC integration for routers and mobile devices. Given this, I needed to check what had been modified within the `ath11k` codebase, and whether these changes could be triggered by PCI or user-accessible virtual hardware.
A deep dive into the modified files revealed the following:
* **`ahb.c`**: It's all about platform driver operations, probing, and compatible strings: `qcom,ipq8074-wifi`, `qcom,ipq6018-wifi`, `qcom,wcn6750-wifi`, `qcom,ipq5018-wifi`, `qcom,qcn6122-wifi`. All seem to be OF platform devices designed for Qualcomm SoCs. No PCI, USB, or virtual here.
But wait! `ath11k` *does* have PCI support, so now I need to expand my check beyond `ahb.c`. Let's see what was changed in other files:
* **`core.c`**: Here, I find additions or modifications to the `.m3_loader` for several SoCs, including `ath11k_m3_fw_loader_ahb` or `ath11k_m3_fw_loader_qmi`, which point to firmware loading strategies. Specifically,
* IPQ8074/hw2.0, IPQ6018/hw1.0, and IPQ5018/hw1.0 all now use the ahb loader, which is interesting.
* The others, QCA6390/hw2.0, QCN9074/hw1.0, WCN6855/hw2.0 and hw2.1, WCN6750/hw1.0, QCA2066/hw2.1 and QCA6698AQ/hw2.1, now use the qmi loader.
* IPQ5018 has `.m3_fw_support = true` added.
* And, most importantly, the new `ATH11K_HW_QCN6122_HW10` entry has been added.
* **`core.h`**: The new hardware revision, `ATH11K_HW_QCN6122_HW10`, has been added to the `ath11k_hw_rev` enum.
* **`hif.h`**: A `config_static_window` function pointer was added to `ath11k_hif_ops`, with an inline function to call it.
* **`hw.c` / `hw.h`**: We see new ring masks, registers, and definitions related to the QCN6122 hardware.
* **`pcic.c`**: This is where things get interesting. I see MSI configuration added for `ATH11K_HW_QCN6122_HW10`, detailing interrupt vector allocation. It's used by both PCI and AHB.
So, the changes involve firmware loading, hardware-specific configuration, and interrupt setup for QCN6122, including PCI support. But most of this is driven by the specific hardware configuration and platform type.
|
| 14/2 | 2026/09/25 16:40 | tool | grepper |
0mArgs:{
"Expression": "ath11k_pcic_init_msi_config",
"PathPrefix": "drivers/net/wireless/ath/ath11k/"
}Results: {
"Output": "drivers/net/wireless/ath/ath11k/ahb.c=1369=static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab)\n--\ndrivers/net/wireless/ath/ath11k/ahb.c-1378-\ndrivers/net/wireless/ath/ath11k/ahb.c:1379:\tret = ath11k_pcic_init_msi_config(ab);\ndrivers/net/wireless/ath/ath11k/ahb.c-1380-\tif (ret) {\n--\ndrivers/net/wireless/ath/ath11k/pci.c=949=static int ath11k_pci_probe(struct pci_dev *pdev,\n--\ndrivers/net/wireless/ath/ath11k/pci.c-1082-\ndrivers/net/wireless/ath/ath11k/pci.c:1083:\tret = ath11k_pcic_init_msi_config(ab);\ndrivers/net/wireless/ath/ath11k/pci.c-1084-\tif (ret) {\n--\ndrivers/net/wireless/ath/ath11k/pcic.c=66=static const struct ath11k_msi_config ath11k_msi_config[] = {\n--\ndrivers/net/wireless/ath/ath11k/pcic.c-151-\ndrivers/net/wireless/ath/ath11k/pcic.c:152:int ath11k_pcic_init_msi_config(struct ath11k_base *ab)\ndrivers/net/wireless/ath/ath11k/pcic.c-153-{\n--\ndrivers/net/wireless/ath/ath11k/pcic.c-172-}\ndrivers/net/wireless/ath/ath11k/pcic.c:173:EXPORT_SYMBOL(ath11k_pcic_init_msi_config);\ndrivers/net/wireless/ath/ath11k/pcic.c-174-\n--\ndrivers/net/wireless/ath/ath11k/pcic.h=46=void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab);\ndrivers/net/wireless/ath/ath11k/pcic.h:47:int ath11k_pcic_init_msi_config(struct ath11k_base *ab);\ndrivers/net/wireless/ath/ath11k/pcic.h-48-int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,\n"
} |
| 15/2 | 2026/09/25 16:40 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41480 output: 36 thoughts: 1618 Thoughts: **Analysis of ath11k QMI Changes and Fuzzing Feasibility** Okay, let's break down these changes in `qmi.c`. First, I see the conditional changes in `ath11k_qmi_host_cap_send` and `ath11k_qmi_wlanfw_m3_info_send`. It looks like the added condition, checking for `ab->hw_params.fw.m3_loader == ath11k_m3_fw_loader_qmi`, is redundant for all PCI chips since that's the default value. It doesn't change anything for the PCI use case. This doesn't seem to be a functional change. Next, `ath11k_qmi_request_device_info` now calls `ath11k_hif_config_static_window`, but this function immediately returns for PCI devices due to the `static_window_map` check. So, for PCI devices, this change is essentially a no-op, adding an unnecessary call. Now, let's look at the changes in `ath11k_qmi_assign_target_mem_chunk` and `ath11k_qmi_load_file_target_mem`. Here, we see the introduction of `ath11k_qmi_reserved_memory_to_resource` using `of_reserved_mem_region_to_resource_byname`. This is where it gets interesting, especially concerning PCI devices. For PCI, `ab->dev->of_node` is likely NULL or indicates no reserved memory in the device tree (DT). This means `of_reserved_mem_region_to_resource_byname` will likely fail. The critical question here is whether any of this is reachable in standard virtualized environments like QEMU or GCE on amd64. Thinking about the triage philosophy, specifically the negative criteria for `WorthFuzzing`, "Structurally unreachable hardware" comes to mind. Does the kernel's emulation environment have any level of abstraction of the hardware from ath11k drivers to be reachable? Standard QEMU does not emulate ath11k PCI devices (QCA6390, QCN9074, etc.) or Qualcomm AHB/rproc SoC hardware. It's a non-trivial WiFi device. Furthermore, `mac80211_hwsim` and `virt_wifi` are completely separate drivers. The question then becomes whether `ath11k` has any virtual or software mode or gadget interface. Considering this, the code changes described are likely *not* reachable in a standard fuzzing setup. They fall under the "structurally unreachable hardware" category and should therefore be marked as not worthy of fuzzing. |
| 16/2 | 2026/09/25 16:40 | tool | grepper |
0mArgs:{
"Expression": "virtual|simulat",
"PathPrefix": "drivers/net/wireless/ath/ath11k"
}Results: {
"Output": "drivers/net/wireless/ath/ath11k/debugfs.c=343=static const struct file_operations fops_bcn_stats = {\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-350-\ndrivers/net/wireless/ath/ath11k/debugfs.c:351:static ssize_t ath11k_read_simulate_fw_crash(struct file *file,\ndrivers/net/wireless/ath/ath11k/debugfs.c-352-\t\t\t\t\t char __user *user_buf,\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-355-\tconst char buf[] =\ndrivers/net/wireless/ath/ath11k/debugfs.c:356:\t\t\"To simulate firmware crash write one of the keywords to this file:\\n\"\ndrivers/net/wireless/ath/ath11k/debugfs.c-357-\t\t\"`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\\n\"\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-369- */\ndrivers/net/wireless/ath/ath11k/debugfs.c:370:static ssize_t ath11k_write_simulate_fw_crash(struct file *file,\ndrivers/net/wireless/ath/ath11k/debugfs.c-371-\t\t\t\t\t const char __user *user_buf,\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-406-\tif (!strcmp(buf, \"assert\")) {\ndrivers/net/wireless/ath/ath11k/debugfs.c:407:\t\tath11k_info(ab, \"simulating firmware assert crash\\n\");\ndrivers/net/wireless/ath/ath11k/debugfs.c-408-\t\tret = ath11k_wmi_force_fw_hang_cmd(ar,\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-420-\tif (ret) {\ndrivers/net/wireless/ath/ath11k/debugfs.c:421:\t\tath11k_warn(ab, \"failed to simulate firmware crash: %d\\n\", ret);\ndrivers/net/wireless/ath/ath11k/debugfs.c-422-\t\tgoto exit;\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-430-\ndrivers/net/wireless/ath/ath11k/debugfs.c:431:static const struct file_operations fops_simulate_fw_crash = {\ndrivers/net/wireless/ath/ath11k/debugfs.c:432:\t.read = ath11k_read_simulate_fw_crash,\ndrivers/net/wireless/ath/ath11k/debugfs.c:433:\t.write = ath11k_write_simulate_fw_crash,\ndrivers/net/wireless/ath/ath11k/debugfs.c-434-\t.open = simple_open,\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c=854=int ath11k_debugfs_pdev_create(struct ath11k_base *ab)\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-858-\ndrivers/net/wireless/ath/ath11k/debugfs.c:859:\tdebugfs_create_file(\"simulate_fw_crash\", 0600, ab-\u003edebugfs_soc, ab,\ndrivers/net/wireless/ath/ath11k/debugfs.c:860:\t\t\t \u0026fops_simulate_fw_crash);\ndrivers/net/wireless/ath/ath11k/debugfs.c-861-\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c=1096=static const struct file_operations fops_pktlog_filter = {\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-1101-\ndrivers/net/wireless/ath/ath11k/debugfs.c:1102:static ssize_t ath11k_write_simulate_radar(struct file *file,\ndrivers/net/wireless/ath/ath11k/debugfs.c-1103-\t\t\t\t\t const char __user *user_buf,\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-1108-\ndrivers/net/wireless/ath/ath11k/debugfs.c:1109:\tret = ath11k_wmi_simulate_radar(ar);\ndrivers/net/wireless/ath/ath11k/debugfs.c-1110-\tif (ret)\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-1115-\ndrivers/net/wireless/ath/ath11k/debugfs.c:1116:static const struct file_operations fops_simulate_radar = {\ndrivers/net/wireless/ath/ath11k/debugfs.c:1117:\t.write = ath11k_write_simulate_radar,\ndrivers/net/wireless/ath/ath11k/debugfs.c-1118-\t.open = simple_open\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c=1469=int ath11k_debugfs_register(struct ath11k *ar)\n--\ndrivers/net/wireless/ath/ath11k/debugfs.c-1502-\tif (ar-\u003ehw-\u003ewiphy-\u003ebands[NL80211_BAND_5GHZ]) {\ndrivers/net/wireless/ath/ath11k/debugfs.c:1503:\t\tdebugfs_create_file(\"dfs_simulate_radar\", 0200,\ndrivers/net/wireless/ath/ath11k/debugfs.c-1504-\t\t\t\t ar-\u003edebug.debugfs_pdev, ar,\ndrivers/net/wireless/ath/ath11k/debugfs.c:1505:\t\t\t\t \u0026fops_simulate_radar);\ndrivers/net/wireless/ath/ath11k/debugfs.c-1506-\t\tdebugfs_create_bool(\"dfs_block_radar_events\", 0200,\n--\ndrivers/net/wireless/ath/ath11k/hal_desc.h=17=struct ath11k_buffer_addr {\n--\ndrivers/net/wireless/ath/ath11k/hal_desc.h-47- *\t\tField can be used by SW to for example associate the buffers\ndrivers/net/wireless/ath/ath11k/hal_desc.h:48: *\t\tphysical address with the virtual address.\ndrivers/net/wireless/ath/ath11k/hal_desc.h-49- */\n--\ndrivers/net/wireless/ath/ath11k/wmi.c=9101=ath11k_wmi_send_unit_test_cmd(struct ath11k *ar,\n--\ndrivers/net/wireless/ath/ath11k/wmi.c-9158-\ndrivers/net/wireless/ath/ath11k/wmi.c:9159:int ath11k_wmi_simulate_radar(struct ath11k *ar)\ndrivers/net/wireless/ath/ath11k/wmi.c-9160-{\n--\ndrivers/net/wireless/ath/ath11k/wmi.c-9178-\t/* Currently we could pass segment_id(b0 - b1), chirp(b2)\ndrivers/net/wireless/ath/ath11k/wmi.c:9179:\t * freq offset (b3 - b10) to unit test. For simulation\ndrivers/net/wireless/ath/ath11k/wmi.c-9180-\t * purpose this can be set to 0 which is valid.\n--\ndrivers/net/wireless/ath/ath11k/wmi.h=3516=enum wmi_sta_ps_mode {\n--\ndrivers/net/wireless/ath/ath11k/wmi.h-3528-/* type, 0:unused 1: ASSERT 2: not respond detect command\ndrivers/net/wireless/ath/ath11k/wmi.h:3529: * delay_time_ms, the simulate will delay time\ndrivers/net/wireless/ath/ath11k/wmi.h-3530- */\n--\ndrivers/net/wireless/ath/ath11k/wmi.h=6544=void ath11k_wmi_fw_stats_fill(struct ath11k *ar,\n--\ndrivers/net/wireless/ath/ath11k/wmi.h-6546-\t\t\t char *buf);\ndrivers/net/wireless/ath/ath11k/wmi.h:6547:int ath11k_wmi_simulate_radar(struct ath11k *ar);\ndrivers/net/wireless/ath/ath11k/wmi.h-6548-void ath11k_wmi_fill_default_twt_params(struct wmi_twt_enable_params *twt_params);\n"
} |
| 17/2 | 2026/09/25 16:40 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46059 output: 134 thoughts: 1006 Thoughts: **Analysis of ath11k QCN6122 UserPD Support Patch for Fuzzing Suitability** Okay, let's break this down. The patch focuses on adding UserPD support for the QCN6122 WiFi chip, specifically on Qualcomm platforms using the `ath11k` driver. This involves interactions with Qualcomm's WCSS remote processor via SCM and SMEM. My initial scan of the code changes, including modifications to device tree bindings, the `qcom_scm` driver, and the `ath11k/ahb.c` file, clearly indicate this is specific to Qualcomm's SoC hardware. The changes also touch on hardware definitions, register offsets, and firmware loading specific to QCN6122 and AHB devices within the core `ath11k` driver. Now, the critical question: is this code *reachable* within standard virtualized environments like GCE or QEMU, which is the primary gate for fuzzing? Looking at the criteria, particularly the "structurally unreachable hardware" section, it's pretty clear this is a no. We are dealing with: * Vendor-specific hardware: Qualcomm SoC components and the QCN6122 WiFi chip. * Interactions via SCM, SMEM, and remoteproc, which inherently depend on Qualcomm's TrustZone firmware and ARM SMCCC calls, making them specific to ARM-based Qualcomm platforms. * The requirement of physical ASIC/PCIe cards for proper operation. My assessment is that none of this is emulated in a standard QEMU environment on amd64. Therefore, the modified code is structurally unreachable on the target architecture, which is amd64. I double-checked: `qcom_scm_pas_set_wifi_power_mode` is only called within the `ath11k/ahb.c` file. There are no user-space entrypoints or mock interfaces present. Given the structural unreachability of the code, I conclude that `WorthFuzzing` should be `false`, `FocusSymbols` should be `[]`, and `EnableConfigs` should also be `[]`. I'm ready to prepare the `set-results` call now. |
| 18/2 | 2026/09/25 16:40 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch series adds Multi-UserPD support for Qualcomm QCN6122 and IPQ5018 Wi-Fi devices in the ath11k AHB driver and adds a corresponding power mode SCM call in the Qualcomm SCM firmware driver. These changes require physical Qualcomm SoC hardware (remoteproc, SMEM, TrustZone SCM, and integrated Wi-Fi hardware) and are structurally unreachable in standard virtualized environments (QEMU/GCE on amd64).",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch series adds Multi-UserPD support for Qualcomm QCN6122 and IPQ5018 Wi-Fi devices in the ath11k AHB driver and adds a corresponding power mode SCM call in the Qualcomm SCM firmware driver. These changes require physical Qualcomm SoC hardware (remoteproc, SMEM, TrustZone SCM, and integrated Wi-Fi hardware) and are structurally unreachable in standard virtualized environments (QEMU/GCE on amd64).",
"WorthFuzzing": false
} |
| 19/1 | 2026/09/25 16:40 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|