diff --git a/.github/workflows/ipq807x.yaml b/.github/workflows/ipq807x.yaml new file mode 100644 index 00000000000000..cf133e504657d3 --- /dev/null +++ b/.github/workflows/ipq807x.yaml @@ -0,0 +1,86 @@ +name: Build IPQ807x +on: [push] + +jobs: + build: + name: Prepare and build IPQ807x generic images + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Update and install feeds + run: | + ./scripts/feeds update -a + ./scripts/feeds install -a + + - name: Generate config for IPQ807x Generic + run: | + echo "CONFIG_TARGET_ipq807x=y" > .config + echo "CONFIG_TARGET_ipq807x_generic=y\n" >> .config + echo "CONFIG_TARGET_MULTI_PROFILE=y\n" >> .config + echo "CONFIG_TARGET_ALL_PROFILES=y\n" >> .config + echo "CONFIG_TARGET_PER_DEVICE_ROOTFS=y\n" >> .config + echo "CONFIG_TARGET_ROOTFS_INITRAMFS=y\n" >> .config + echo "CONFIG_TARGET_INITRAMFS_COMPRESSION_ZSTD=y\n" >> .config + echo "CONFIG_PACKAGE_luci=y\n" >> .config + echo "CONFIG_PACKAGE_kmod-qca-nss-dp=y\n" >> .config + echo "CONFIG_PACKAGE_qca-ssdk-shell=y\n" >> .config + echo "CONFIG_PACKAGE_wpad-basic-wolfssl=m\n" >> .config + echo "CONFIG_PACKAGE_wpad-wolfssl=y\n" >> .config + make defconfig + + - name: Download package sources + run: make download V=s + + - name: Build tools + run: | + make tools/install -j$(nproc) V=s || \ + make tools/install V=s + + - name: Build toolchain + run: | + make toolchain/install -j$(nproc) V=s || \ + make toolchain/install V=s + + - name: Build target images + run: | + make -j$(nproc) V=s || \ + make V=s + + - name: TAR output + run: tar -cvf ipq807xx-images.tar bin/targets/ipq807x/generic + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: ipq807x-images + path: ipq807xx-images.tar + + release: + name: Make a release + runs-on: ubuntu-latest + needs: build + + steps: + - name: Download the image artifacts + uses: actions/download-artifact@v2 + with: + name: ipq807x-images + + - name: Extract the image artifacts + run: tar xf ipq807xx-images.tar + + - name: Get the current date + run: echo "NOW=$(date +%F)" >> $GITHUB_ENV + + - name: Create a release + uses: "lauravuo/action-automatic-releases@test-changes" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: true + title: "Updated prebuilt images ${{ env.NOW }}" + automatic_release_tag: "ipq807x-${{ env.NOW }}" + auto_generate_release_notes: true + files: bin/targets/ipq807x/generic/* \ No newline at end of file diff --git a/feeds.conf.default b/feeds.conf.default index 50ae67cb9dba99..64397d88a1119c 100644 --- a/feeds.conf.default +++ b/feeds.conf.default @@ -2,6 +2,7 @@ src-git-full packages https://git.openwrt.org/feed/packages.git src-git-full luci https://git.openwrt.org/project/luci.git src-git-full routing https://git.openwrt.org/feed/routing.git src-git-full telephony https://git.openwrt.org/feed/telephony.git +src-git-full nss_packages https://github.com/robimarko/nss-packages.git #src-git-full video https://github.com/openwrt/video.git #src-git-full targets https://github.com/openwrt/targets.git #src-git-full oldpackages http://git.openwrt.org/packages.git diff --git a/package/boot/uboot-envtools/files/ipq807x b/package/boot/uboot-envtools/files/ipq807x new file mode 100644 index 00000000000000..834f668935e989 --- /dev/null +++ b/package/boot/uboot-envtools/files/ipq807x @@ -0,0 +1,34 @@ +[ -e /etc/config/ubootenv ] && exit 0 + +touch /etc/config/ubootenv + +. /lib/uboot-envtools.sh +. /lib/functions.sh + +board=$(board_name) + +case "$board" in +edgecore,eap102) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x10000" "1" + ;; +edimax,cax1800|\ +redmi,ax6|\ +xiaomi,ax3600|\ +xiaomi,ax9000) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x20000" + ;; +qnap,301w) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x20000" "0x20000" "1" + ;; +esac + +config_load ubootenv +config_foreach ubootenv_add_app_config ubootenv + +exit 0 diff --git a/package/firmware/ath11k-firmware/Makefile b/package/firmware/ath11k-firmware/Makefile new file mode 100644 index 00000000000000..525a927fb50268 --- /dev/null +++ b/package/firmware/ath11k-firmware/Makefile @@ -0,0 +1,74 @@ +# +# Copyright (C) 2021 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=ath11k-firmware +PKG_SOURCE_DATE:=2022-03-04 +PKG_SOURCE_VERSION:=4abc7856e26618a9b7a70f24d7f61e844f160e17 +PKG_MIRROR_HASH:=7fa0f1d56e951a9351826710729312ca4d6a18866cb22d0cbaff49c628e75902 +PKG_RELEASE:=$(AUTORELEASE) + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/quic/upstream-wifi-fw.git + +PKG_LICENSE_FILES:=ath11k-firmware/LICENSE.md + +PKG_MAINTAINER:=Robert Marko + +include $(INCLUDE_DIR)/package.mk + +define Package/ath11k-firmware-default + SECTION:=firmware + CATEGORY:=Firmware + URL:=$(PKG_SOURCE_URL) + DEPENDS:= +endef + +define Package/ath11k-firmware-ipq6018 +$(Package/ath11k-firmware-default) + TITLE:=ath11k firmware for IPQ6018 devices +endef + +define Package/ath11k-firmware-ipq8074 +$(Package/ath11k-firmware-default) + TITLE:=ath11k firmware for IPQ8074 devices +endef + +define Package/ath11k-firmware-qcn9074 +$(Package/ath11k-firmware-default) + TITLE:=ath11k firmware for QCN9074 devices +endef + +define Build/Compile + +endef + +define Package/ath11k-firmware-ipq6018/install + $(INSTALL_DIR) $(1)/lib/firmware/IPQ6018 + $(INSTALL_DATA) \ + $(PKG_BUILD_DIR)/ath11k-firmware/IPQ6018/hw1.0/2.5.0.1/WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1/* \ + $(1)/lib/firmware/IPQ6018/ +endef + +define Package/ath11k-firmware-ipq8074/install + $(INSTALL_DIR) $(1)/lib/firmware/IPQ8074 + $(INSTALL_DATA) \ + $(PKG_BUILD_DIR)/ath11k-firmware/IPQ8074/hw2.0/2.5.0.1/WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1/* \ + $(1)/lib/firmware/IPQ8074/ +endef + +define Package/ath11k-firmware-qcn9074/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/QCN9074/hw1.0 + $(INSTALL_DATA) \ + $(PKG_BUILD_DIR)/ath11k-firmware/QCN9074/hw1.0/2.5.0.1/WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1/* \ + $(1)/lib/firmware/ath11k/QCN9074/hw1.0/ +endef + +$(eval $(call BuildPackage,ath11k-firmware-ipq6018)) +$(eval $(call BuildPackage,ath11k-firmware-ipq8074)) +$(eval $(call BuildPackage,ath11k-firmware-qcn9074)) diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index f8445f0e3383be..5db7e743e7686a 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -27,16 +27,22 @@ endef ALLWIFIBOARDS:= \ aruba_ap-365 \ devolo_magic-2-wifi-next \ + edgecore_eap102 \ edgecore_ecw5410 \ edgecore_oap100 \ + edimax_cax1800 \ glinet_gl-ap1300 \ glinet_gl-b2200 \ glinet_gl-s1300 \ linksys_ea8300 \ p2w_r619ac \ + qnap_301w \ qxwlan_e2600ac-c1 \ qxwlan_e2600ac-c2 \ - teltonika_rutx + teltonika_rutx \ + redmi_ax6 \ + xiaomi_ax3600 \ + xiaomi_ax9000 ALLWIFIPACKAGES:=$(foreach BOARD,$(ALLWIFIBOARDS),ipq-wifi-$(BOARD)) @@ -44,7 +50,7 @@ define Package/ipq-wifi-default SUBMENU:=ath10k Board-Specific Overrides SECTION:=firmware CATEGORY:=Firmware - DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x) + DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x||TARGET_ipq807x) TITLE:=Custom Board endef @@ -53,6 +59,11 @@ define ipq-wifi-install-one-to $(INSTALL_DATA) $(1) $(2)/lib/firmware/ath10k/$(3)/board-2.bin endef +define ipq-wifi-install-ath11-one-to + $(INSTALL_DIR) $(2)/lib/firmware/ath11k/$(3)/ + $(INSTALL_DATA) $(1) $(2)/lib/firmware/ath11k/$(3)/board-2.bin +endef + define ipq-wifi-install-one $(if $(filter $(suffix $(1)),.QCA4019 .qca4019),\ $(call ipq-wifi-install-one-to,$(1),$(2),QCA4019/hw1.0),\ @@ -60,8 +71,14 @@ define ipq-wifi-install-one $(call ipq-wifi-install-one-to,$(1),$(2),QCA9888/hw2.0),\ $(if $(filter $(suffix $(1)),.QCA9984 .qca9984),\ $(call ipq-wifi-install-one-to,$(1),$(2),QCA9984/hw1.0),\ + $(if $(filter $(suffix $(1)),.QCA9889 .qca9889),\ + $(call ipq-wifi-install-one-to,$(1),$(2),QCA9887/hw1.0),\ + $(if $(filter $(suffix $(1)),.IPQ8074 .ipq8074),\ + $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ8074/hw2.0),\ + $(if $(filter $(suffix $(1)),.QCN9074 .qcn9074),\ + $(call ipq-wifi-install-ath11-one-to,$(1),$(2),QCN9074/hw1.0),\ $(error Unrecognized board-file suffix '$(suffix $(1))' for '$(1)')\ - ))) + )))))) endef # Blank line required at end of above define due to foreach context @@ -99,15 +116,21 @@ endef $(eval $(call generate-ipq-wifi-package,aruba_ap-365,Aruba AP-365)) $(eval $(call generate-ipq-wifi-package,devolo_magic-2-wifi-next,devolo Magic 2 WiFi next)) +$(eval $(call generate-ipq-wifi-package,edgecore_eap102,Edgecore EAP102)) $(eval $(call generate-ipq-wifi-package,edgecore_ecw5410,Edgecore ECW5410)) $(eval $(call generate-ipq-wifi-package,edgecore_oap100,Edgecore OAP100)) +$(eval $(call generate-ipq-wifi-package,edimax_cax1800,Edimax CAX1800)) $(eval $(call generate-ipq-wifi-package,glinet_gl-ap1300,GL.iNet GL-AP1300)) $(eval $(call generate-ipq-wifi-package,glinet_gl-b2200,GL.iNet GL-B2200)) $(eval $(call generate-ipq-wifi-package,glinet_gl-s1300,GL.iNet GL-S1300)) $(eval $(call generate-ipq-wifi-package,linksys_ea8300,Linksys EA8300)) $(eval $(call generate-ipq-wifi-package,p2w_r619ac,P&W R619AC)) +$(eval $(call generate-ipq-wifi-package,qnap_301w,QNAP 301w)) $(eval $(call generate-ipq-wifi-package,qxwlan_e2600ac-c1,Qxwlan E2600AC C1)) $(eval $(call generate-ipq-wifi-package,qxwlan_e2600ac-c2,Qxwlan E2600AC C2)) $(eval $(call generate-ipq-wifi-package,teltonika_rutx,Teltonika RUTX)) +$(eval $(call generate-ipq-wifi-package,redmi_ax6,Redmi AX6)) +$(eval $(call generate-ipq-wifi-package,xiaomi_ax3600,Xiaomi AX3600)) +$(eval $(call generate-ipq-wifi-package,xiaomi_ax9000,Xiaomi AX9000)) $(foreach PACKAGE,$(ALLWIFIPACKAGES),$(eval $(call BuildPackage,$(PACKAGE)))) diff --git a/package/firmware/ipq-wifi/board-edgecore_eap102.ipq8074 b/package/firmware/ipq-wifi/board-edgecore_eap102.ipq8074 new file mode 100644 index 00000000000000..6bb9b39133ea8d Binary files /dev/null and b/package/firmware/ipq-wifi/board-edgecore_eap102.ipq8074 differ diff --git a/package/firmware/ipq-wifi/board-edimax_cax1800.ipq8074 b/package/firmware/ipq-wifi/board-edimax_cax1800.ipq8074 new file mode 100644 index 00000000000000..44b090d0fa203e Binary files /dev/null and b/package/firmware/ipq-wifi/board-edimax_cax1800.ipq8074 differ diff --git a/package/firmware/ipq-wifi/board-qnap_301w.ipq8074 b/package/firmware/ipq-wifi/board-qnap_301w.ipq8074 new file mode 100644 index 00000000000000..6b8f8b9ce8470b Binary files /dev/null and b/package/firmware/ipq-wifi/board-qnap_301w.ipq8074 differ diff --git a/package/firmware/ipq-wifi/board-redmi_ax6.ipq8074 b/package/firmware/ipq-wifi/board-redmi_ax6.ipq8074 new file mode 100644 index 00000000000000..98ed9c6f65961a Binary files /dev/null and b/package/firmware/ipq-wifi/board-redmi_ax6.ipq8074 differ diff --git a/package/firmware/ipq-wifi/board-xiaomi_ax3600.ipq8074 b/package/firmware/ipq-wifi/board-xiaomi_ax3600.ipq8074 new file mode 100644 index 00000000000000..db8ef4cefbb50a Binary files /dev/null and b/package/firmware/ipq-wifi/board-xiaomi_ax3600.ipq8074 differ diff --git a/package/firmware/ipq-wifi/board-xiaomi_ax3600.qca9889 b/package/firmware/ipq-wifi/board-xiaomi_ax3600.qca9889 new file mode 100644 index 00000000000000..af4405cd53cb9c Binary files /dev/null and b/package/firmware/ipq-wifi/board-xiaomi_ax3600.qca9889 differ diff --git a/package/firmware/ipq-wifi/board-xiaomi_ax9000.ipq8074 b/package/firmware/ipq-wifi/board-xiaomi_ax9000.ipq8074 new file mode 100644 index 00000000000000..babfaa2a9523df Binary files /dev/null and b/package/firmware/ipq-wifi/board-xiaomi_ax9000.ipq8074 differ diff --git a/package/firmware/ipq-wifi/board-xiaomi_ax9000.qca9889 b/package/firmware/ipq-wifi/board-xiaomi_ax9000.qca9889 new file mode 100644 index 00000000000000..abccf935585ae3 Binary files /dev/null and b/package/firmware/ipq-wifi/board-xiaomi_ax9000.qca9889 differ diff --git a/package/firmware/ipq-wifi/board-xiaomi_ax9000.qcn9074 b/package/firmware/ipq-wifi/board-xiaomi_ax9000.qcn9074 new file mode 100644 index 00000000000000..67e0102ee89531 Binary files /dev/null and b/package/firmware/ipq-wifi/board-xiaomi_ax9000.qcn9074 differ diff --git a/package/kernel/linux/modules/netsupport.mk b/package/kernel/linux/modules/netsupport.mk index 6fb3b57e126fb7..658d2deadacbf0 100644 --- a/package/kernel/linux/modules/netsupport.mk +++ b/package/kernel/linux/modules/netsupport.mk @@ -1346,3 +1346,67 @@ define KernelPackage/wireguard/description endef $(eval $(call KernelPackage,wireguard)) + + +define KernelPackage/qrtr + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=Qualcomm IPC Router support + HIDDEN:=1 + DEPENDS:=@LINUX_5_15 + KCONFIG:=CONFIG_QRTR + FILES:= \ + $(LINUX_DIR)/net/qrtr/qrtr.ko \ + $(LINUX_DIR)/net/qrtr/ns.ko + AUTOLOAD:=$(call AutoProbe,qrtr) +endef + +define KernelPackage/qrtr/description + Qualcomm IPC Router support +endef + +$(eval $(call KernelPackage,qrtr)) + +define KernelPackage/qrtr-tun + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=TUN device for Qualcomm IPC Router + DEPENDS:=+kmod-qrtr + KCONFIG:=CONFIG_QRTR_TUN + FILES:= $(LINUX_DIR)/net/qrtr/qrtr-tun.ko + AUTOLOAD:=$(call AutoProbe,qrtr-tun) +endef + +define KernelPackage/qrtr-tun/description + TUN device for Qualcomm IPC Router +endef + +$(eval $(call KernelPackage,qrtr-tun)) + +define KernelPackage/qrtr-smd + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=SMD IPC Router channels + DEPENDS:=+kmod-qrtr @TARGET_ipq807x + KCONFIG:=CONFIG_QRTR_SMD + FILES:= $(LINUX_DIR)/net/qrtr/qrtr-smd.ko + AUTOLOAD:=$(call AutoProbe,qrtr-smd) +endef + +define KernelPackage/qrtr-smd/description + SMD IPC Router channels +endef + +$(eval $(call KernelPackage,qrtr-smd)) + +define KernelPackage/qrtr-mhi + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=MHI IPC Router channels + DEPENDS:=+kmod-mhi-bus +kmod-qrtr + KCONFIG:=CONFIG_QRTR_MHI + FILES:= $(LINUX_DIR)/net/qrtr/qrtr-mhi.ko + AUTOLOAD:=$(call AutoProbe,qrtr-mhi) +endef + +define KernelPackage/qrtr-mhi/description + MHI IPC Router channels +endef + +$(eval $(call KernelPackage,qrtr-mhi)) diff --git a/package/kernel/linux/modules/usb.mk b/package/kernel/linux/modules/usb.mk index 19c44f805be0c2..0990c53ac4b913 100644 --- a/package/kernel/linux/modules/usb.mk +++ b/package/kernel/linux/modules/usb.mk @@ -477,7 +477,7 @@ $(eval $(call KernelPackage,usb-dwc3)) define KernelPackage/usb-dwc3-qcom TITLE:=DWC3 Qualcomm USB driver - DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x) +kmod-usb-dwc3 + DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x||TARGET_ipq807x) +kmod-usb-dwc3 KCONFIG:= CONFIG_USB_DWC3_QCOM FILES:= $(LINUX_DIR)/drivers/usb/dwc3/dwc3-qcom.ko AUTOLOAD:=$(call AutoLoad,53,dwc3-qcom,1) diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile index 3917f48f05a68c..4ac98c5c2048ae 100644 --- a/package/kernel/mac80211/Makefile +++ b/package/kernel/mac80211/Makefile @@ -12,11 +12,12 @@ PKG_NAME:=mac80211 PKG_VERSION:=5.15.33-1 PKG_RELEASE:=1 -PKG_SOURCE_URL:=@KERNEL/linux/kernel/projects/backports/stable/v5.15.33/ -PKG_HASH:=1b6b3bded4c81814ebebe2d194c2f8966d2399005b85ebb0557285b6e73f5422 +PKG_SOURCE_URL:=https://github.com/robimarko/wireless-backport-releases.git +PKG_SOURCE_PROTO:=git +PKG_SOURCE_DATE:=2022-05-21 +PKG_SOURCE_VERSION:=7aa1df4f57570a0c148165f5aa869b289bfcd72f +PKG_MIRROR_HASH:=e8b3462d26b4ec02e618a52e7d02a2b9a8d33e54e3d7719ba29defb1cf9d1c61 -PKG_SOURCE:=backports-$(PKG_VERSION).tar.xz -PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/backports-$(PKG_VERSION) PKG_BUILD_PARALLEL:=1 PKG_MAINTAINER:=Felix Fietkau diff --git a/package/kernel/mac80211/ath.mk b/package/kernel/mac80211/ath.mk index 9af8c4665d198f..91566a1d2bbaf5 100644 --- a/package/kernel/mac80211/ath.mk +++ b/package/kernel/mac80211/ath.mk @@ -1,6 +1,6 @@ PKG_DRIVERS += \ ath ath5k ath6kl ath6kl-sdio ath6kl-usb ath9k ath9k-common ath9k-htc ath10k ath10k-smallbuffers \ - carl9170 owl-loader ar5523 wil6210 + ath11k ath11k-ahb ath11k-pci carl9170 owl-loader ar5523 wil6210 PKG_CONFIG_DEPENDS += \ CONFIG_PACKAGE_ATH_DEBUG \ @@ -12,6 +12,8 @@ PKG_CONFIG_DEPENDS += \ CONFIG_ATH9K_TX99 \ CONFIG_ATH10K_LEDS \ CONFIG_ATH10K_THERMAL \ + CONFIG_ATH11K_MEM_PROFILE_512MB \ + CONFIG_ATH11K_MEM_PROFILE_1GB \ CONFIG_ATH_USER_REGD ifdef CONFIG_PACKAGE_MAC80211_DEBUGFS @@ -19,6 +21,7 @@ ifdef CONFIG_PACKAGE_MAC80211_DEBUGFS ATH9K_DEBUGFS \ ATH9K_HTC_DEBUGFS \ ATH10K_DEBUGFS \ + ATH11K_DEBUGFS \ CARL9170_DEBUGFS \ ATH5K_DEBUG \ ATH6KL_DEBUG \ @@ -28,6 +31,7 @@ endif ifdef CONFIG_PACKAGE_MAC80211_TRACING config-y += \ ATH10K_TRACING \ + ATH11K_TRACING \ ATH6KL_TRACING \ ATH_TRACEPOINTS \ ATH5K_TRACER \ @@ -35,9 +39,9 @@ ifdef CONFIG_PACKAGE_MAC80211_TRACING endif config-$(call config_package,ath) += ATH_CARDS ATH_COMMON -config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG ATH9K_STATION_STATISTICS +config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG ATH11K_DEBUG ATH9K_STATION_STATISTICS config-$(CONFIG_PACKAGE_ATH_DFS) += ATH9K_DFS_CERTIFIED ATH10K_DFS_CERTIFIED -config-$(CONFIG_PACKAGE_ATH_SPECTRAL) += ATH9K_COMMON_SPECTRAL ATH10K_SPECTRAL +config-$(CONFIG_PACKAGE_ATH_SPECTRAL) += ATH9K_COMMON_SPECTRAL ATH10K_SPECTRAL ATH11K_SPECTRAL config-$(CONFIG_PACKAGE_ATH_DYNACK) += ATH9K_DYNACK config-$(call config_package,ath9k) += ATH9K config-$(call config_package,ath9k-common) += ATH9K_COMMON @@ -52,10 +56,15 @@ config-$(CONFIG_ATH9K_TX99) += ATH9K_TX99 config-$(CONFIG_ATH9K_UBNTHSR) += ATH9K_UBNTHSR config-$(CONFIG_ATH10K_LEDS) += ATH10K_LEDS config-$(CONFIG_ATH10K_THERMAL) += ATH10K_THERMAL +config-$(CONFIG_ATH11K_MEM_PROFILE_512MB) += ATH11K_MEM_PROFILE_512MB +config-$(CONFIG_ATH11K_MEM_PROFILE_1GB) += ATH11K_MEM_PROFILE_1GB config-$(call config_package,ath9k-htc) += ATH9K_HTC config-$(call config_package,ath10k) += ATH10K ATH10K_PCI config-$(call config_package,ath10k-smallbuffers) += ATH10K ATH10K_PCI ATH10K_SMALLBUFFERS +config-$(call config_package,ath11k) += ATH11K +config-$(call config_package,ath11k-ahb) += ATH11K_AHB +config-$(call config_package,ath11k-pci) += ATH11K_PCI config-$(call config_package,ath5k) += ATH5K ifdef CONFIG_TARGET_ath25 @@ -290,6 +299,77 @@ define KernelPackage/ath10k-smallbuffers VARIANT:=smallbuffers endef +define KernelPackage/ath11k + $(call KernelPackage/mac80211/Default) + TITLE:=Qualcomm 802.11ax wireless chipset support (common code) + URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath11k + DEPENDS+= +kmod-ath +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT +@DRIVER_11AX_SUPPORT \ + +kmod-crypto-michael-mic +ATH11K_THERMAL:kmod-hwmon-core +ATH11K_THERMAL:kmod-thermal + FILES:=$(PKG_BUILD_DIR)/drivers/soc/qcom/qmi_helpers.ko \ + $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k.ko + AUTOLOAD:=$(call AutoProbe,ath11k) + # Enable encapsulation/decapsulation offload by default + MODPARAMS.ath11k:=frame_mode=2 +endef + +define KernelPackage/ath11k/description +This module adds support for Qualcomm Technologies 802.11ax family of +chipsets. +endef + +define KernelPackage/ath11k/config + + config ATH11K_THERMAL + bool "Enable thermal sensors and throttling support" + depends on PACKAGE_kmod-ath11k + default y if TARGET_ipq807x + + if PACKAGE_kmod-ath11k + + choice + prompt "ath11k memory profile" + default ATH11K_MEM_PROFILE_512MB + help + This allows selecting the ath11k memory size profile to be used. + + config ATH11K_MEM_PROFILE_512MB + bool "Use limits for the 512MB memory size" + + config ATH11K_MEM_PROFILE_1GB + bool "Use limits for the 1GB memory size" + + endchoice + endif +endef + +define KernelPackage/ath11k-ahb + $(call KernelPackage/mac80211/Default) + TITLE:=Qualcomm 802.11ax AHB wireless chipset support + URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath11k + DEPENDS+= @TARGET_ipq807x +kmod-ath11k +kmod-qrtr-smd + FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k_ahb.ko + AUTOLOAD:=$(call AutoProbe,ath11k_ahb) +endef + +define KernelPackage/ath11k-ahb/description +This module adds support for Qualcomm Technologies 802.11ax family of +chipsets with AHB bus. +endef + +define KernelPackage/ath11k-pci + $(call KernelPackage/mac80211/Default) + TITLE:=Qualcomm 802.11ax PCI wireless chipset support + URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath11k + DEPENDS+= @PCI_SUPPORT +kmod-qrtr-mhi +kmod-ath11k + FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k_pci.ko + AUTOLOAD:=$(call AutoProbe,ath11k_pci) +endef + +define KernelPackage/ath11k-pci/description +This module adds support for Qualcomm Technologies 802.11ax family of +chipsets with PCI bus. +endef + define KernelPackage/carl9170 $(call KernelPackage/mac80211/Default) TITLE:=Driver for Atheros AR9170 USB sticks diff --git a/package/kernel/mac80211/patches/ath11k/0001-ath11k-fix-4-addr-tx-failure-for-AP-and-STA-modes.patch b/package/kernel/mac80211/patches/ath11k/0001-ath11k-fix-4-addr-tx-failure-for-AP-and-STA-modes.patch new file mode 100644 index 00000000000000..69e6b43ee10145 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0001-ath11k-fix-4-addr-tx-failure-for-AP-and-STA-modes.patch @@ -0,0 +1,151 @@ +From 34c67dc366419e06129dad0f32f521842bdff9bc Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Wed, 21 Jul 2021 00:31:46 +0300 +Subject: [PATCH] ath11k: fix 4-addr tx failure for AP and STA modes + +Ath11k FW requires peer parameter WMI_PEER_USE_4ADDR to be set for +4-addr peers allowing 4-address frame transmission to those peers. + +Add ath11k driver callback for sta_set_4addr() to queue new workq +set_4addr_wk only once based on new boolean, use_4addr_set. + +sta_set_4addr() will be called during 4-addr STA association cases +applicable for both AP and STA modes. + +In ath11k_sta_set_4addr_wk(), + +AP mode: + WMI_PEER_USE_4ADDR will be set for the corresponding + associated 4-addr STA(s) + +STA mode: + WMI_PEER_USE_4ADDR will be set for the AP to which the + 4-addr STA got associated. + +Tested-on: IPQ8074 WLAN.HK.2.1.0.1-01238-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sathishkumar Muruganandam +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210720213147.90042-1-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 3 ++ + drivers/net/wireless/ath/ath11k/mac.c | 48 ++++++++++++++++++++++++-- + 2 files changed, 49 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -368,6 +368,7 @@ struct ath11k_sta { + enum hal_pn_type pn_type; + + struct work_struct update_wk; ++ struct work_struct set_4addr_wk; + struct rate_info txrate; + struct rate_info last_txrate; + u64 rx_duration; +@@ -380,6 +381,8 @@ struct ath11k_sta { + /* protected by conf_mutex */ + bool aggr_mode; + #endif ++ ++ bool use_4addr_set; + }; + + #define ATH11K_MIN_5G_FREQ 4150 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -3161,6 +3161,31 @@ static void ath11k_sta_rc_update_wk(stru + mutex_unlock(&ar->conf_mutex); + } + ++static void ath11k_sta_set_4addr_wk(struct work_struct *wk) ++{ ++ struct ath11k *ar; ++ struct ath11k_vif *arvif; ++ struct ath11k_sta *arsta; ++ struct ieee80211_sta *sta; ++ int ret = 0; ++ ++ arsta = container_of(wk, struct ath11k_sta, set_4addr_wk); ++ sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); ++ arvif = arsta->arvif; ++ ar = arvif->ar; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "setting USE_4ADDR for peer %pM\n", sta->addr); ++ ++ ret = ath11k_wmi_set_peer_param(ar, sta->addr, ++ arvif->vdev_id, ++ WMI_PEER_USE_4ADDR, 1); ++ ++ if (ret) ++ ath11k_warn(ar->ab, "failed to set peer %pM 4addr capability: %d\n", ++ sta->addr, ret); ++} ++ + static int ath11k_mac_inc_num_stations(struct ath11k_vif *arvif, + struct ieee80211_sta *sta) + { +@@ -3240,11 +3265,13 @@ static int ath11k_mac_station_add(struct + } + + if (ieee80211_vif_is_mesh(vif)) { ++ ath11k_dbg(ab, ATH11K_DBG_MAC, ++ "setting USE_4ADDR for mesh STA %pM\n", sta->addr); + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, 1); + if (ret) { +- ath11k_warn(ab, "failed to STA %pM 4addr capability: %d\n", ++ ath11k_warn(ab, "failed to set mesh STA %pM 4addr capability: %d\n", + sta->addr, ret); + goto free_tx_stats; + } +@@ -3297,8 +3324,10 @@ static int ath11k_mac_op_sta_state(struc + + /* cancel must be done outside the mutex to avoid deadlock */ + if ((old_state == IEEE80211_STA_NONE && +- new_state == IEEE80211_STA_NOTEXIST)) ++ new_state == IEEE80211_STA_NOTEXIST)) { + cancel_work_sync(&arsta->update_wk); ++ cancel_work_sync(&arsta->set_4addr_wk); ++ } + + mutex_lock(&ar->conf_mutex); + +@@ -3307,6 +3336,7 @@ static int ath11k_mac_op_sta_state(struc + memset(arsta, 0, sizeof(*arsta)); + arsta->arvif = arvif; + INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); ++ INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk); + + ret = ath11k_mac_station_add(ar, vif, sta); + if (ret) +@@ -3401,6 +3431,19 @@ out: + return ret; + } + ++static void ath11k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, bool enabled) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; ++ ++ if (enabled && !arsta->use_4addr_set) { ++ ieee80211_queue_work(ar->hw, &arsta->set_4addr_wk); ++ arsta->use_4addr_set = true; ++ } ++} ++ + static void ath11k_mac_op_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, +@@ -6190,6 +6233,7 @@ static const struct ieee80211_ops ath11k + .cancel_hw_scan = ath11k_mac_op_cancel_hw_scan, + .set_key = ath11k_mac_op_set_key, + .sta_state = ath11k_mac_op_sta_state, ++ .sta_set_4addr = ath11k_mac_op_sta_set_4addr, + .sta_set_txpwr = ath11k_mac_op_sta_set_txpwr, + .sta_rc_update = ath11k_mac_op_sta_rc_update, + .conf_tx = ath11k_mac_op_conf_tx, diff --git a/package/kernel/mac80211/patches/ath11k/0002-ath11k-fix-4addr-multicast-packet-tx.patch b/package/kernel/mac80211/patches/ath11k/0002-ath11k-fix-4addr-multicast-packet-tx.patch new file mode 100644 index 00000000000000..dba737ef742dad --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0002-ath11k-fix-4addr-multicast-packet-tx.patch @@ -0,0 +1,123 @@ +From e20cfa3b62aeb1b5fc5ffa86a007af97f9954767 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Wed, 21 Jul 2021 00:31:47 +0300 +Subject: [PATCH] ath11k: fix 4addr multicast packet tx + +In 4addr, AP wired backbone to STA wired backbone ping fails due to ARP +request not getting answered. Here 4addr ARP multicast packet is sent in +3addr, so that 4addr STA not honouring the 3addr ARP multicast packet. +Fix this issue by sending out multicast packet in 4addr format, firmware +expects peer meta flag instead of vdev meta flag in Tx descriptor. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01641-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210720213147.90042-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/dp_tx.c | 12 ++++++++++-- + drivers/net/wireless/ath/ath11k/dp_tx.h | 2 +- + drivers/net/wireless/ath/ath11k/mac.c | 6 +++++- + drivers/net/wireless/ath/ath11k/peer.c | 11 +++++++++++ + 5 files changed, 28 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -383,6 +383,7 @@ struct ath11k_sta { + #endif + + bool use_4addr_set; ++ u16 tcl_metadata; + }; + + #define ATH11K_MIN_5G_FREQ 4150 +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -78,7 +78,7 @@ enum hal_encrypt_type ath11k_dp_tx_get_e + } + + int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, +- struct sk_buff *skb) ++ struct ath11k_sta *arsta, struct sk_buff *skb) + { + struct ath11k_base *ab = ar->ab; + struct ath11k_dp *dp = &ab->dp; +@@ -145,7 +145,15 @@ tcl_ring_sel: + FIELD_PREP(DP_TX_DESC_ID_MSDU_ID, ret) | + FIELD_PREP(DP_TX_DESC_ID_POOL_ID, pool_id); + ti.encap_type = ath11k_dp_tx_get_encap_type(arvif, skb); +- ti.meta_data_flags = arvif->tcl_metadata; ++ ++ if (ieee80211_has_a4(hdr->frame_control) && ++ is_multicast_ether_addr(hdr->addr3) && arsta && ++ arsta->use_4addr_set) { ++ ti.meta_data_flags = arsta->tcl_metadata; ++ ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TO_FW, 1); ++ } else { ++ ti.meta_data_flags = arvif->tcl_metadata; ++ } + + if (ti.encap_type == HAL_TCL_ENCAP_TYPE_RAW) { + if (skb_cb->flags & ATH11K_SKB_CIPHER_SET) { +--- a/drivers/net/wireless/ath/ath11k/dp_tx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.h +@@ -17,7 +17,7 @@ struct ath11k_dp_htt_wbm_tx_status { + + int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab); + int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, +- struct sk_buff *skb); ++ struct ath11k_sta *arsta, struct sk_buff *skb); + void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id); + int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, + enum hal_reo_cmd_type type, +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4366,6 +4366,7 @@ static void ath11k_mac_op_tx(struct ieee + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_key_conf *key = info->control.hw_key; ++ struct ath11k_sta *arsta = NULL; + u32 info_flags = info->flags; + bool is_prb_rsp; + int ret; +@@ -4391,7 +4392,10 @@ static void ath11k_mac_op_tx(struct ieee + return; + } + +- ret = ath11k_dp_tx(ar, arvif, skb); ++ if (control->sta) ++ arsta = (struct ath11k_sta *)control->sta->drv_priv; ++ ++ ret = ath11k_dp_tx(ar, arvif, arsta, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->hw, skb); +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -251,6 +251,7 @@ int ath11k_peer_create(struct ath11k *ar + struct ieee80211_sta *sta, struct peer_create_params *param) + { + struct ath11k_peer *peer; ++ struct ath11k_sta *arsta; + int ret; + + lockdep_assert_held(&ar->conf_mutex); +@@ -319,6 +320,16 @@ int ath11k_peer_create(struct ath11k *ar + peer->sec_type = HAL_ENCRYPT_TYPE_OPEN; + peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; + ++ if (sta) { ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ arsta->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 0) | ++ FIELD_PREP(HTT_TCL_META_DATA_PEER_ID, ++ peer->peer_id); ++ ++ /* set HTT extension valid bit to 0 by default */ ++ arsta->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; ++ } ++ + ar->num_peers++; + + spin_unlock_bh(&ar->ab->base_lock); diff --git a/package/kernel/mac80211/patches/ath11k/0003-ath11k-Rename-atf_config-to-flag1-in-target_resource.patch b/package/kernel/mac80211/patches/ath11k/0003-ath11k-Rename-atf_config-to-flag1-in-target_resource.patch new file mode 100644 index 00000000000000..30ea23cb601a02 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0003-ath11k-Rename-atf_config-to-flag1-in-target_resource.patch @@ -0,0 +1,41 @@ +From 7e9fb2418a4c092a363d23e97973c9624150e5b2 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Wed, 21 Jul 2021 00:49:20 +0300 +Subject: [PATCH] ath11k: Rename atf_config to flag1 in target_resource_config + +The flag's purpose is not only meant for ATF configs. Rename atf_config +to flag1, so it can be used for future purposes. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01228-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210720214922.118078-1-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/wmi.c | 2 +- + drivers/net/wireless/ath/ath11k/wmi.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -3497,7 +3497,7 @@ ath11k_wmi_copy_resource_config(struct w + wmi_cfg->bpf_instruction_size = tg_cfg->bpf_instruction_size; + wmi_cfg->max_bssid_rx_filters = tg_cfg->max_bssid_rx_filters; + wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id; +- wmi_cfg->flag1 = tg_cfg->atf_config; ++ wmi_cfg->flag1 = tg_cfg->flag1; + wmi_cfg->peer_map_unmap_v2_support = tg_cfg->peer_map_unmap_v2_support; + wmi_cfg->sched_params = tg_cfg->sched_params; + wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5015,7 +5015,7 @@ struct target_resource_config { + u32 vo_minfree; + u32 rx_batchmode; + u32 tt_support; +- u32 atf_config; ++ u32 flag1; + u32 iphdr_pad_config; + u32 qwrap_config:16, + alloc_frag_desc_for_data_pkt:16; diff --git a/package/kernel/mac80211/patches/ath11k/0004-ath11k-add-support-in-survey-dump-with-bss_chan_info.patch b/package/kernel/mac80211/patches/ath11k/0004-ath11k-add-support-in-survey-dump-with-bss_chan_info.patch new file mode 100644 index 00000000000000..e2a824558514f2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0004-ath11k-add-support-in-survey-dump-with-bss_chan_info.patch @@ -0,0 +1,51 @@ +From 9b4dd38b46cf24d8cb3ab433661cdc23a35160d0 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Wed, 21 Jul 2021 00:49:21 +0300 +Subject: [PATCH] ath11k: add support in survey dump with bss_chan_info + +Survey dump statistics is not displaying channel rx and tx time because +the service flag is not enabled. Enable the service flag "bss_chan_info" +in wmi_resource_config to fetch and print the stats for the specific +pdev. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01228-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Ritesh Singh +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210720214922.118078-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/hw.c | 2 ++ + drivers/net/wireless/ath/ath11k/wmi.h | 2 ++ + 2 files changed, 4 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -97,6 +97,7 @@ static void ath11k_init_wmi_config_qca63 + config->num_multicast_filter_entries = 0x20; + config->num_wow_filters = 0x16; + config->num_keep_alive_pattern = 0; ++ config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64; + } + + static void ath11k_hw_ipq8074_reo_setup(struct ath11k_base *ab) +@@ -197,6 +198,7 @@ static void ath11k_init_wmi_config_ipq80 + config->peer_map_unmap_v2_support = 1; + config->twt_ap_pdev_count = ab->num_radios; + config->twt_ap_sta_count = 1000; ++ config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64; + } + + static int ath11k_hw_mac_id_to_pdev_id_ipq8074(struct ath11k_hw_params *hw, +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -2244,6 +2244,8 @@ struct wmi_init_cmd { + u32 num_host_mem_chunks; + } __packed; + ++#define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) ++ + struct wmi_resource_config { + u32 tlv_header; + u32 num_vdevs; diff --git a/package/kernel/mac80211/patches/ath11k/0006-ath11k-move-static-function-ath11k_mac_vdev_setup_sy.patch b/package/kernel/mac80211/patches/ath11k/0006-ath11k-move-static-function-ath11k_mac_vdev_setup_sy.patch new file mode 100644 index 00000000000000..56c221ab3454ba --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0006-ath11k-move-static-function-ath11k_mac_vdev_setup_sy.patch @@ -0,0 +1,65 @@ +From d37b4862312c980d1f6843d11a14ad4eda242c8d Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 21 Sep 2021 16:39:29 +0300 +Subject: [PATCH] ath11k: move static function ath11k_mac_vdev_setup_sync to + top + +This is to prepare for monitor mode clean up. +No functional changes are done. + +Co-developed-by: Miles Hu +Signed-off-by: Miles Hu +Co-developed-by: Vasanthakumar Thiagarajan +Signed-off-by: Vasanthakumar Thiagarajan +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721162053.46290-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 28 +++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -732,6 +732,20 @@ static int ath11k_monitor_vdev_up(struct + return 0; + } + ++static inline int ath11k_mac_vdev_setup_sync(struct ath11k *ar) ++{ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) ++ return -ESHUTDOWN; ++ ++ if (!wait_for_completion_timeout(&ar->vdev_setup_done, ++ ATH11K_VDEV_SETUP_TIMEOUT_HZ)) ++ return -ETIMEDOUT; ++ ++ return ar->last_wmi_vdev_start_status ? -EINVAL : 0; ++} ++ + static int ath11k_mac_op_config(struct ieee80211_hw *hw, u32 changed) + { + /* mac80211 requires this op to be present and that's why +@@ -5175,20 +5189,6 @@ static void ath11k_mac_op_remove_chanctx + mutex_unlock(&ar->conf_mutex); + } + +-static inline int ath11k_mac_vdev_setup_sync(struct ath11k *ar) +-{ +- lockdep_assert_held(&ar->conf_mutex); +- +- if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) +- return -ESHUTDOWN; +- +- if (!wait_for_completion_timeout(&ar->vdev_setup_done, +- ATH11K_VDEV_SETUP_TIMEOUT_HZ)) +- return -ETIMEDOUT; +- +- return ar->last_wmi_vdev_start_status ? -EINVAL : 0; +-} +- + static int + ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef, diff --git a/package/kernel/mac80211/patches/ath11k/0007-ath11k-add-separate-APIs-for-monitor-mode.patch b/package/kernel/mac80211/patches/ath11k/0007-ath11k-add-separate-APIs-for-monitor-mode.patch new file mode 100644 index 00000000000000..51944e6e61817c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0007-ath11k-add-separate-APIs-for-monitor-mode.patch @@ -0,0 +1,442 @@ +From 64e06b78a92744d43d3993ba623d2686d8f937e7 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 21 Sep 2021 16:39:29 +0300 +Subject: [PATCH] ath11k: add separate APIs for monitor mode + +Add separate APIs for monitor_vdev_create/monitor_vdev_delete +and monitor_vdev_start/monitor_vdev_stop. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01725-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Miles Hu +Signed-off-by: Miles Hu +Co-developed-by: Vasanthakumar Thiagarajan +Signed-off-by: Vasanthakumar Thiagarajan +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721162053.46290-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 5 +- + drivers/net/wireless/ath/ath11k/mac.c | 371 ++++++++++++++++++++++++- + 2 files changed, 370 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -200,6 +200,9 @@ enum ath11k_dev_flags { + + enum ath11k_monitor_flags { + ATH11K_FLAG_MONITOR_ENABLED, ++ ATH11K_FLAG_MONITOR_CONF_ENABLED, ++ ATH11K_FLAG_MONITOR_STARTED, ++ ATH11K_FLAG_MONITOR_VDEV_CREATED, + }; + + struct ath11k_vif { +@@ -494,7 +497,6 @@ struct ath11k { + u32 chan_tx_pwr; + u32 num_stations; + u32 max_num_stations; +- bool monitor_present; + /* To synchronize concurrent synchronous mac80211 callback operations, + * concurrent debugfs configuration and concurrent FW statistics events. + */ +@@ -569,6 +571,7 @@ struct ath11k { + struct ath11k_per_peer_tx_stats cached_stats; + u32 last_ppdu_id; + u32 cached_ppdu_id; ++ int monitor_vdev_id; + #ifdef CPTCFG_ATH11K_DEBUGFS + struct ath11k_debug debug; + #endif +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -746,14 +746,370 @@ static inline int ath11k_mac_vdev_setup_ + return ar->last_wmi_vdev_start_status ? -EINVAL : 0; + } + +-static int ath11k_mac_op_config(struct ieee80211_hw *hw, u32 changed) ++static void ++ath11k_mac_get_any_chandef_iter(struct ieee80211_hw *hw, ++ struct ieee80211_chanctx_conf *conf, ++ void *data) + { +- /* mac80211 requires this op to be present and that's why +- * there's an empty function, this can be extended when +- * required. +- */ ++ struct cfg80211_chan_def **def = data; ++ ++ *def = &conf->def; ++} ++ ++static int ath11k_mac_monitor_vdev_start(struct ath11k *ar, int vdev_id, ++ struct cfg80211_chan_def *chandef) ++{ ++ struct ieee80211_channel *channel; ++ struct wmi_vdev_start_req_arg arg = {}; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ channel = chandef->chan; ++ ++ arg.vdev_id = vdev_id; ++ arg.channel.freq = channel->center_freq; ++ arg.channel.band_center_freq1 = chandef->center_freq1; ++ arg.channel.band_center_freq2 = chandef->center_freq2; ++ ++ arg.channel.mode = ath11k_phymodes[chandef->chan->band][chandef->width]; ++ arg.channel.chan_radar = !!(channel->flags & IEEE80211_CHAN_RADAR); ++ ++ arg.channel.min_power = 0; ++ arg.channel.max_power = channel->max_power * 2; ++ arg.channel.max_reg_power = channel->max_reg_power * 2; ++ arg.channel.max_antenna_gain = channel->max_antenna_gain * 2; ++ ++ arg.pref_tx_streams = ar->num_tx_chains; ++ arg.pref_rx_streams = ar->num_rx_chains; ++ ++ arg.channel.passive = !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR); ++ ++ reinit_completion(&ar->vdev_setup_done); ++ reinit_completion(&ar->vdev_delete_done); ++ ++ ret = ath11k_wmi_vdev_start(ar, &arg, false); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to request monitor vdev %i start: %d\n", ++ vdev_id, ret); ++ return ret; ++ } ++ ++ ret = ath11k_mac_vdev_setup_sync(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to synchronize setup for monitor vdev %i start: %d\n", ++ vdev_id, ret); ++ return ret; ++ } ++ ++ ret = ath11k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n", ++ vdev_id, ret); ++ goto vdev_stop; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor vdev %i started\n", ++ vdev_id); + + return 0; ++ ++vdev_stop: ++ reinit_completion(&ar->vdev_setup_done); ++ ++ ret = ath11k_wmi_vdev_stop(ar, vdev_id); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to stop monitor vdev %i after start failure: %d\n", ++ vdev_id, ret); ++ return ret; ++ } ++ ++ ret = ath11k_mac_vdev_setup_sync(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to synchronize setup for vdev %i stop: %d\n", ++ vdev_id, ret); ++ return ret; ++ } ++ ++ return -EIO; ++} ++ ++static int ath11k_mac_monitor_vdev_stop(struct ath11k *ar) ++{ ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ reinit_completion(&ar->vdev_setup_done); ++ ++ ret = ath11k_wmi_vdev_stop(ar, ar->monitor_vdev_id); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to request monitor vdev %i stop: %d\n", ++ ar->monitor_vdev_id, ret); ++ return ret; ++ } ++ ++ ret = ath11k_mac_vdev_setup_sync(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to synchronize monitor vdev %i stop: %d\n", ++ ar->monitor_vdev_id, ret); ++ return ret; ++ } ++ ++ ret = ath11k_wmi_vdev_down(ar, ar->monitor_vdev_id); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to put down monitor vdev %i: %d\n", ++ ar->monitor_vdev_id, ret); ++ return ret; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor vdev %i stopped\n", ++ ar->monitor_vdev_id); ++ ++ return 0; ++} ++ ++static int ath11k_mac_monitor_vdev_create(struct ath11k *ar) ++{ ++ struct ath11k_pdev *pdev = ar->pdev; ++ struct vdev_create_params param = {}; ++ int bit, ret; ++ u8 tmp_addr[6] = {0}; ++ u16 nss; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ if (test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) ++ return 0; ++ ++ if (ar->ab->free_vdev_map == 0) { ++ ath11k_warn(ar->ab, "failed to find free vdev id for monitor vdev\n"); ++ return -ENOMEM; ++ } ++ ++ bit = __ffs64(ar->ab->free_vdev_map); ++ ++ ar->monitor_vdev_id = bit; ++ ++ param.if_id = ar->monitor_vdev_id; ++ param.type = WMI_VDEV_TYPE_MONITOR; ++ param.subtype = WMI_VDEV_SUBTYPE_NONE; ++ param.pdev_id = pdev->pdev_id; ++ ++ if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { ++ param.chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains; ++ param.chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains; ++ } ++ if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { ++ param.chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains; ++ param.chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains; ++ } ++ ++ ret = ath11k_wmi_vdev_create(ar, tmp_addr, ¶m); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to request monitor vdev %i creation: %d\n", ++ ar->monitor_vdev_id, ret); ++ ar->monitor_vdev_id = -1; ++ return ret; ++ } ++ ++ nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1; ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, ar->monitor_vdev_id, ++ WMI_VDEV_PARAM_NSS, nss); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set vdev %d chainmask 0x%x, nss %d :%d\n", ++ ar->monitor_vdev_id, ar->cfg_tx_chainmask, nss, ret); ++ goto err_vdev_del; ++ } ++ ++ ret = ath11k_mac_txpower_recalc(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to recalc txpower for monitor vdev %d: %d\n", ++ ar->monitor_vdev_id, ret); ++ goto err_vdev_del; ++ } ++ ++ ar->allocated_vdev_map |= 1LL << ar->monitor_vdev_id; ++ ar->ab->free_vdev_map &= ~(1LL << ar->monitor_vdev_id); ++ ar->num_created_vdevs++; ++ set_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor vdev %d created\n", ++ ar->monitor_vdev_id); ++ ++ return 0; ++ ++err_vdev_del: ++ ath11k_wmi_vdev_delete(ar, ar->monitor_vdev_id); ++ ar->monitor_vdev_id = -1; ++ return ret; ++} ++ ++static int ath11k_mac_monitor_vdev_delete(struct ath11k *ar) ++{ ++ int ret; ++ unsigned long time_left; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ if (!test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) ++ return 0; ++ ++ reinit_completion(&ar->vdev_delete_done); ++ ++ ret = ath11k_wmi_vdev_delete(ar, ar->monitor_vdev_id); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to request wmi monitor vdev %i removal: %d\n", ++ ar->monitor_vdev_id, ret); ++ return ret; ++ } ++ ++ time_left = wait_for_completion_timeout(&ar->vdev_delete_done, ++ ATH11K_VDEV_DELETE_TIMEOUT_HZ); ++ if (time_left == 0) { ++ ath11k_warn(ar->ab, "Timeout in receiving vdev delete response\n"); ++ } else { ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor vdev %d deleted\n", ++ ar->monitor_vdev_id); ++ ++ ar->allocated_vdev_map &= ~(1LL << ar->monitor_vdev_id); ++ ar->ab->free_vdev_map |= 1LL << (ar->monitor_vdev_id); ++ ar->num_created_vdevs--; ++ ar->monitor_vdev_id = -1; ++ clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ++ } ++ ++ return ret; ++} ++ ++static int ath11k_mac_monitor_start(struct ath11k *ar) ++{ ++ struct cfg80211_chan_def *chandef = NULL; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) ++ return 0; ++ ++ ieee80211_iter_chan_contexts_atomic(ar->hw, ++ ath11k_mac_get_any_chandef_iter, ++ &chandef); ++ if (!chandef) ++ return 0; ++ ++ ret = ath11k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id, chandef); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start monitor vdev: %d\n", ret); ++ ath11k_mac_monitor_vdev_delete(ar); ++ return ret; ++ } ++ ++ set_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags); ++ ++ ar->num_started_vdevs++; ++ ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, false); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to configure htt monitor mode ring during start: %d", ++ ret); ++ return ret; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor started\n"); ++ ++ return 0; ++} ++ ++static int ath11k_mac_monitor_stop(struct ath11k *ar) ++{ ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ if (!test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) ++ return 0; ++ ++ ret = ath11k_mac_monitor_vdev_stop(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to stop monitor vdev: %d\n", ret); ++ return ret; ++ } ++ ++ clear_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags); ++ ar->num_started_vdevs--; ++ ++ ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, true); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to configure htt monitor mode ring during stop: %d", ++ ret); ++ return ret; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor stopped ret %d\n", ret); ++ ++ return 0; ++} ++ ++static int ath11k_mac_op_config(struct ieee80211_hw *hw, u32 changed) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ieee80211_conf *conf = &hw->conf; ++ int ret = 0; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { ++ if (conf->flags & IEEE80211_CONF_MONITOR) { ++ set_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags); ++ ++ if (test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, ++ &ar->monitor_flags)) ++ goto out; ++ ++ ret = ath11k_mac_monitor_vdev_create(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to create monitor vdev: %d", ++ ret); ++ goto out; ++ } ++ ++ ret = ath11k_mac_monitor_start(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start monitor: %d", ++ ret); ++ goto err_mon_del; ++ } ++ } else { ++ clear_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags); ++ ++ if (!test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, ++ &ar->monitor_flags)) ++ goto out; ++ ++ ret = ath11k_mac_monitor_stop(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to stop monitor: %d", ++ ret); ++ goto out; ++ } ++ ++ ret = ath11k_mac_monitor_vdev_delete(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to delete monitor vdev: %d", ++ ret); ++ goto out; ++ } ++ } ++ } ++ ++out: ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++ ++err_mon_del: ++ ath11k_mac_monitor_vdev_delete(ar); ++ mutex_unlock(&ar->conf_mutex); ++ return ret; + } + + static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) +@@ -6781,7 +7137,12 @@ int ath11k_mac_allocate(struct ath11k_ba + + INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); ++ + clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); ++ clear_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags); ++ ++ ar->monitor_vdev_id = -1; ++ clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); + } + + return 0; diff --git a/package/kernel/mac80211/patches/ath11k/0008-ath11k-monitor-mode-clean-up-to-use-separate-APIs.patch b/package/kernel/mac80211/patches/ath11k/0008-ath11k-monitor-mode-clean-up-to-use-separate-APIs.patch new file mode 100644 index 00000000000000..b241aacc003a4e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0008-ath11k-monitor-mode-clean-up-to-use-separate-APIs.patch @@ -0,0 +1,370 @@ +From 689a5e6fff75229ac7c2af7a9c51dc2d3ca1882b Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 21 Sep 2021 16:39:30 +0300 +Subject: [PATCH] ath11k: monitor mode clean up to use separate APIs + +If monitor interface is enabled in co-exist mode, only local traffic are +captured. It's caused by missing monitor vdev in co-exist mode. So, +monitor mode clean up is done with separate Monitor APIs. For this, +introduce flags monitor_started and monitor_vdev_created. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01725-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Miles Hu +Signed-off-by: Miles Hu +Co-developed-by: Vasanthakumar Thiagarajan +Signed-off-by: Vasanthakumar Thiagarajan +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721162053.46290-4-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 1 - + drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +- + drivers/net/wireless/ath/ath11k/dp_tx.c | 8 +- + drivers/net/wireless/ath/ath11k/mac.c | 150 ++++++++++++++++-------- + 4 files changed, 110 insertions(+), 51 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -199,7 +199,6 @@ enum ath11k_dev_flags { + }; + + enum ath11k_monitor_flags { +- ATH11K_FLAG_MONITOR_ENABLED, + ATH11K_FLAG_MONITOR_CONF_ENABLED, + ATH11K_FLAG_MONITOR_STARTED, + ATH11K_FLAG_MONITOR_VDEV_CREATED, +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -5032,7 +5032,7 @@ int ath11k_dp_rx_process_mon_rings(struc + struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); + int ret = 0; + +- if (test_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags)) ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) + ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget); + else + ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -1076,12 +1076,16 @@ int ath11k_dp_tx_htt_monitor_mode_ring_c + + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; +- if (!reset) ++ if (!reset) { + tlv_filter.rx_filter = + HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; +- else ++ } else { + tlv_filter = ath11k_mac_mon_status_filter_default; + ++ if (ath11k_debugfs_is_extd_rx_stats_enabled(ar)) ++ tlv_filter.rx_filter = ath11k_debugfs_rx_filter(ar); ++ } ++ + ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id, + dp->mac_id + i, + HAL_RXDMA_MONITOR_STATUS, +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -716,22 +716,6 @@ void ath11k_mac_peer_cleanup_all(struct + ar->num_stations = 0; + } + +-static int ath11k_monitor_vdev_up(struct ath11k *ar, int vdev_id) +-{ +- int ret = 0; +- +- ret = ath11k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); +- if (ret) { +- ath11k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n", +- vdev_id, ret); +- return ret; +- } +- +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor vdev %i started\n", +- vdev_id); +- return 0; +-} +- + static inline int ath11k_mac_vdev_setup_sync(struct ath11k *ar) + { + lockdep_assert_held(&ar->conf_mutex); +@@ -2331,7 +2315,7 @@ static int ath11k_mac_config_obss_pd(str + + /* Set and enable SRG/non-SRG OBSS PD Threshold */ + param_id = WMI_PDEV_PARAM_SET_CMD_OBSS_PD_THRESHOLD; +- if (test_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags)) { ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) { + ret = ath11k_wmi_pdev_set_param(ar, param_id, 0, pdev_id); + if (ret) + ath11k_warn(ar->ab, +@@ -5110,8 +5094,8 @@ static int ath11k_mac_op_add_interface(s + } + + if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) { +- ath11k_warn(ab, "failed to create vdev, reached max vdev limit %d\n", +- TARGET_NUM_VDEVS); ++ ath11k_warn(ab, "failed to create vdev %u, reached max vdev limit %d\n", ++ ar->num_created_vdevs, TARGET_NUM_VDEVS); + ret = -EBUSY; + goto err; + } +@@ -5151,6 +5135,7 @@ static int ath11k_mac_op_add_interface(s + break; + case NL80211_IFTYPE_MONITOR: + arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; ++ ar->monitor_vdev_id = bit; + break; + default: + WARN_ON(1); +@@ -5252,6 +5237,9 @@ static int ath11k_mac_op_add_interface(s + goto err_peer_del; + } + break; ++ case WMI_VDEV_TYPE_MONITOR: ++ set_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ++ break; + default: + break; + } +@@ -5272,6 +5260,16 @@ static int ath11k_mac_op_add_interface(s + + ath11k_dp_vdev_tx_attach(ar, arvif); + ++ if (vif->type != NL80211_IFTYPE_MONITOR && ++ test_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags)) { ++ ret = ath11k_mac_monitor_vdev_create(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to create monitor vdev during add interface: %d", ++ ret); ++ goto err_peer_del; ++ } ++ } ++ + mutex_unlock(&ar->conf_mutex); + + return 0; +@@ -5369,6 +5367,18 @@ static void ath11k_mac_op_remove_interfa + ath11k_dbg(ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", + vif->addr, arvif->vdev_id); + ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ++ clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ++ ar->monitor_vdev_id = -1; ++ } else if (test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags) && ++ !test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) { ++ ret = ath11k_mac_monitor_vdev_delete(ar); ++ if (ret) ++ /* continue even if there's an error */ ++ ath11k_warn(ar->ab, "failed to delete vdev monitor during remove interface: %d", ++ ret); ++ } ++ + err_vdev_del: + spin_lock_bh(&ar->data_lock); + list_del(&arvif->list); +@@ -5388,7 +5398,6 @@ err_vdev_del: + + /* Recalc txpower for remaining vdev */ + ath11k_mac_txpower_recalc(ar); +- clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + + /* TODO: recal traffic pause state based on the available vdevs */ + +@@ -5411,8 +5420,6 @@ static void ath11k_mac_op_configure_filt + u64 multicast) + { + struct ath11k *ar = hw->priv; +- bool reset_flag = false; +- int ret = 0; + + mutex_lock(&ar->conf_mutex); + +@@ -5420,23 +5427,6 @@ static void ath11k_mac_op_configure_filt + *total_flags &= SUPPORTED_FILTERS; + ar->filter_flags = *total_flags; + +- /* For monitor mode */ +- reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC); +- +- ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, reset_flag); +- if (!ret) { +- if (!reset_flag) +- set_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); +- else +- clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); +- } else { +- ath11k_warn(ar->ab, +- "fail to set monitor filter: %d\n", ret); +- } +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, +- "changed_flags:0x%x, total_flags:0x%x, reset_flag:%d\n", +- changed_flags, *total_flags, reset_flag); +- + mutex_unlock(&ar->conf_mutex); + } + +@@ -5627,7 +5617,9 @@ ath11k_mac_vdev_start_restart(struct ath + return ret; + } + +- ar->num_started_vdevs++; ++ if (!restart) ++ ar->num_started_vdevs++; ++ + ath11k_dbg(ab, ATH11K_DBG_MAC, "vdev %pM started, vdev_id %d\n", + arvif->vif->addr, arvif->vdev_id); + +@@ -5755,12 +5747,16 @@ ath11k_mac_update_vif_chan(struct ath11k + struct ath11k_vif *arvif; + int ret; + int i; ++ bool monitor_vif = false; + + lockdep_assert_held(&ar->conf_mutex); + + for (i = 0; i < n_vifs; i++) { + arvif = (void *)vifs[i].vif->drv_priv; + ++ if (vifs[i].vif->type == NL80211_IFTYPE_MONITOR) ++ monitor_vif = true; ++ + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", + arvif->vdev_id, +@@ -5781,6 +5777,8 @@ ath11k_mac_update_vif_chan(struct ath11k + arvif->vdev_id, ret); + continue; + } ++ ++ ar->num_started_vdevs--; + } + + /* All relevant vdevs are downed and associated channel resources +@@ -5818,6 +5816,24 @@ ath11k_mac_update_vif_chan(struct ath11k + continue; + } + } ++ ++ /* Restart the internal monitor vdev on new channel */ ++ if (!monitor_vif && ++ test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) { ++ ret = ath11k_mac_monitor_stop(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to stop monitor during vif channel update: %d", ++ ret); ++ return; ++ } ++ ++ ret = ath11k_mac_monitor_start(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start monitor during vif channel update: %d", ++ ret); ++ return; ++ } ++ } + } + + static void +@@ -5897,7 +5913,7 @@ static int ath11k_start_vdev_delay(struc + } + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { +- ret = ath11k_monitor_vdev_up(ar, arvif->vdev_id); ++ ret = ath11k_wmi_vdev_up(ar, arvif->vdev_id, 0, ar->mac_addr); + if (ret) { + ath11k_warn(ab, "failed put monitor up: %d\n", ret); + return ret; +@@ -5957,6 +5973,18 @@ ath11k_mac_op_assign_vif_chanctx(struct + } + } + ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ++ ret = ath11k_mac_monitor_start(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start monitor during vif channel context assignment: %d", ++ ret); ++ goto out; ++ } ++ ++ arvif->is_started = true; ++ goto out; ++ } ++ + ret = ath11k_mac_vdev_start(arvif, &ctx->def); + if (ret) { + ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", +@@ -5964,14 +5992,19 @@ ath11k_mac_op_assign_vif_chanctx(struct + ctx->def.chan->center_freq, ret); + goto out; + } +- if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { +- ret = ath11k_monitor_vdev_up(ar, arvif->vdev_id); +- if (ret) +- goto out; +- } + + arvif->is_started = true; + ++ if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && ++ test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) { ++ ret = ath11k_mac_monitor_start(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start monitor during vif channel context assignment: %d", ++ ret); ++ goto out; ++ } ++ } ++ + /* TODO: Setup ps and cts/rts protection */ + + ret = 0; +@@ -6005,6 +6038,20 @@ ath11k_mac_op_unassign_vif_chanctx(struc + ath11k_peer_find_by_addr(ab, ar->mac_addr)) + ath11k_peer_delete(ar, arvif->vdev_id, ar->mac_addr); + ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ++ ret = ath11k_mac_monitor_stop(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to stop monitor during vif channel context unassignment: %d", ++ ret); ++ mutex_unlock(&ar->conf_mutex); ++ return; ++ } ++ ++ arvif->is_started = false; ++ mutex_unlock(&ar->conf_mutex); ++ return; ++ } ++ + ret = ath11k_mac_vdev_stop(arvif); + if (ret) + ath11k_warn(ab, "failed to stop vdev %i: %d\n", +@@ -6016,6 +6063,16 @@ ath11k_mac_op_unassign_vif_chanctx(struc + arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + ath11k_wmi_vdev_down(ar, arvif->vdev_id); + ++ if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && ++ ar->num_started_vdevs == 1 && ++ test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) { ++ ret = ath11k_mac_monitor_stop(ar); ++ if (ret) ++ /* continue even if there's an error */ ++ ath11k_warn(ar->ab, "failed to stop monitor during vif channel context unassignment: %d", ++ ret); ++ } ++ + mutex_unlock(&ar->conf_mutex); + } + +@@ -7138,7 +7195,6 @@ int ath11k_mac_allocate(struct ath11k_ba + INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + +- clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + clear_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags); + + ar->monitor_vdev_id = -1; diff --git a/package/kernel/mac80211/patches/ath11k/0009-ath11k-add-support-for-setting-fixed-HE-rate-gi-ltf.patch b/package/kernel/mac80211/patches/ath11k/0009-ath11k-add-support-for-setting-fixed-HE-rate-gi-ltf.patch new file mode 100644 index 00000000000000..78c06b92ba7c58 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0009-ath11k-add-support-for-setting-fixed-HE-rate-gi-ltf.patch @@ -0,0 +1,1020 @@ +From 61fe43e7216df6e9a912d831aafc7142fa20f280 Mon Sep 17 00:00:00 2001 +From: Miles Hu +Date: Fri, 24 Sep 2021 16:52:45 +0300 +Subject: [PATCH] ath11k: add support for setting fixed HE rate/gi/ltf + +Support setting fixed HE rate/gi/ltf values that we are now able to send +to the kernel using nl80211. The added code is reusing parts of the +existing code path already used for HT/VHT. The new helpers are +symmetric to how we do it for HT/VHT. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00235-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Miles Hu +Co-developed-by: Aloka Dixit +Signed-off-by: Aloka Dixit +Co-developed-by: Lavanya Suresh +Signed-off-by: Lavanya Suresh +Co-developed-by: Pradeep Chitrapu +Signed-off-by: Pradeep Chitrapu +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721173615.75637-1-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 595 ++++++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/wmi.c | 4 +- + drivers/net/wireless/ath/ath11k/wmi.h | 22 + + 3 files changed, 580 insertions(+), 41 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -355,6 +355,18 @@ ath11k_mac_max_vht_nss(const u16 vht_mcs + return 1; + } + ++static u32 ++ath11k_mac_max_he_nss(const u16 he_mcs_mask[NL80211_HE_NSS_MAX]) ++{ ++ int nss; ++ ++ for (nss = NL80211_HE_NSS_MAX - 1; nss >= 0; nss--) ++ if (he_mcs_mask[nss]) ++ return nss + 1; ++ ++ return 1; ++} ++ + static u8 ath11k_parse_mpdudensity(u8 mpdudensity) + { + /* 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": +@@ -1452,6 +1464,14 @@ static void ath11k_peer_assoc_h_ht(struc + arg->peer_rate_caps |= WMI_HOST_RC_CW40_FLAG; + } + ++ /* As firmware handles this two flags (IEEE80211_HT_CAP_SGI_20 ++ * and IEEE80211_HT_CAP_SGI_40) for enabling SGI, we reset ++ * both flags if guard interval is Default GI ++ */ ++ if (arvif->bitrate_mask.control[band].gi == NL80211_TXRATE_DEFAULT_GI) ++ arg->peer_ht_caps &= ~(IEEE80211_HT_CAP_SGI_20 | ++ IEEE80211_HT_CAP_SGI_40); ++ + if (arvif->bitrate_mask.control[band].gi != NL80211_TXRATE_FORCE_LGI) { + if (ht_cap->cap & (IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40)) +@@ -1575,10 +1595,11 @@ static void ath11k_peer_assoc_h_vht(stru + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + enum nl80211_band band; +- const u16 *vht_mcs_mask; ++ u16 *vht_mcs_mask; + u8 ampdu_factor; + u8 max_nss, vht_mcs; +- int i; ++ int i, vht_nss, nss_idx; ++ bool user_rate_valid = true; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; +@@ -1621,6 +1642,24 @@ static void ath11k_peer_assoc_h_vht(stru + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) + arg->bw_160 = true; + ++ vht_nss = ath11k_mac_max_vht_nss(vht_mcs_mask); ++ ++ if (vht_nss > sta->rx_nss) { ++ user_rate_valid = false; ++ for (nss_idx = sta->rx_nss - 1; nss_idx >= 0; nss_idx--) { ++ if (vht_mcs_mask[nss_idx]) { ++ user_rate_valid = true; ++ break; ++ } ++ } ++ } ++ ++ if (!user_rate_valid) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac setting vht range mcs value to peer supported nss %d for peer %pM\n", ++ sta->rx_nss, sta->addr); ++ vht_mcs_mask[sta->rx_nss - 1] = vht_mcs_mask[vht_nss - 1]; ++ } ++ + /* Calculate peer NSS capability from VHT capabilities if STA + * supports VHT. + */ +@@ -1659,18 +1698,100 @@ static void ath11k_peer_assoc_h_vht(stru + /* TODO: rxnss_override */ + } + ++static int ath11k_mac_get_max_he_mcs_map(u16 mcs_map, int nss) ++{ ++ switch ((mcs_map >> (2 * nss)) & 0x3) { ++ case IEEE80211_HE_MCS_SUPPORT_0_7: return BIT(8) - 1; ++ case IEEE80211_HE_MCS_SUPPORT_0_9: return BIT(10) - 1; ++ case IEEE80211_HE_MCS_SUPPORT_0_11: return BIT(12) - 1; ++ } ++ return 0; ++} ++ ++static u16 ath11k_peer_assoc_h_he_limit(u16 tx_mcs_set, ++ const u16 he_mcs_limit[NL80211_HE_NSS_MAX]) ++{ ++ int idx_limit; ++ int nss; ++ u16 mcs_map; ++ u16 mcs; ++ ++ for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) { ++ mcs_map = ath11k_mac_get_max_he_mcs_map(tx_mcs_set, nss) & ++ he_mcs_limit[nss]; ++ ++ if (mcs_map) ++ idx_limit = fls(mcs_map) - 1; ++ else ++ idx_limit = -1; ++ ++ switch (idx_limit) { ++ case 0 ... 7: ++ mcs = IEEE80211_HE_MCS_SUPPORT_0_7; ++ break; ++ case 8: ++ case 9: ++ mcs = IEEE80211_HE_MCS_SUPPORT_0_9; ++ break; ++ case 10: ++ case 11: ++ mcs = IEEE80211_HE_MCS_SUPPORT_0_11; ++ break; ++ default: ++ WARN_ON(1); ++ fallthrough; ++ case -1: ++ mcs = IEEE80211_HE_MCS_NOT_SUPPORTED; ++ break; ++ } ++ ++ tx_mcs_set &= ~(0x3 << (nss * 2)); ++ tx_mcs_set |= mcs << (nss * 2); ++ } ++ ++ return tx_mcs_set; ++} ++ ++static bool ++ath11k_peer_assoc_h_he_masked(const u16 he_mcs_mask[NL80211_HE_NSS_MAX]) ++{ ++ int nss; ++ ++ for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) ++ if (he_mcs_mask[nss]) ++ return false; ++ ++ return true; ++} ++ + static void ath11k_peer_assoc_h_he(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) + { ++ struct ath11k_vif *arvif = (void *)vif->drv_priv; ++ struct cfg80211_chan_def def; + const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; + u8 ampdu_factor; +- u16 v; ++ enum nl80211_band band; ++ u16 *he_mcs_mask; ++ u8 max_nss, he_mcs; ++ u16 he_tx_mcs = 0, v = 0; ++ int i, he_nss, nss_idx; ++ bool user_rate_valid = true; ++ ++ if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) ++ return; + + if (!he_cap->has_he) + return; + ++ band = def.chan->band; ++ he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs; ++ ++ if (ath11k_peer_assoc_h_he_masked(he_mcs_mask)) ++ return; ++ + arg->he_flag = true; + + memcpy_and_pad(&arg->peer_he_cap_macinfo, +@@ -1747,25 +1868,48 @@ static void ath11k_peer_assoc_h_he(struc + if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_REQ) + arg->twt_requester = true; + ++ he_nss = ath11k_mac_max_he_nss(he_mcs_mask); ++ ++ if (he_nss > sta->rx_nss) { ++ user_rate_valid = false; ++ for (nss_idx = sta->rx_nss - 1; nss_idx >= 0; nss_idx--) { ++ if (he_mcs_mask[nss_idx]) { ++ user_rate_valid = true; ++ break; ++ } ++ } ++ } ++ ++ if (!user_rate_valid) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac setting he range mcs value to peer supported nss %d for peer %pM\n", ++ sta->rx_nss, sta->addr); ++ he_mcs_mask[sta->rx_nss - 1] = he_mcs_mask[he_nss - 1]; ++ } ++ + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80p80); ++ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80p80); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v; + + arg->peer_he_mcs_count++; ++ he_tx_mcs = v; + } + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160); ++ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; + + arg->peer_he_mcs_count++; ++ if (!he_tx_mcs) ++ he_tx_mcs = v; + fallthrough; + + default: +@@ -1773,11 +1917,34 @@ static void ath11k_peer_assoc_h_he(struc + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); ++ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; + + arg->peer_he_mcs_count++; ++ if (!he_tx_mcs) ++ he_tx_mcs = v; + break; + } ++ ++ /* Calculate peer NSS capability from HE capabilities if STA ++ * supports HE. ++ */ ++ for (i = 0, max_nss = 0, he_mcs = 0; i < NL80211_HE_NSS_MAX; i++) { ++ he_mcs = he_tx_mcs >> (2 * i) & 3; ++ ++ /* In case of fixed rates, MCS Range in he_tx_mcs might have ++ * unsupported range, with he_mcs_mask set, so check either of them ++ * to find nss. ++ */ ++ if (he_mcs != IEEE80211_HE_MCS_NOT_SUPPORTED || ++ he_mcs_mask[i]) ++ max_nss = i + 1; ++ } ++ arg->peer_nss = min(sta->rx_nss, max_nss); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac he peer %pM nss %d mcs cnt %d\n", ++ sta->addr, arg->peer_nss, arg->peer_he_mcs_count); + } + + static void ath11k_peer_assoc_h_smps(struct ieee80211_sta *sta, +@@ -1980,6 +2147,7 @@ static void ath11k_peer_assoc_h_phymode( + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; ++ const u16 *he_mcs_mask; + enum wmi_phy_mode phymode = MODE_UNKNOWN; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) +@@ -1988,10 +2156,12 @@ static void ath11k_peer_assoc_h_phymode( + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; ++ he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs; + + switch (band) { + case NL80211_BAND_2GHZ: +- if (sta->he_cap.has_he) { ++ if (sta->he_cap.has_he && ++ !ath11k_peer_assoc_h_he_masked(he_mcs_mask)) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + phymode = MODE_11AX_HE80_2G; + else if (sta->bandwidth == IEEE80211_STA_RX_BW_40) +@@ -2019,7 +2189,8 @@ static void ath11k_peer_assoc_h_phymode( + case NL80211_BAND_5GHZ: + case NL80211_BAND_6GHZ: + /* Check HE first */ +- if (sta->he_cap.has_he) { ++ if (sta->he_cap.has_he && ++ !ath11k_peer_assoc_h_he_masked(he_mcs_mask)) { + phymode = ath11k_mac_get_phymode_he(ar, sta); + } else if (sta->vht_cap.vht_supported && + !ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) { +@@ -3247,6 +3418,20 @@ ath11k_mac_bitrate_mask_num_vht_rates(st + } + + static int ++ath11k_mac_bitrate_mask_num_he_rates(struct ath11k *ar, ++ enum nl80211_band band, ++ const struct cfg80211_bitrate_mask *mask) ++{ ++ int num_rates = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++) ++ num_rates += hweight16(mask->control[band].he_mcs[i]); ++ ++ return num_rates; ++} ++ ++static int + ath11k_mac_set_peer_vht_fixed_rate(struct ath11k_vif *arvif, + struct ieee80211_sta *sta, + const struct cfg80211_bitrate_mask *mask, +@@ -3274,6 +3459,10 @@ ath11k_mac_set_peer_vht_fixed_rate(struc + return -EINVAL; + } + ++ /* Avoid updating invalid nss as fixed rate*/ ++ if (nss > sta->rx_nss) ++ return -EINVAL; ++ + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Setting Fixed VHT Rate for peer %pM. Device will not switch to any other selected rates", + sta->addr); +@@ -3292,6 +3481,57 @@ ath11k_mac_set_peer_vht_fixed_rate(struc + return ret; + } + ++static int ++ath11k_mac_set_peer_he_fixed_rate(struct ath11k_vif *arvif, ++ struct ieee80211_sta *sta, ++ const struct cfg80211_bitrate_mask *mask, ++ enum nl80211_band band) ++{ ++ struct ath11k *ar = arvif->ar; ++ u8 he_rate, nss; ++ u32 rate_code; ++ int ret, i; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ nss = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++) { ++ if (hweight16(mask->control[band].he_mcs[i]) == 1) { ++ nss = i + 1; ++ he_rate = ffs(mask->control[band].he_mcs[i]) - 1; ++ } ++ } ++ ++ if (!nss) { ++ ath11k_warn(ar->ab, "No single he fixed rate found to set for %pM", ++ sta->addr); ++ return -EINVAL; ++ } ++ ++ /* Avoid updating invalid nss as fixed rate */ ++ if (nss > sta->rx_nss) ++ return -EINVAL; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac setting fixed he rate for peer %pM, device will not switch to any other selected rates", ++ sta->addr); ++ ++ rate_code = ATH11K_HW_RATE_CODE(he_rate, nss - 1, ++ WMI_RATE_PREAMBLE_HE); ++ ++ ret = ath11k_wmi_set_peer_param(ar, sta->addr, ++ arvif->vdev_id, ++ WMI_PEER_PARAM_FIXED_RATE, ++ rate_code); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "failed to update sta %pM fixed rate %d: %d\n", ++ sta->addr, rate_code, ret); ++ ++ return ret; ++} ++ + static int ath11k_station_assoc(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, +@@ -3303,7 +3543,7 @@ static int ath11k_station_assoc(struct a + struct cfg80211_chan_def def; + enum nl80211_band band; + struct cfg80211_bitrate_mask *mask; +- u8 num_vht_rates; ++ u8 num_vht_rates, num_he_rates; + + lockdep_assert_held(&ar->conf_mutex); + +@@ -3329,9 +3569,10 @@ static int ath11k_station_assoc(struct a + } + + num_vht_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask); ++ num_he_rates = ath11k_mac_bitrate_mask_num_he_rates(ar, band, mask); + +- /* If single VHT rate is configured (by set_bitrate_mask()), +- * peer_assoc will disable VHT. This is now enabled by a peer specific ++ /* If single VHT/HE rate is configured (by set_bitrate_mask()), ++ * peer_assoc will disable VHT/HE. This is now enabled by a peer specific + * fixed param. + * Note that all other rates and NSS will be disabled for this peer. + */ +@@ -3340,6 +3581,11 @@ static int ath11k_station_assoc(struct a + band); + if (ret) + return ret; ++ } else if (sta->he_cap.has_he && num_he_rates == 1) { ++ ret = ath11k_mac_set_peer_he_fixed_rate(arvif, sta, mask, ++ band); ++ if (ret) ++ return ret; + } + + /* Re-assoc is run only to update supported rates for given station. It +@@ -3410,8 +3656,9 @@ static void ath11k_sta_rc_update_wk(stru + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; ++ const u16 *he_mcs_mask; + u32 changed, bw, nss, smps; +- int err, num_vht_rates; ++ int err, num_vht_rates, num_he_rates; + const struct cfg80211_bitrate_mask *mask; + struct peer_assoc_params peer_arg; + +@@ -3426,6 +3673,7 @@ static void ath11k_sta_rc_update_wk(stru + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; ++ he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs; + + spin_lock_bh(&ar->data_lock); + +@@ -3441,8 +3689,9 @@ static void ath11k_sta_rc_update_wk(stru + mutex_lock(&ar->conf_mutex); + + nss = max_t(u32, 1, nss); +- nss = min(nss, max(ath11k_mac_max_ht_nss(ht_mcs_mask), +- ath11k_mac_max_vht_nss(vht_mcs_mask))); ++ nss = min(nss, max(max(ath11k_mac_max_ht_nss(ht_mcs_mask), ++ ath11k_mac_max_vht_nss(vht_mcs_mask)), ++ ath11k_mac_max_he_nss(he_mcs_mask))); + + if (changed & IEEE80211_RC_BW_CHANGED) { + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, +@@ -3478,6 +3727,8 @@ static void ath11k_sta_rc_update_wk(stru + mask = &arvif->bitrate_mask; + num_vht_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, + mask); ++ num_he_rates = ath11k_mac_bitrate_mask_num_he_rates(ar, band, ++ mask); + + /* Peer_assoc_prepare will reject vht rates in + * bitrate_mask if its not available in range format and +@@ -3493,11 +3744,25 @@ static void ath11k_sta_rc_update_wk(stru + if (sta->vht_cap.vht_supported && num_vht_rates == 1) { + ath11k_mac_set_peer_vht_fixed_rate(arvif, sta, mask, + band); ++ } else if (sta->he_cap.has_he && num_he_rates == 1) { ++ ath11k_mac_set_peer_he_fixed_rate(arvif, sta, mask, ++ band); + } else { +- /* If the peer is non-VHT or no fixed VHT rate ++ /* If the peer is non-VHT/HE or no fixed VHT/HE rate + * is provided in the new bitrate mask we set the +- * other rates using peer_assoc command. ++ * other rates using peer_assoc command. Also clear ++ * the peer fixed rate settings as it has higher proprity ++ * than peer assoc + */ ++ err = ath11k_wmi_set_peer_param(ar, sta->addr, ++ arvif->vdev_id, ++ WMI_PEER_PARAM_FIXED_RATE, ++ WMI_FIXED_RATE_NONE); ++ if (err) ++ ath11k_warn(ar->ab, ++ "failed to disable peer fixed rate for sta %pM: %d\n", ++ sta->addr, err); ++ + ath11k_peer_assoc_prepare(ar, arvif->vif, sta, + &peer_arg, true); + +@@ -5111,10 +5376,13 @@ static int ath11k_mac_op_add_interface(s + + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; ++ arvif->bitrate_mask.control[i].gi = NL80211_TXRATE_FORCE_SGI; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); ++ memset(arvif->bitrate_mask.control[i].he_mcs, 0xff, ++ sizeof(arvif->bitrate_mask.control[i].he_mcs)); + } + + bit = __ffs64(ab->free_vdev_map); +@@ -6190,9 +6458,26 @@ ath11k_mac_has_single_legacy_rate(struct + if (ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask)) + return false; + ++ if (ath11k_mac_bitrate_mask_num_he_rates(ar, band, mask)) ++ return false; ++ + return num_rates == 1; + } + ++static __le16 ++ath11k_mac_get_tx_mcs_map(const struct ieee80211_sta_he_cap *he_cap) ++{ ++ if (he_cap->he_cap_elem.phy_cap_info[0] & ++ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) ++ return he_cap->he_mcs_nss_supp.tx_mcs_80p80; ++ ++ if (he_cap->he_cap_elem.phy_cap_info[0] & ++ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) ++ return he_cap->he_mcs_nss_supp.tx_mcs_160; ++ ++ return he_cap->he_mcs_nss_supp.tx_mcs_80; ++} ++ + static bool + ath11k_mac_bitrate_mask_get_single_nss(struct ath11k *ar, + enum nl80211_band band, +@@ -6201,8 +6486,10 @@ ath11k_mac_bitrate_mask_get_single_nss(s + { + struct ieee80211_supported_band *sband = &ar->mac.sbands[band]; + u16 vht_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); ++ u16 he_mcs_map = 0; + u8 ht_nss_mask = 0; + u8 vht_nss_mask = 0; ++ u8 he_nss_mask = 0; + int i; + + /* No need to consider legacy here. Basic rates are always present +@@ -6229,7 +6516,20 @@ ath11k_mac_bitrate_mask_get_single_nss(s + return false; + } + +- if (ht_nss_mask != vht_nss_mask) ++ he_mcs_map = le16_to_cpu(ath11k_mac_get_tx_mcs_map(&sband->iftype_data->he_cap)); ++ ++ for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++) { ++ if (mask->control[band].he_mcs[i] == 0) ++ continue; ++ ++ if (mask->control[band].he_mcs[i] == ++ ath11k_mac_get_max_he_mcs_map(he_mcs_map, i)) ++ he_nss_mask |= BIT(i); ++ else ++ return false; ++ } ++ ++ if (ht_nss_mask != vht_nss_mask || ht_nss_mask != he_nss_mask) + return false; + + if (ht_nss_mask == 0) +@@ -6276,42 +6576,125 @@ ath11k_mac_get_single_legacy_rate(struct + return 0; + } + +-static int ath11k_mac_set_fixed_rate_params(struct ath11k_vif *arvif, +- u32 rate, u8 nss, u8 sgi, u8 ldpc) ++static int ++ath11k_mac_set_fixed_rate_gi_ltf(struct ath11k_vif *arvif, u8 he_gi, u8 he_ltf) + { + struct ath11k *ar = arvif->ar; +- u32 vdev_param; + int ret; + +- lockdep_assert_held(&ar->conf_mutex); ++ /* 0.8 = 0, 1.6 = 2 and 3.2 = 3. */ ++ if (he_gi && he_gi != 0xFF) ++ he_gi += 1; + +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac set fixed rate params vdev %i rate 0x%02x nss %u sgi %u\n", +- arvif->vdev_id, rate, nss, sgi); ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ WMI_VDEV_PARAM_SGI, he_gi); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set he gi %d: %d\n", ++ he_gi, ret); ++ return ret; ++ } ++ /* start from 1 */ ++ if (he_ltf != 0xFF) ++ he_ltf += 1; + +- vdev_param = WMI_VDEV_PARAM_FIXED_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, +- vdev_param, rate); ++ WMI_VDEV_PARAM_HE_LTF, he_ltf); + if (ret) { +- ath11k_warn(ar->ab, "failed to set fixed rate param 0x%02x: %d\n", +- rate, ret); ++ ath11k_warn(ar->ab, "failed to set he ltf %d: %d\n", ++ he_ltf, ret); + return ret; + } + +- vdev_param = WMI_VDEV_PARAM_NSS; ++ return 0; ++} ++ ++static int ++ath11k_mac_set_auto_rate_gi_ltf(struct ath11k_vif *arvif, u16 he_gi, u8 he_ltf) ++{ ++ struct ath11k *ar = arvif->ar; ++ int ret; ++ u32 he_ar_gi_ltf; ++ ++ if (he_gi != 0xFF) { ++ switch (he_gi) { ++ case NL80211_RATE_INFO_HE_GI_0_8: ++ he_gi = WMI_AUTORATE_800NS_GI; ++ break; ++ case NL80211_RATE_INFO_HE_GI_1_6: ++ he_gi = WMI_AUTORATE_1600NS_GI; ++ break; ++ case NL80211_RATE_INFO_HE_GI_3_2: ++ he_gi = WMI_AUTORATE_3200NS_GI; ++ break; ++ default: ++ ath11k_warn(ar->ab, "invalid he gi: %d\n", he_gi); ++ return -EINVAL; ++ } ++ } ++ ++ if (he_ltf != 0xFF) { ++ switch (he_ltf) { ++ case NL80211_RATE_INFO_HE_1XLTF: ++ he_ltf = WMI_HE_AUTORATE_LTF_1X; ++ break; ++ case NL80211_RATE_INFO_HE_2XLTF: ++ he_ltf = WMI_HE_AUTORATE_LTF_2X; ++ break; ++ case NL80211_RATE_INFO_HE_4XLTF: ++ he_ltf = WMI_HE_AUTORATE_LTF_4X; ++ break; ++ default: ++ ath11k_warn(ar->ab, "invalid he ltf: %d\n", he_ltf); ++ return -EINVAL; ++ } ++ } ++ ++ he_ar_gi_ltf = he_gi | he_ltf; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, +- vdev_param, nss); ++ WMI_VDEV_PARAM_AUTORATE_MISC_CFG, ++ he_ar_gi_ltf); + if (ret) { +- ath11k_warn(ar->ab, "failed to set nss param %d: %d\n", +- nss, ret); ++ ath11k_warn(ar->ab, ++ "failed to set he autorate gi %u ltf %u: %d\n", ++ he_gi, he_ltf, ret); + return ret; + } + +- vdev_param = WMI_VDEV_PARAM_SGI; ++ return 0; ++} ++ ++static int ath11k_mac_set_rate_params(struct ath11k_vif *arvif, ++ u32 rate, u8 nss, u8 sgi, u8 ldpc, ++ u8 he_gi, u8 he_ltf, bool he_fixed_rate) ++{ ++ struct ath11k *ar = arvif->ar; ++ u32 vdev_param; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac set rate params vdev %i rate 0x%02x nss 0x%02x sgi 0x%02x ldpc 0x%02x he_gi 0x%02x he_ltf 0x%02x he_fixed_rate %d\n", ++ arvif->vdev_id, rate, nss, sgi, ldpc, he_gi, ++ he_ltf, he_fixed_rate); ++ ++ if (!arvif->vif->bss_conf.he_support) { ++ vdev_param = WMI_VDEV_PARAM_FIXED_RATE; ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ vdev_param, rate); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set fixed rate param 0x%02x: %d\n", ++ rate, ret); ++ return ret; ++ } ++ } ++ ++ vdev_param = WMI_VDEV_PARAM_NSS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, +- vdev_param, sgi); ++ vdev_param, nss); + if (ret) { +- ath11k_warn(ar->ab, "failed to set sgi param %d: %d\n", +- sgi, ret); ++ ath11k_warn(ar->ab, "failed to set nss param %d: %d\n", ++ nss, ret); + return ret; + } + +@@ -6324,6 +6707,35 @@ static int ath11k_mac_set_fixed_rate_par + return ret; + } + ++ if (arvif->vif->bss_conf.he_support) { ++ if (he_fixed_rate) { ++ ret = ath11k_mac_set_fixed_rate_gi_ltf(arvif, he_gi, ++ he_ltf); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set fixed rate gi ltf: %d\n", ++ ret); ++ return ret; ++ } ++ } else { ++ ret = ath11k_mac_set_auto_rate_gi_ltf(arvif, he_gi, ++ he_ltf); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set auto rate gi ltf: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ } else { ++ vdev_param = WMI_VDEV_PARAM_SGI; ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ vdev_param, sgi); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set sgi param %d: %d\n", ++ sgi, ret); ++ return ret; ++ } ++ } ++ + return 0; + } + +@@ -6352,6 +6764,31 @@ ath11k_mac_vht_mcs_range_present(struct + return true; + } + ++static bool ++ath11k_mac_he_mcs_range_present(struct ath11k *ar, ++ enum nl80211_band band, ++ const struct cfg80211_bitrate_mask *mask) ++{ ++ int i; ++ u16 he_mcs; ++ ++ for (i = 0; i < NL80211_HE_NSS_MAX; i++) { ++ he_mcs = mask->control[band].he_mcs[i]; ++ ++ switch (he_mcs) { ++ case 0: ++ case BIT(8) - 1: ++ case BIT(10) - 1: ++ case BIT(12) - 1: ++ break; ++ default: ++ return false; ++ } ++ } ++ ++ return true; ++} ++ + static void ath11k_mac_set_bitrate_mask_iter(void *data, + struct ieee80211_sta *sta) + { +@@ -6383,6 +6820,54 @@ static void ath11k_mac_disable_peer_fixe + sta->addr, ret); + } + ++static bool ++ath11k_mac_validate_vht_he_fixed_rate_settings(struct ath11k *ar, enum nl80211_band band, ++ const struct cfg80211_bitrate_mask *mask) ++{ ++ bool he_fixed_rate = false, vht_fixed_rate = false; ++ struct ath11k_peer *peer, *tmp; ++ const u16 *vht_mcs_mask, *he_mcs_mask; ++ u8 vht_nss, he_nss; ++ bool ret = true; ++ ++ vht_mcs_mask = mask->control[band].vht_mcs; ++ he_mcs_mask = mask->control[band].he_mcs; ++ ++ if (ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask) == 1) ++ vht_fixed_rate = true; ++ ++ if (ath11k_mac_bitrate_mask_num_he_rates(ar, band, mask) == 1) ++ he_fixed_rate = true; ++ ++ if (!vht_fixed_rate && !he_fixed_rate) ++ return true; ++ ++ vht_nss = ath11k_mac_max_vht_nss(vht_mcs_mask); ++ he_nss = ath11k_mac_max_he_nss(he_mcs_mask); ++ ++ rcu_read_lock(); ++ spin_lock_bh(&ar->ab->base_lock); ++ list_for_each_entry_safe(peer, tmp, &ar->ab->peers, list) { ++ if (peer->sta) { ++ if (vht_fixed_rate && (!peer->sta->vht_cap.vht_supported || ++ peer->sta->rx_nss < vht_nss)) { ++ ret = false; ++ goto out; ++ } ++ if (he_fixed_rate && (!peer->sta->he_cap.has_he || ++ peer->sta->rx_nss < he_nss)) { ++ ret = false; ++ goto out; ++ } ++ } ++ } ++ ++out: ++ spin_unlock_bh(&ar->ab->base_lock); ++ rcu_read_unlock(); ++ return ret; ++} ++ + static int + ath11k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, +@@ -6394,6 +6879,9 @@ ath11k_mac_op_set_bitrate_mask(struct ie + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; ++ const u16 *he_mcs_mask; ++ u8 he_ltf = 0; ++ u8 he_gi = 0; + u32 rate; + u8 nss; + u8 sgi; +@@ -6401,6 +6889,7 @@ ath11k_mac_op_set_bitrate_mask(struct ie + int single_nss; + int ret; + int num_rates; ++ bool he_fixed_rate = false; + + if (ath11k_mac_vif_chan(vif, &def)) + return -EPERM; +@@ -6408,12 +6897,16 @@ ath11k_mac_op_set_bitrate_mask(struct ie + band = def.chan->band; + ht_mcs_mask = mask->control[band].ht_mcs; + vht_mcs_mask = mask->control[band].vht_mcs; ++ he_mcs_mask = mask->control[band].he_mcs; + ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); + + sgi = mask->control[band].gi; + if (sgi == NL80211_TXRATE_FORCE_LGI) + return -EINVAL; + ++ he_gi = mask->control[band].he_gi; ++ he_ltf = mask->control[band].he_ltf; ++ + /* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it + * requires passing atleast one of used basic rates along with them. + * Fixed rate setting across different preambles(legacy, HT, VHT) is +@@ -6437,11 +6930,22 @@ ath11k_mac_op_set_bitrate_mask(struct ie + &single_nss)) { + rate = WMI_FIXED_RATE_NONE; + nss = single_nss; ++ mutex_lock(&ar->conf_mutex); ++ arvif->bitrate_mask = *mask; ++ ieee80211_iterate_stations_atomic(ar->hw, ++ ath11k_mac_set_bitrate_mask_iter, ++ arvif); ++ mutex_unlock(&ar->conf_mutex); + } else { + rate = WMI_FIXED_RATE_NONE; ++ ++ if (!ath11k_mac_validate_vht_he_fixed_rate_settings(ar, band, mask)) ++ ath11k_warn(ar->ab, ++ "could not update fixed rate settings to all peers due to mcs/nss incompaitiblity\n"); + nss = min_t(u32, ar->num_tx_chains, +- max(ath11k_mac_max_ht_nss(ht_mcs_mask), +- ath11k_mac_max_vht_nss(vht_mcs_mask))); ++ max(max(ath11k_mac_max_ht_nss(ht_mcs_mask), ++ ath11k_mac_max_vht_nss(vht_mcs_mask)), ++ ath11k_mac_max_he_nss(he_mcs_mask))); + + /* If multiple rates across different preambles are given + * we can reconfigure this info with all peers using PEER_ASSOC +@@ -6472,16 +6976,28 @@ ath11k_mac_op_set_bitrate_mask(struct ie + * RATEMASK CMD + */ + ath11k_warn(ar->ab, +- "Setting more than one MCS Value in bitrate mask not supported\n"); ++ "setting %d mcs values in bitrate mask not supported\n", ++ num_rates); + return -EINVAL; + } + ++ num_rates = ath11k_mac_bitrate_mask_num_he_rates(ar, band, ++ mask); ++ if (num_rates == 1) ++ he_fixed_rate = true; ++ ++ if (!ath11k_mac_he_mcs_range_present(ar, band, mask) && ++ num_rates > 1) { ++ ath11k_warn(ar->ab, ++ "Setting more than one HE MCS Value in bitrate mask not supported\n"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&ar->conf_mutex); + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_disable_peer_fixed_rate, + arvif); + +- mutex_lock(&ar->conf_mutex); +- + arvif->bitrate_mask = *mask; + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_set_bitrate_mask_iter, +@@ -6492,9 +7008,10 @@ ath11k_mac_op_set_bitrate_mask(struct ie + + mutex_lock(&ar->conf_mutex); + +- ret = ath11k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); ++ ret = ath11k_mac_set_rate_params(arvif, rate, nss, sgi, ldpc, he_gi, ++ he_ltf, he_fixed_rate); + if (ret) { +- ath11k_warn(ar->ab, "failed to set fixed rate params on vdev %i: %d\n", ++ ath11k_warn(ar->ab, "failed to set rate params on vdev %i: %d\n", + arvif->vdev_id, ret); + } + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -1905,8 +1905,8 @@ int ath11k_wmi_send_peer_assoc_cmd(struc + FIELD_PREP(WMI_TLV_LEN, + sizeof(*he_mcs) - TLV_HDR_SIZE); + +- he_mcs->rx_mcs_set = param->peer_he_rx_mcs_set[i]; +- he_mcs->tx_mcs_set = param->peer_he_tx_mcs_set[i]; ++ he_mcs->rx_mcs_set = param->peer_he_tx_mcs_set[i]; ++ he_mcs->tx_mcs_set = param->peer_he_rx_mcs_set[i]; + ptr += sizeof(*he_mcs); + } + +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -119,6 +119,22 @@ enum { + WMI_HOST_WLAN_2G_5G_CAP = 0x3, + }; + ++/* Parameters used for WMI_VDEV_PARAM_AUTORATE_MISC_CFG command. ++ * Used only for HE auto rate mode. ++ */ ++enum { ++ /* HE LTF related configuration */ ++ WMI_HE_AUTORATE_LTF_1X = BIT(0), ++ WMI_HE_AUTORATE_LTF_2X = BIT(1), ++ WMI_HE_AUTORATE_LTF_4X = BIT(2), ++ ++ /* HE GI related configuration */ ++ WMI_AUTORATE_400NS_GI = BIT(8), ++ WMI_AUTORATE_800NS_GI = BIT(9), ++ WMI_AUTORATE_1600NS_GI = BIT(10), ++ WMI_AUTORATE_3200NS_GI = BIT(11), ++}; ++ + /* + * wmi command groups. + */ +@@ -1044,7 +1060,9 @@ enum wmi_tlv_vdev_param { + WMI_VDEV_PARAM_HE_RANGE_EXT, + WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, + WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME, ++ WMI_VDEV_PARAM_HE_LTF = 0x74, + WMI_VDEV_PARAM_BA_MODE = 0x7e, ++ WMI_VDEV_PARAM_AUTORATE_MISC_CFG = 0x80, + WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87, + WMI_VDEV_PARAM_6GHZ_PARAMS = 0x99, + WMI_VDEV_PARAM_PROTOTYPE = 0x8000, +@@ -3920,7 +3938,11 @@ struct wmi_vht_rate_set { + + struct wmi_he_rate_set { + u32 tlv_header; ++ ++ /* MCS at which the peer can receive */ + u32 rx_mcs_set; ++ ++ /* MCS at which the peer can transmit */ + u32 tx_mcs_set; + } __packed; + diff --git a/package/kernel/mac80211/patches/ath11k/0010-ath11k-add-support-for-80P80-and-160-MHz-bandwidth.patch b/package/kernel/mac80211/patches/ath11k/0010-ath11k-add-support-for-80P80-and-160-MHz-bandwidth.patch new file mode 100644 index 00000000000000..1b3dddb303041b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0010-ath11k-add-support-for-80P80-and-160-MHz-bandwidth.patch @@ -0,0 +1,322 @@ +From f552d6fd2f27ce9430c74482c46272838e2de688 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 24 Sep 2021 16:52:46 +0300 +Subject: [PATCH] ath11k: add support for 80P80 and 160 MHz bandwidth + +For 160 MHz, nss_ratio_enabled flag is added to indicate firmware +supports sending NSS ratio information from firmware as a part of +service ready ext event. Extract this NSS ratio info from service +ready ext event and save this information in ath11k_pdev_cap to +calculate NSS ratio. + +Current firmware configurations support two types of NSS ratio +which is WMI_NSS_RATIO_1_NSS for QCN9074 and WMI_NSS_RATIO_1BY2_NSS +for IPQ8074. Based on this two configuration, max supported +NSS getting calculated. + +Move ath11k_peer_assoc_h_phymode() before ath11k_peer_assoc_h_vht() +to get arg->peer_phymode updated. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-00097-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01467-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Ganesh Sesetti +Signed-off-by: Ganesh Sesetti +Co-developed-by: Sathishkumar Muruganandam +Signed-off-by: Sathishkumar Muruganandam +Signed-off-by: P Praneesh +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721173615.75637-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 2 + + drivers/net/wireless/ath/ath11k/mac.c | 95 ++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/mac.h | 3 + + drivers/net/wireless/ath/ath11k/wmi.c | 20 +++++- + drivers/net/wireless/ath/ath11k/wmi.h | 30 ++++++++ + 5 files changed, 136 insertions(+), 14 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -603,6 +603,8 @@ struct ath11k_pdev_cap { + u32 tx_chain_mask_shift; + u32 rx_chain_mask_shift; + struct ath11k_band_cap band[NUM_NL80211_BANDS]; ++ bool nss_ratio_enabled; ++ u8 nss_ratio_info; + }; + + struct ath11k_pdev { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1586,6 +1586,34 @@ ath11k_peer_assoc_h_vht_limit(u16 tx_mcs + return tx_mcs_set; + } + ++static u8 ath11k_get_nss_160mhz(struct ath11k *ar, ++ u8 max_nss) ++{ ++ u8 nss_ratio_info = ar->pdev->cap.nss_ratio_info; ++ u8 max_sup_nss = 0; ++ ++ switch (nss_ratio_info) { ++ case WMI_NSS_RATIO_1BY2_NSS: ++ max_sup_nss = max_nss >> 1; ++ break; ++ case WMI_NSS_RATIO_3BY4_NSS: ++ ath11k_warn(ar->ab, "WMI_NSS_RATIO_3BY4_NSS not supported\n"); ++ break; ++ case WMI_NSS_RATIO_1_NSS: ++ max_sup_nss = max_nss; ++ break; ++ case WMI_NSS_RATIO_2_NSS: ++ ath11k_warn(ar->ab, "WMI_NSS_RATIO_2_NSS not supported\n"); ++ break; ++ default: ++ ath11k_warn(ar->ab, "invalid nss ratio received from firmware: %d\n", ++ nss_ratio_info); ++ break; ++ } ++ ++ return max_sup_nss; ++} ++ + static void ath11k_peer_assoc_h_vht(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, +@@ -1600,6 +1628,7 @@ static void ath11k_peer_assoc_h_vht(stru + u8 max_nss, vht_mcs; + int i, vht_nss, nss_idx; + bool user_rate_valid = true; ++ u32 rx_nss, tx_nss, nss_160; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; +@@ -1692,10 +1721,29 @@ static void ath11k_peer_assoc_h_vht(stru + /* TODO: Check */ + arg->tx_max_mcs_nss = 0xFF; + +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", +- sta->addr, arg->peer_max_mpdu, arg->peer_flags); ++ if (arg->peer_phymode == MODE_11AC_VHT160 || ++ arg->peer_phymode == MODE_11AC_VHT80_80) { ++ tx_nss = ath11k_get_nss_160mhz(ar, max_nss); ++ rx_nss = min(arg->peer_nss, tx_nss); ++ arg->peer_bw_rxnss_override = ATH11K_BW_NSS_MAP_ENABLE; ++ ++ if (!rx_nss) { ++ ath11k_warn(ar->ab, "invalid max_nss\n"); ++ return; ++ } ++ ++ if (arg->peer_phymode == MODE_11AC_VHT160) ++ nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_160MHZ, rx_nss - 1); ++ else ++ nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_80_80MHZ, rx_nss - 1); + +- /* TODO: rxnss_override */ ++ arg->peer_bw_rxnss_override |= nss_160; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac vht peer %pM max_mpdu %d flags 0x%x nss_override 0x%x\n", ++ sta->addr, arg->peer_max_mpdu, arg->peer_flags, ++ arg->peer_bw_rxnss_override); + } + + static int ath11k_mac_get_max_he_mcs_map(u16 mcs_map, int nss) +@@ -1779,6 +1827,7 @@ static void ath11k_peer_assoc_h_he(struc + u16 he_tx_mcs = 0, v = 0; + int i, he_nss, nss_idx; + bool user_rate_valid = true; ++ u32 rx_nss, tx_nss, nss_160; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; +@@ -1942,9 +1991,30 @@ static void ath11k_peer_assoc_h_he(struc + } + arg->peer_nss = min(sta->rx_nss, max_nss); + ++ if (arg->peer_phymode == MODE_11AX_HE160 || ++ arg->peer_phymode == MODE_11AX_HE80_80) { ++ tx_nss = ath11k_get_nss_160mhz(ar, max_nss); ++ rx_nss = min(arg->peer_nss, tx_nss); ++ arg->peer_bw_rxnss_override = ATH11K_BW_NSS_MAP_ENABLE; ++ ++ if (!rx_nss) { ++ ath11k_warn(ar->ab, "invalid max_nss\n"); ++ return; ++ } ++ ++ if (arg->peer_phymode == MODE_11AX_HE160) ++ nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_160MHZ, rx_nss - 1); ++ else ++ nss_160 = FIELD_PREP(ATH11K_PEER_RX_NSS_80_80MHZ, rx_nss - 1); ++ ++ arg->peer_bw_rxnss_override |= nss_160; ++ } ++ + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, +- "mac he peer %pM nss %d mcs cnt %d\n", +- sta->addr, arg->peer_nss, arg->peer_he_mcs_count); ++ "mac he peer %pM nss %d mcs cnt %d nss_override 0x%x\n", ++ sta->addr, arg->peer_nss, ++ arg->peer_he_mcs_count, ++ arg->peer_bw_rxnss_override); + } + + static void ath11k_peer_assoc_h_smps(struct ieee80211_sta *sta, +@@ -2232,11 +2302,11 @@ static void ath11k_peer_assoc_prepare(st + ath11k_peer_assoc_h_basic(ar, vif, sta, arg); + ath11k_peer_assoc_h_crypto(ar, vif, sta, arg); + ath11k_peer_assoc_h_rates(ar, vif, sta, arg); ++ ath11k_peer_assoc_h_phymode(ar, vif, sta, arg); + ath11k_peer_assoc_h_ht(ar, vif, sta, arg); + ath11k_peer_assoc_h_vht(ar, vif, sta, arg); + ath11k_peer_assoc_h_he(ar, vif, sta, arg); + ath11k_peer_assoc_h_qos(ar, vif, sta, arg); +- ath11k_peer_assoc_h_phymode(ar, vif, sta, arg); + ath11k_peer_assoc_h_smps(sta, arg); + + /* TODO: amsdu_disable req? */ +@@ -4433,11 +4503,6 @@ ath11k_create_vht_cap(struct ath11k *ar, + + ath11k_set_vht_txbf_cap(ar, &vht_cap.cap); + +- /* TODO: Enable back VHT160 mode once association issues are fixed */ +- /* Disabling VHT160 and VHT80+80 modes */ +- vht_cap.cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; +- vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; +- + rxmcs_map = 0; + txmcs_map = 0; + for (i = 0; i < 8; i++) { +@@ -7355,7 +7420,9 @@ static int ath11k_mac_setup_iface_combin + combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | +- BIT(NL80211_CHAN_WIDTH_80); ++ BIT(NL80211_CHAN_WIDTH_80) | ++ BIT(NL80211_CHAN_WIDTH_80P80) | ++ BIT(NL80211_CHAN_WIDTH_160); + + ar->hw->wiphy->iface_combinations = combinations; + ar->hw->wiphy->n_iface_combinations = 1; +@@ -7494,6 +7561,10 @@ static int __ath11k_mac_register(struct + ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG); + ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK); + ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD); ++ ++ if (cap->nss_ratio_enabled) ++ ieee80211_hw_set(ar->hw, SUPPORTS_VHT_EXT_NSS_BW); ++ + if (ht_cap & WMI_HT_CAP_ENABLED) { + ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION); + ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW); +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -115,6 +115,9 @@ struct ath11k_generic_iter { + #define WMI_MAX_SPATIAL_STREAM 3 + + #define ATH11K_CHAN_WIDTH_NUM 8 ++#define ATH11K_BW_NSS_MAP_ENABLE BIT(31) ++#define ATH11K_PEER_RX_NSS_160MHZ GENMASK(2, 0) ++#define ATH11K_PEER_RX_NSS_80_80MHZ GENMASK(5, 3) + + #define ATH11K_OBSS_PD_MAX_THRESHOLD -82 + #define ATH11K_OBSS_PD_NON_SRG_MAX_THRESHOLD -62 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -360,6 +360,10 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(st + pdev_cap->he_mcs = mac_phy_caps->he_supp_mcs_5g; + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_5g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_5g; ++ pdev_cap->nss_ratio_enabled = ++ WMI_NSS_RATIO_ENABLE_DISABLE_GET(mac_phy_caps->nss_ratio); ++ pdev_cap->nss_ratio_info = ++ WMI_NSS_RATIO_INFO_GET(mac_phy_caps->nss_ratio); + } else { + return -EINVAL; + } +@@ -783,14 +787,26 @@ int ath11k_wmi_vdev_down(struct ath11k * + static void ath11k_wmi_put_wmi_channel(struct wmi_channel *chan, + struct wmi_vdev_start_req_arg *arg) + { ++ u32 center_freq1 = arg->channel.band_center_freq1; ++ + memset(chan, 0, sizeof(*chan)); + + chan->mhz = arg->channel.freq; + chan->band_center_freq1 = arg->channel.band_center_freq1; +- if (arg->channel.mode == MODE_11AC_VHT80_80) ++ ++ if (arg->channel.mode == MODE_11AX_HE160) { ++ if (arg->channel.freq > arg->channel.band_center_freq1) ++ chan->band_center_freq1 = center_freq1 + 40; ++ else ++ chan->band_center_freq1 = center_freq1 - 40; ++ ++ chan->band_center_freq2 = arg->channel.band_center_freq1; ++ ++ } else if (arg->channel.mode == MODE_11AC_VHT80_80) { + chan->band_center_freq2 = arg->channel.band_center_freq2; +- else ++ } else { + chan->band_center_freq2 = 0; ++ } + + chan->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, arg->channel.mode); + if (arg->channel.passive) +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -2146,6 +2146,24 @@ enum wmi_direct_buffer_module { + WMI_DIRECT_BUF_MAX + }; + ++/* enum wmi_nss_ratio - NSS ratio received from FW during service ready ext ++ * event ++ * WMI_NSS_RATIO_1BY2_NSS -Max nss of 160MHz is equals to half of the max nss ++ * of 80MHz ++ * WMI_NSS_RATIO_3BY4_NSS - Max nss of 160MHz is equals to 3/4 of the max nss ++ * of 80MHz ++ * WMI_NSS_RATIO_1_NSS - Max nss of 160MHz is equals to the max nss of 80MHz ++ * WMI_NSS_RATIO_2_NSS - Max nss of 160MHz is equals to two times the max ++ * nss of 80MHz ++ */ ++ ++enum wmi_nss_ratio { ++ WMI_NSS_RATIO_1BY2_NSS = 0x0, ++ WMI_NSS_RATIO_3BY4_NSS = 0x1, ++ WMI_NSS_RATIO_1_NSS = 0x2, ++ WMI_NSS_RATIO_2_NSS = 0x3, ++}; ++ + struct wmi_host_pdev_band_to_mac { + u32 pdev_id; + u32 start_freq; +@@ -2390,6 +2408,12 @@ struct wmi_hw_mode_capabilities { + } __packed; + + #define WMI_MAX_HECAP_PHY_SIZE (3) ++#define WMI_NSS_RATIO_ENABLE_DISABLE_BITPOS BIT(0) ++#define WMI_NSS_RATIO_ENABLE_DISABLE_GET(_val) \ ++ FIELD_GET(WMI_NSS_RATIO_ENABLE_DISABLE_BITPOS, _val) ++#define WMI_NSS_RATIO_INFO_BITPOS GENMASK(4, 1) ++#define WMI_NSS_RATIO_INFO_GET(_val) \ ++ FIELD_GET(WMI_NSS_RATIO_INFO_BITPOS, _val) + + struct wmi_mac_phy_capabilities { + u32 hw_mode_id; +@@ -2423,6 +2447,12 @@ struct wmi_mac_phy_capabilities { + u32 he_cap_info_2g_ext; + u32 he_cap_info_5g_ext; + u32 he_cap_info_internal; ++ u32 wireless_modes; ++ u32 low_2ghz_chan_freq; ++ u32 high_2ghz_chan_freq; ++ u32 low_5ghz_chan_freq; ++ u32 high_5ghz_chan_freq; ++ u32 nss_ratio; + } __packed; + + struct wmi_hal_reg_capabilities_ext { diff --git a/package/kernel/mac80211/patches/ath11k/0011-ath11k-Refactor-spectral-FFT-bin-size.patch b/package/kernel/mac80211/patches/ath11k/0011-ath11k-Refactor-spectral-FFT-bin-size.patch new file mode 100644 index 00000000000000..c7dc058d16e971 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0011-ath11k-Refactor-spectral-FFT-bin-size.patch @@ -0,0 +1,165 @@ +From cc2ad7541486f1f755949c1ccd17e14a15bf1f4e Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Fri, 24 Sep 2021 16:52:46 +0300 +Subject: [PATCH] ath11k: Refactor spectral FFT bin size + +In IPQ8074, actual FFT bin size is two bytes but hardware reports it +with extra pad size of two bytes for each FFT bin. So finally each FFT +bin advertise as four bytes size in the collected data. This FFT pad is +not advertised in IPQ6018 platform. To accommodate this different +behavior across the platforms, introduce the hw param fft_pad_sz and use +it in spectral process. Also group all the spectral params under the new +structure in hw param structure for scalable in future. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01492-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ6018 hw1.0 AHB WLAN.HK.2.4.0.1-00330-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721180809.90960-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 33 +++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/hw.h | 6 +++- + drivers/net/wireless/ath/ath11k/spectral.c | 13 ++++----- + 3 files changed, 40 insertions(+), 12 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -59,7 +59,14 @@ static const struct ath11k_hw_params ath + .vdev_start_delay = false, + .htt_peer_map_v2 = true, + .tcl_0_only = false, +- .spectral_fft_sz = 2, ++ ++ .spectral = { ++ .fft_sz = 2, ++ /* HW bug, expected BIN size is 2 bytes but HW report as 4 bytes. ++ * so added pad size as 2 bytes to compensate the BIN size ++ */ ++ .fft_pad_sz = 2, ++ }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | +@@ -100,7 +107,11 @@ static const struct ath11k_hw_params ath + .vdev_start_delay = false, + .htt_peer_map_v2 = true, + .tcl_0_only = false, +- .spectral_fft_sz = 4, ++ ++ .spectral = { ++ .fft_sz = 4, ++ .fft_pad_sz = 0, ++ }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | +@@ -141,7 +152,11 @@ static const struct ath11k_hw_params ath + .vdev_start_delay = true, + .htt_peer_map_v2 = false, + .tcl_0_only = true, +- .spectral_fft_sz = 0, ++ ++ .spectral = { ++ .fft_sz = 0, ++ .fft_pad_sz = 0, ++ }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), +@@ -180,6 +195,12 @@ static const struct ath11k_hw_params ath + .vdev_start_delay = false, + .htt_peer_map_v2 = true, + .tcl_0_only = false, ++ ++ .spectral = { ++ .fft_sz = 0, ++ .fft_pad_sz = 0, ++ }, ++ + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), +@@ -219,7 +240,11 @@ static const struct ath11k_hw_params ath + .vdev_start_delay = true, + .htt_peer_map_v2 = false, + .tcl_0_only = true, +- .spectral_fft_sz = 0, ++ ++ .spectral = { ++ .fft_sz = 0, ++ .fft_pad_sz = 0, ++ }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -153,7 +153,11 @@ struct ath11k_hw_params { + bool vdev_start_delay; + bool htt_peer_map_v2; + bool tcl_0_only; +- u8 spectral_fft_sz; ++ ++ struct { ++ u8 fft_sz; ++ u8 fft_pad_sz; ++ } spectral; + + u16 interface_modes; + bool supports_monitor; +--- a/drivers/net/wireless/ath/ath11k/spectral.c ++++ b/drivers/net/wireless/ath/ath11k/spectral.c +@@ -11,8 +11,6 @@ + #define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS 1 + + #define ATH11K_SPECTRAL_DWORD_SIZE 4 +-/* HW bug, expected BIN size is 2 bytes but HW report as 4 bytes */ +-#define ATH11K_SPECTRAL_BIN_SIZE 4 + #define ATH11K_SPECTRAL_ATH11K_MIN_BINS 64 + #define ATH11K_SPECTRAL_ATH11K_MIN_IB_BINS 32 + #define ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS 256 +@@ -585,12 +583,12 @@ int ath11k_spectral_process_fft(struct a + struct spectral_tlv *tlv; + int tlv_len, bin_len, num_bins; + u16 length, freq; +- u8 chan_width_mhz; ++ u8 chan_width_mhz, bin_sz; + int ret; + + lockdep_assert_held(&ar->spectral.lock); + +- if (!ab->hw_params.spectral_fft_sz) { ++ if (!ab->hw_params.spectral.fft_sz) { + ath11k_warn(ab, "invalid bin size type for hw rev %d\n", + ab->hw_rev); + return -EINVAL; +@@ -608,7 +606,8 @@ int ath11k_spectral_process_fft(struct a + return -EINVAL; + } + +- num_bins = bin_len / ATH11K_SPECTRAL_BIN_SIZE; ++ bin_sz = ab->hw_params.spectral.fft_sz + ab->hw_params.spectral.fft_pad_sz; ++ num_bins = bin_len / bin_sz; + /* Only In-band bins are useful to user for visualize */ + num_bins >>= 1; + +@@ -658,7 +657,7 @@ int ath11k_spectral_process_fft(struct a + fft_sample->freq2 = __cpu_to_be16(freq); + + ath11k_spectral_parse_fft(fft_sample->data, fft_report->bins, num_bins, +- ab->hw_params.spectral_fft_sz); ++ ab->hw_params.spectral.fft_sz); + + fft_sample->max_exp = ath11k_spectral_get_max_exp(fft_sample->max_index, + search.peak_mag, +@@ -966,7 +965,7 @@ int ath11k_spectral_init(struct ath11k_b + ab->wmi_ab.svc_map)) + return 0; + +- if (!ab->hw_params.spectral_fft_sz) ++ if (!ab->hw_params.spectral.fft_sz) + return 0; + + for (i = 0; i < ab->num_radios; i++) { diff --git a/package/kernel/mac80211/patches/ath11k/0012-ath11k-Introduce-spectral-hw-configurable-param.patch b/package/kernel/mac80211/patches/ath11k/0012-ath11k-Introduce-spectral-hw-configurable-param.patch new file mode 100644 index 00000000000000..3f2ea13dd76555 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0012-ath11k-Introduce-spectral-hw-configurable-param.patch @@ -0,0 +1,168 @@ +From 1cae9c0009d35cec94ad8e1b06ebcb2d704626bf Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Fri, 24 Sep 2021 16:52:46 +0300 +Subject: [PATCH] ath11k: Introduce spectral hw configurable param + +Below parameters have been identified as configurable across the platforms. +So to scale the spectral across the platforms, move these parameter +into hw param. + + 1. Maximum FFT bins + 2. Summary report pad size + 3. FFT report header length + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01492-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ6018 hw1.0 AHB WLAN.HK.2.4.0.1-00330-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721180809.90960-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 12 +++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 3 +++ + drivers/net/wireless/ath/ath11k/spectral.c | 29 +++++++++++----------- + 3 files changed, 30 insertions(+), 14 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -66,6 +66,9 @@ static const struct ath11k_hw_params ath + * so added pad size as 2 bytes to compensate the BIN size + */ + .fft_pad_sz = 2, ++ .summary_pad_sz = 0, ++ .fft_hdr_len = 16, ++ .max_fft_bins = 512, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | +@@ -111,6 +114,9 @@ static const struct ath11k_hw_params ath + .spectral = { + .fft_sz = 4, + .fft_pad_sz = 0, ++ .summary_pad_sz = 0, ++ .fft_hdr_len = 16, ++ .max_fft_bins = 512, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | +@@ -156,6 +162,9 @@ static const struct ath11k_hw_params ath + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, ++ .summary_pad_sz = 0, ++ .fft_hdr_len = 0, ++ .max_fft_bins = 0, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | +@@ -244,6 +253,9 @@ static const struct ath11k_hw_params ath + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, ++ .summary_pad_sz = 0, ++ .fft_hdr_len = 0, ++ .max_fft_bins = 0, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -157,6 +157,9 @@ struct ath11k_hw_params { + struct { + u8 fft_sz; + u8 fft_pad_sz; ++ u8 summary_pad_sz; ++ u8 fft_hdr_len; ++ u16 max_fft_bins; + } spectral; + + u16 interface_modes; +--- a/drivers/net/wireless/ath/ath11k/spectral.c ++++ b/drivers/net/wireless/ath/ath11k/spectral.c +@@ -11,20 +11,20 @@ + #define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS 1 + + #define ATH11K_SPECTRAL_DWORD_SIZE 4 +-#define ATH11K_SPECTRAL_ATH11K_MIN_BINS 64 +-#define ATH11K_SPECTRAL_ATH11K_MIN_IB_BINS 32 +-#define ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS 256 ++#define ATH11K_SPECTRAL_MIN_BINS 64 ++#define ATH11K_SPECTRAL_MIN_IB_BINS (ATH11K_SPECTRAL_MIN_BINS >> 1) ++#define ATH11K_SPECTRAL_MAX_IB_BINS(x) ((x)->hw_params.spectral.max_fft_bins >> 1) + + #define ATH11K_SPECTRAL_SCAN_COUNT_MAX 4095 + + /* Max channel computed by sum of 2g and 5g band channels */ + #define ATH11K_SPECTRAL_TOTAL_CHANNEL 41 + #define ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL 70 +-#define ATH11K_SPECTRAL_PER_SAMPLE_SIZE (sizeof(struct fft_sample_ath11k) + \ +- ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS) ++#define ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x) (sizeof(struct fft_sample_ath11k) + \ ++ ATH11K_SPECTRAL_MAX_IB_BINS(x)) + #define ATH11K_SPECTRAL_TOTAL_SAMPLE (ATH11K_SPECTRAL_TOTAL_CHANNEL * \ + ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL) +-#define ATH11K_SPECTRAL_SUB_BUFF_SIZE ATH11K_SPECTRAL_PER_SAMPLE_SIZE ++#define ATH11K_SPECTRAL_SUB_BUFF_SIZE(x) ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x) + #define ATH11K_SPECTRAL_NUM_SUB_BUF ATH11K_SPECTRAL_TOTAL_SAMPLE + + #define ATH11K_SPECTRAL_20MHZ 20 +@@ -446,8 +446,8 @@ static ssize_t ath11k_write_file_spectra + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + +- if (val < ATH11K_SPECTRAL_ATH11K_MIN_BINS || +- val > SPECTRAL_ATH11K_MAX_NUM_BINS) ++ if (val < ATH11K_SPECTRAL_MIN_BINS || ++ val > ar->ab->hw_params.spectral.max_fft_bins) + return -EINVAL; + + if (!is_power_of_2(val)) +@@ -598,7 +598,7 @@ int ath11k_spectral_process_fft(struct a + tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN, __le32_to_cpu(tlv->header)); + /* convert Dword into bytes */ + tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE; +- bin_len = tlv_len - (sizeof(*fft_report) - sizeof(*tlv)); ++ bin_len = tlv_len - ab->hw_params.spectral.fft_hdr_len; + + if (data_len < (bin_len + sizeof(*fft_report))) { + ath11k_warn(ab, "mismatch in expected bin len %d and data len %d\n", +@@ -611,8 +611,8 @@ int ath11k_spectral_process_fft(struct a + /* Only In-band bins are useful to user for visualize */ + num_bins >>= 1; + +- if (num_bins < ATH11K_SPECTRAL_ATH11K_MIN_IB_BINS || +- num_bins > ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS || ++ if (num_bins < ATH11K_SPECTRAL_MIN_IB_BINS || ++ num_bins > ATH11K_SPECTRAL_MAX_IB_BINS(ab) || + !is_power_of_2(num_bins)) { + ath11k_warn(ab, "Invalid num of bins %d\n", num_bins); + return -EINVAL; +@@ -693,7 +693,7 @@ static int ath11k_spectral_process_data( + goto unlock; + } + +- sample_sz = sizeof(*fft_sample) + ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS; ++ sample_sz = sizeof(*fft_sample) + ATH11K_SPECTRAL_MAX_IB_BINS(ab); + fft_sample = kmalloc(sample_sz, GFP_ATOMIC); + if (!fft_sample) { + ret = -ENOBUFS; +@@ -741,7 +741,8 @@ static int ath11k_spectral_process_data( + * is 4 DWORD size (16 bytes). + * Need to remove this workaround once HW bug fixed + */ +- tlv_len = sizeof(*summary) - sizeof(*tlv); ++ tlv_len = sizeof(*summary) - sizeof(*tlv) + ++ ab->hw_params.spectral.summary_pad_sz; + + if (tlv_len < (sizeof(*summary) - sizeof(*tlv))) { + ath11k_warn(ab, "failed to parse spectral summary at bytes %d tlv_len:%d\n", +@@ -904,7 +905,7 @@ static inline int ath11k_spectral_debug_ + + ar->spectral.rfs_scan = relay_open("spectral_scan", + ar->debug.debugfs_pdev, +- ATH11K_SPECTRAL_SUB_BUFF_SIZE, ++ ATH11K_SPECTRAL_SUB_BUFF_SIZE(ar->ab), + ATH11K_SPECTRAL_NUM_SUB_BUF, + &rfs_scan_cb, NULL); + if (!ar->spectral.rfs_scan) { diff --git a/package/kernel/mac80211/patches/ath11k/0013-ath11k-Fix-the-spectral-minimum-FFT-bin-count.patch b/package/kernel/mac80211/patches/ath11k/0013-ath11k-Fix-the-spectral-minimum-FFT-bin-count.patch new file mode 100644 index 00000000000000..50dbdd55c745f9 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0013-ath11k-Fix-the-spectral-minimum-FFT-bin-count.patch @@ -0,0 +1,33 @@ +From 6dfd20c8a6cd1fcf2c68d86c9d678f42535f6ade Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Fri, 24 Sep 2021 16:52:46 +0300 +Subject: [PATCH] ath11k: Fix the spectral minimum FFT bin count + +User was not able to configure the spectral with the FFT bin count 32. +In all supported platforms, the expected minimum FFT bin count is 32 but +it was wrongly defined as 64. This restrict the user to not configure +down to the actually supported minimum FFT bin count. So update the +minimum FFT bin count as 32. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01492-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ6018 hw1.0 AHB WLAN.HK.2.4.0.1-00330-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721180809.90960-4-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/spectral.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/spectral.c ++++ b/drivers/net/wireless/ath/ath11k/spectral.c +@@ -11,7 +11,7 @@ + #define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS 1 + + #define ATH11K_SPECTRAL_DWORD_SIZE 4 +-#define ATH11K_SPECTRAL_MIN_BINS 64 ++#define ATH11K_SPECTRAL_MIN_BINS 32 + #define ATH11K_SPECTRAL_MIN_IB_BINS (ATH11K_SPECTRAL_MIN_BINS >> 1) + #define ATH11K_SPECTRAL_MAX_IB_BINS(x) ((x)->hw_params.spectral.max_fft_bins >> 1) + diff --git a/package/kernel/mac80211/patches/ath11k/0014-ath11k-Add-spectral-scan-support-for-QCN9074.patch b/package/kernel/mac80211/patches/ath11k/0014-ath11k-Add-spectral-scan-support-for-QCN9074.patch new file mode 100644 index 00000000000000..f2c2c7c561b1ca --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0014-ath11k-Add-spectral-scan-support-for-QCN9074.patch @@ -0,0 +1,36 @@ +From b72e86c07e9881d249fbb7511060692f3fb6b687 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Fri, 24 Sep 2021 16:52:46 +0300 +Subject: [PATCH] ath11k: Add spectral scan support for QCN9074 + +Populate the below hw parameters as per the QCN9074 support + 1. FFT bin size as two bytes + 2. Maximum FFT bin count as 1024 + 3. Summary report pad size as 16 + 4. FFT report header length as 24 + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01492-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721180809.90960-5-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -206,8 +206,11 @@ static const struct ath11k_hw_params ath + .tcl_0_only = false, + + .spectral = { +- .fft_sz = 0, ++ .fft_sz = 2, + .fft_pad_sz = 0, ++ .summary_pad_sz = 16, ++ .fft_hdr_len = 24, ++ .max_fft_bins = 1024, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | diff --git a/package/kernel/mac80211/patches/ath11k/0015-ath11k-Wstringop-overread-warning.patch b/package/kernel/mac80211/patches/ath11k/0015-ath11k-Wstringop-overread-warning.patch new file mode 100644 index 00000000000000..1188b00fc261a6 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0015-ath11k-Wstringop-overread-warning.patch @@ -0,0 +1,44 @@ +From eb19efed836a51ee30a602abe2dd21a97c47bbcc Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Fri, 24 Sep 2021 16:52:52 +0300 +Subject: [PATCH] ath11k: Wstringop-overread warning + +gcc-11 with the kernel address sanitizer prints a warning for this +driver: + +In function 'ath11k_peer_assoc_h_vht', + inlined from 'ath11k_peer_assoc_prepare' at drivers/net/wireless/ath/ath11k/mac.c:1632:2: +drivers/net/wireless/ath/ath11k/mac.c:1164:13: error: 'ath11k_peer_assoc_h_vht_masked' reading 16 bytes from a region of size 4 [-Werror=stringop-overread] + 1164 | if (ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +drivers/net/wireless/ath/ath11k/mac.c: In function 'ath11k_peer_assoc_prepare': +drivers/net/wireless/ath/ath11k/mac.c:1164:13: note: referencing argument 1 of type 'const u16 *' {aka 'const short unsigned int *'} +drivers/net/wireless/ath/ath11k/mac.c:969:1: note: in a call to function 'ath11k_peer_assoc_h_vht_masked' + 969 | ath11k_peer_assoc_h_vht_masked(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +According to analysis from gcc developers, this is a glitch in the +way gcc tracks the size of struct members. This should really get +fixed in gcc, but it's also easy to work around this instance +by changing the function prototype to no include the length of +the array. + +Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99673 +Signed-off-by: Arnd Bergmann +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210322160253.4032422-5-arnd@kernel.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1406,7 +1406,7 @@ ath11k_peer_assoc_h_ht_masked(const u8 h + } + + static bool +-ath11k_peer_assoc_h_vht_masked(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) ++ath11k_peer_assoc_h_vht_masked(const u16 vht_mcs_mask[]) + { + int nss; + diff --git a/package/kernel/mac80211/patches/ath11k/0016-ath11k-use-hw_params-to-access-board_size-and-cal_of.patch b/package/kernel/mac80211/patches/ath11k/0016-ath11k-use-hw_params-to-access-board_size-and-cal_of.patch new file mode 100644 index 00000000000000..1ff4ae8dd2b51b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0016-ath11k-use-hw_params-to-access-board_size-and-cal_of.patch @@ -0,0 +1,113 @@ +From c72aa32d6d1c04fa83d4c0e6849e4e60d9d39ae4 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Tue, 28 Sep 2021 12:05:39 +0300 +Subject: [PATCH] ath11k: use hw_params to access board_size and cal_offset + +Reuse board_size from hw_params, add cal_offset to hw params. +This patch is clean up only, there is no change in functionality. + +cal_size was unused, so remove that. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00009-QCAHKSWPL_SILICONZ-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721201927.100369-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 10 +++++----- + drivers/net/wireless/ath/ath11k/hw.h | 2 +- + drivers/net/wireless/ath/ath11k/qmi.c | 4 ++-- + drivers/net/wireless/ath/ath11k/qmi.h | 2 -- + 4 files changed, 8 insertions(+), 10 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -37,7 +37,7 @@ static const struct ath11k_hw_params ath + .fw = { + .dir = "IPQ8074/hw2.0", + .board_size = 256 * 1024, +- .cal_size = 256 * 1024, ++ .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, +@@ -88,7 +88,7 @@ static const struct ath11k_hw_params ath + .fw = { + .dir = "IPQ6018/hw1.0", + .board_size = 256 * 1024, +- .cal_size = 256 * 1024, ++ .cal_offset = 128 * 1024, + }, + .max_radios = 2, + .bdf_addr = 0x4ABC0000, +@@ -136,7 +136,7 @@ static const struct ath11k_hw_params ath + .fw = { + .dir = "QCA6390/hw2.0", + .board_size = 256 * 1024, +- .cal_size = 256 * 1024, ++ .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, +@@ -183,7 +183,7 @@ static const struct ath11k_hw_params ath + .fw = { + .dir = "QCN9074/hw1.0", + .board_size = 256 * 1024, +- .cal_size = 256 * 1024, ++ .cal_offset = 128 * 1024, + }, + .max_radios = 1, + .single_pdev_only = false, +@@ -230,7 +230,7 @@ static const struct ath11k_hw_params ath + .fw = { + .dir = "WCN6855/hw2.0", + .board_size = 256 * 1024, +- .cal_size = 256 * 1024, ++ .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -128,7 +128,7 @@ struct ath11k_hw_params { + struct { + const char *dir; + size_t board_size; +- size_t cal_size; ++ size_t cal_offset; + } fw; + + const struct ath11k_hw_ops *hw_ops; +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1953,7 +1953,7 @@ ath11k_qmi_prepare_bdf_download(struct a + fw_size = min_t(u32, ab->hw_params.fw.board_size, + fw_entry->size); + +- memcpy_toio(bdf_addr + ATH11K_QMI_CALDATA_OFFSET, ++ memcpy_toio(bdf_addr + ab->hw_params.fw.cal_offset, + fw_entry->data, fw_size); + + release_firmware(fw_entry); +@@ -1979,7 +1979,7 @@ static int ath11k_qmi_load_bdf_fixed_add + return -ENOMEM; + memset(&resp, 0, sizeof(resp)); + +- bdf_addr = ioremap(ab->hw_params.bdf_addr, ATH11K_QMI_BDF_MAX_SIZE); ++ bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size); + if (!bdf_addr) { + ath11k_warn(ab, "failed ioremap for board file\n"); + ret = -EIO; +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -13,8 +13,6 @@ + #define ATH11K_QMI_WLANFW_TIMEOUT_MS 5000 + #define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64 + #define ATH11K_QMI_CALDB_ADDRESS 0x4BA00000 +-#define ATH11K_QMI_BDF_MAX_SIZE (256 * 1024) +-#define ATH11K_QMI_CALDATA_OFFSET (128 * 1024) + #define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 + #define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45 + #define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01 diff --git a/package/kernel/mac80211/patches/ath11k/0017-ath11k-clean-up-BDF-download-functions.patch b/package/kernel/mac80211/patches/ath11k/0017-ath11k-clean-up-BDF-download-functions.patch new file mode 100644 index 00000000000000..f26c6ce3340a7b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0017-ath11k-clean-up-BDF-download-functions.patch @@ -0,0 +1,357 @@ +From 336e7b53c82fc74d261024773a0fab43623a94fb Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Tue, 28 Sep 2021 12:05:39 +0300 +Subject: [PATCH] ath11k: clean up BDF download functions + +In current code, AHB/PCI uses two separate functions to download +BDF file. Refactor code and make a common function to send QMI BDF +download request for both AHB and PCI devices. This patch has no +functional change. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00009-QCAHKSWPL_SILICONZ-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721201927.100369-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/qmi.c | 248 +++++++++++--------------- + 1 file changed, 101 insertions(+), 147 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1917,98 +1917,72 @@ out: + return ret; + } + +-static int +-ath11k_qmi_prepare_bdf_download(struct ath11k_base *ab, int type, +- struct qmi_wlanfw_bdf_download_req_msg_v01 *req, +- void __iomem *bdf_addr) +-{ +- const struct firmware *fw_entry; +- struct ath11k_board_data bd; +- u32 fw_size; +- int ret; +- +- switch (type) { +- case ATH11K_QMI_FILE_TYPE_BDF_GOLDEN: +- memset(&bd, 0, sizeof(bd)); +- +- ret = ath11k_core_fetch_bdf(ab, &bd); +- if (ret) { +- ath11k_warn(ab, "failed to load board file: %d\n", ret); +- return ret; +- } +- +- fw_size = min_t(u32, ab->hw_params.fw.board_size, bd.len); +- memcpy_toio(bdf_addr, bd.data, fw_size); +- ath11k_core_free_bdf(ab, &bd); +- break; +- case ATH11K_QMI_FILE_TYPE_CALDATA: +- fw_entry = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_CAL_FILE); +- if (IS_ERR(fw_entry)) { +- ret = PTR_ERR(fw_entry); +- ath11k_warn(ab, "failed to load %s: %d\n", +- ATH11K_DEFAULT_CAL_FILE, ret); +- return ret; +- } +- +- fw_size = min_t(u32, ab->hw_params.fw.board_size, +- fw_entry->size); +- +- memcpy_toio(bdf_addr + ab->hw_params.fw.cal_offset, +- fw_entry->data, fw_size); +- +- release_firmware(fw_entry); +- break; +- default: +- return -EINVAL; +- } +- +- req->total_size = fw_size; +- return 0; +-} +- +-static int ath11k_qmi_load_bdf_fixed_addr(struct ath11k_base *ab) ++static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, ++ const u8 *data, u32 len, u8 type) + { + struct qmi_wlanfw_bdf_download_req_msg_v01 *req; + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + struct qmi_txn txn = {}; ++ const u8 *temp = data; + void __iomem *bdf_addr = NULL; +- int type, ret; ++ int ret; ++ u32 remaining = len; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; ++ + memset(&resp, 0, sizeof(resp)); + +- bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size); +- if (!bdf_addr) { +- ath11k_warn(ab, "failed ioremap for board file\n"); +- ret = -EIO; +- goto out; ++ if (ab->bus_params.fixed_bdf_addr) { ++ 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; ++ goto err_free_req; ++ } + } + +- for (type = 0; type < ATH11K_QMI_MAX_FILE_TYPE; type++) { ++ while (remaining) { + req->valid = 1; + req->file_id_valid = 1; + req->file_id = ab->qmi.target.board_id; + req->total_size_valid = 1; ++ req->total_size = remaining; + req->seg_id_valid = 1; +- req->seg_id = type; +- req->data_valid = 0; +- req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; +- req->bdf_type = 0; +- req->bdf_type_valid = 0; ++ req->data_valid = 1; ++ req->bdf_type = type; ++ req->bdf_type_valid = 1; + req->end_valid = 1; +- req->end = 1; ++ req->end = 0; + +- ret = ath11k_qmi_prepare_bdf_download(ab, type, req, bdf_addr); +- if (ret < 0) +- goto out_qmi_bdf; ++ if (remaining > QMI_WLANFW_MAX_DATA_SIZE_V01) { ++ req->data_len = QMI_WLANFW_MAX_DATA_SIZE_V01; ++ } else { ++ req->data_len = remaining; ++ req->end = 1; ++ } ++ ++ if (ab->bus_params.fixed_bdf_addr) { ++ req->data_valid = 0; ++ req->end = 1; ++ req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; ++ } else { ++ memcpy(req->data, temp, req->data_len); ++ } ++ ++ if (ab->bus_params.fixed_bdf_addr) { ++ if (type == ATH11K_QMI_FILE_TYPE_CALDATA) ++ bdf_addr += ab->hw_params.fw.cal_offset; ++ ++ memcpy_toio(bdf_addr, temp, len); ++ } + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_bdf_download_resp_msg_v01_ei, + &resp); + if (ret < 0) +- goto out_qmi_bdf; ++ goto err_iounmap; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi bdf download req fixed addr type %d\n", + type); +@@ -2019,54 +1993,59 @@ static int ath11k_qmi_load_bdf_fixed_add + qmi_wlanfw_bdf_download_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); +- goto out_qmi_bdf; ++ goto err_iounmap; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); +- if (ret < 0) +- goto out_qmi_bdf; ++ if (ret < 0) { ++ ath11k_warn(ab, "failed to wait board file download request: %d\n", ++ ret); ++ goto err_iounmap; ++ } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "board file download request failed: %d %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; +- goto out_qmi_bdf; ++ goto err_iounmap; ++ } ++ ++ if (ab->bus_params.fixed_bdf_addr) { ++ remaining = 0; ++ } else { ++ remaining -= req->data_len; ++ temp += req->data_len; ++ req->seg_id++; ++ ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi bdf download request remaining %i\n", ++ remaining); + } + } + +-out_qmi_bdf: +- iounmap(bdf_addr); +-out: ++err_iounmap: ++ if (ab->bus_params.fixed_bdf_addr) ++ iounmap(bdf_addr); ++ ++err_free_req: + kfree(req); ++ + return ret; + } + + static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) + { +- struct qmi_wlanfw_bdf_download_req_msg_v01 *req; +- struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; ++ char filename[ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE]; ++ const struct firmware *fw_entry; + struct ath11k_board_data bd; +- unsigned int remaining; +- struct qmi_txn txn = {}; +- int ret; +- const u8 *temp; +- int bdf_type; +- +- req = kzalloc(sizeof(*req), GFP_KERNEL); +- if (!req) +- return -ENOMEM; +- memset(&resp, 0, sizeof(resp)); ++ u32 fw_size, file_type; ++ int ret = 0, bdf_type; + + memset(&bd, 0, sizeof(bd)); + ret = ath11k_core_fetch_bdf(ab, &bd); + if (ret) { +- ath11k_warn(ab, "failed to fetch board file: %d\n", ret); ++ ath11k_warn(ab, "qmi failed to fetch board file: %d\n", ret); + goto out; + } + +- temp = bd.data; +- remaining = bd.len; +- + if (bd.len >= SELFMAG && memcmp(bd.data, ELFMAG, SELFMAG) == 0) + bdf_type = ATH11K_QMI_BDF_TYPE_ELF; + else +@@ -2074,67 +2053,45 @@ static int ath11k_qmi_load_bdf_qmi(struc + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi bdf_type %d\n", bdf_type); + +- while (remaining) { +- req->valid = 1; +- req->file_id_valid = 1; +- req->file_id = ab->qmi.target.board_id; +- req->total_size_valid = 1; +- req->total_size = bd.len; +- req->seg_id_valid = 1; +- req->data_valid = 1; +- req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; +- req->bdf_type = bdf_type; +- req->bdf_type_valid = 1; +- req->end_valid = 1; +- req->end = 0; +- +- if (remaining > QMI_WLANFW_MAX_DATA_SIZE_V01) { +- req->data_len = QMI_WLANFW_MAX_DATA_SIZE_V01; +- } else { +- req->data_len = remaining; +- req->end = 1; +- } +- +- memcpy(req->data, temp, req->data_len); +- +- ret = qmi_txn_init(&ab->qmi.handle, &txn, +- qmi_wlanfw_bdf_download_resp_msg_v01_ei, +- &resp); +- if (ret < 0) +- goto out_qmi_bdf; ++ fw_size = bd.len; ++ fw_size = min_t(u32, ab->hw_params.fw.board_size, bd.len); + +- ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi bdf download request remaining %i\n", +- remaining); ++ ret = ath11k_qmi_load_file_target_mem(ab, bd.data, fw_size, bdf_type); ++ if (ret < 0) { ++ ath11k_warn(ab, "qmi failed to load bdf file\n"); ++ goto out; ++ } + +- ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, +- QMI_WLANFW_BDF_DOWNLOAD_REQ_V01, +- QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN, +- qmi_wlanfw_bdf_download_req_msg_v01_ei, req); +- if (ret < 0) { +- qmi_txn_cancel(&txn); +- goto out_qmi_bdf; +- } ++ /* QCA6390 does not support cal data file, skip it */ ++ if (bdf_type == ATH11K_QMI_BDF_TYPE_ELF) ++ goto out; + +- ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); +- if (ret < 0) +- goto out_qmi_bdf; ++ file_type = ATH11K_QMI_FILE_TYPE_CALDATA; ++ fw_entry = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_CAL_FILE); ++ if (IS_ERR(fw_entry)) { ++ ret = PTR_ERR(fw_entry); ++ ath11k_warn(ab, ++ "qmi failed to load CAL data file:%s\n", ++ filename); ++ goto out; ++ } + +- if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { +- ath11k_warn(ab, "bdf download request failed: %d %d\n", +- resp.resp.result, resp.resp.error); +- ret = resp.resp.result; +- goto out_qmi_bdf; +- } +- remaining -= req->data_len; +- temp += req->data_len; +- req->seg_id++; ++ fw_size = min_t(u32, ab->hw_params.fw.board_size, fw_entry->size); ++ ret = ath11k_qmi_load_file_target_mem(ab, fw_entry->data, fw_size, file_type); ++ if (ret < 0) { ++ ath11k_warn(ab, "qmi failed to load caldata\n"); ++ goto out_qmi_cal; + } + +-out_qmi_bdf: +- ath11k_core_free_bdf(ab, &bd); ++ ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi caldata downloaded: type: %u\n", ++ file_type); + ++out_qmi_cal: ++ release_firmware(fw_entry); + out: +- kfree(req); ++ ath11k_core_free_bdf(ab, &bd); ++ ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi BDF download sequence completed\n"); ++ + return ret; + } + +@@ -2519,10 +2476,7 @@ static int ath11k_qmi_event_load_bdf(str + return ret; + } + +- if (ab->bus_params.fixed_bdf_addr) +- ret = ath11k_qmi_load_bdf_fixed_addr(ab); +- else +- ret = ath11k_qmi_load_bdf_qmi(ab); ++ ret = ath11k_qmi_load_bdf_qmi(ab); + if (ret < 0) { + ath11k_warn(ab, "failed to load board data file: %d\n", ret); + return ret; diff --git a/package/kernel/mac80211/patches/ath11k/0018-ath11k-add-caldata-file-for-multiple-radios.patch b/package/kernel/mac80211/patches/ath11k/0018-ath11k-add-caldata-file-for-multiple-radios.patch new file mode 100644 index 00000000000000..9975cca01e60db --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0018-ath11k-add-caldata-file-for-multiple-radios.patch @@ -0,0 +1,68 @@ +From e82dfe7b5608592c270cc69100cb4322069f949d Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Tue, 28 Sep 2021 12:05:39 +0300 +Subject: [PATCH] ath11k: add caldata file for multiple radios + +If multiple PCI cards are attached, each needs its own caldata file. + +Added new Caldata file name, +PCI Bus: + cal-pci-0001:01:00.0.bin + cal-pci-0000:01:00.0.bin +AHB Bus: + cal-ahb-c000000.wifi1.bin + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00009-QCAHKSWPL_SILICONZ-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721201927.100369-4-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/qmi.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2033,6 +2033,7 @@ err_free_req: + + static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) + { ++ struct device *dev = ab->dev; + char filename[ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE]; + const struct firmware *fw_entry; + struct ath11k_board_data bd; +@@ -2067,6 +2068,14 @@ static int ath11k_qmi_load_bdf_qmi(struc + goto out; + + file_type = ATH11K_QMI_FILE_TYPE_CALDATA; ++ ++ /* cal--.bin */ ++ snprintf(filename, sizeof(filename), "cal-%s-%s.bin", ++ ath11k_bus_str(ab->hif.bus), dev_name(dev)); ++ fw_entry = ath11k_core_firmware_request(ab, filename); ++ if (!IS_ERR(fw_entry)) ++ goto success; ++ + fw_entry = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_CAL_FILE); + if (IS_ERR(fw_entry)) { + ret = PTR_ERR(fw_entry); +@@ -2076,6 +2085,7 @@ static int ath11k_qmi_load_bdf_qmi(struc + goto out; + } + ++success: + fw_size = min_t(u32, ab->hw_params.fw.board_size, fw_entry->size); + ret = ath11k_qmi_load_file_target_mem(ab, fw_entry->data, fw_size, file_type); + if (ret < 0) { +@@ -2083,8 +2093,7 @@ static int ath11k_qmi_load_bdf_qmi(struc + goto out_qmi_cal; + } + +- ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi caldata downloaded: type: %u\n", +- file_type); ++ ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi caldata type: %u\n", file_type); + + out_qmi_cal: + release_firmware(fw_entry); diff --git a/package/kernel/mac80211/patches/ath11k/0019-ath11k-add-caldata-download-support-from-EEPROM.patch b/package/kernel/mac80211/patches/ath11k/0019-ath11k-add-caldata-download-support-from-EEPROM.patch new file mode 100644 index 00000000000000..22993dac3e904b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0019-ath11k-add-caldata-download-support-from-EEPROM.patch @@ -0,0 +1,282 @@ +From 4ba3b05ebd0c3e98c7dd8c7ee03aed9d80299b79 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Tue, 28 Sep 2021 12:05:39 +0300 +Subject: [PATCH] ath11k: add caldata download support from EEPROM + +Firmware updates EEPROM support capability in QMI FW caps, send QMI BDF +download request message with file type EEPROM, to get caldata download +from EEPROM. Firmware takes more time to update cal data from EEPROM, so +increase QMI timeout. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721201927.100369-5-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/qmi.c | 139 +++++++++++++++++++++----- + drivers/net/wireless/ath/ath11k/qmi.h | 16 ++- + 2 files changed, 127 insertions(+), 28 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -951,6 +951,78 @@ static struct qmi_elem_info qmi_wlanfw_c + num_macs), + }, + { ++ .data_type = QMI_OPT_FLAG, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x16, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ voltage_mv_valid), ++ }, ++ { ++ .data_type = QMI_UNSIGNED_4_BYTE, ++ .elem_len = 1, ++ .elem_size = sizeof(u32), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x16, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ voltage_mv), ++ }, ++ { ++ .data_type = QMI_OPT_FLAG, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x17, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ time_freq_hz_valid), ++ }, ++ { ++ .data_type = QMI_UNSIGNED_4_BYTE, ++ .elem_len = 1, ++ .elem_size = sizeof(u32), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x17, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ time_freq_hz), ++ }, ++ { ++ .data_type = QMI_OPT_FLAG, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x18, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ otp_version_valid), ++ }, ++ { ++ .data_type = QMI_UNSIGNED_4_BYTE, ++ .elem_len = 1, ++ .elem_size = sizeof(u32), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x18, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ otp_version), ++ }, ++ { ++ .data_type = QMI_OPT_FLAG, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x19, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ eeprom_read_timeout_valid), ++ }, ++ { ++ .data_type = QMI_UNSIGNED_4_BYTE, ++ .elem_len = 1, ++ .elem_size = sizeof(u32), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x19, ++ .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, ++ eeprom_read_timeout), ++ }, ++ { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, +@@ -1846,8 +1918,8 @@ static int ath11k_qmi_request_target_cap + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + +- ret = qmi_txn_init(&ab->qmi.handle, &txn, +- qmi_wlanfw_cap_resp_msg_v01_ei, &resp); ++ ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_cap_resp_msg_v01_ei, ++ &resp); + if (ret < 0) + goto out; + +@@ -1900,6 +1972,12 @@ static int ath11k_qmi_request_target_cap + strlcpy(ab->qmi.target.fw_build_id, resp.fw_build_id, + sizeof(ab->qmi.target.fw_build_id)); + ++ if (resp.eeprom_read_timeout_valid) { ++ ab->qmi.target.eeprom_caldata = ++ resp.eeprom_read_timeout; ++ ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi cal data supported from eeprom\n"); ++ } ++ + ath11k_info(ab, "chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x\n", + ab->qmi.target.chip_id, ab->qmi.target.chip_family, + ab->qmi.target.board_id, ab->qmi.target.soc_id); +@@ -1963,7 +2041,8 @@ static int ath11k_qmi_load_file_target_m + req->end = 1; + } + +- if (ab->bus_params.fixed_bdf_addr) { ++ if (ab->bus_params.fixed_bdf_addr || ++ type == ATH11K_QMI_FILE_TYPE_EEPROM) { + req->data_valid = 0; + req->end = 1; + req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; +@@ -2010,7 +2089,8 @@ static int ath11k_qmi_load_file_target_m + goto err_iounmap; + } + +- if (ab->bus_params.fixed_bdf_addr) { ++ if (ab->bus_params.fixed_bdf_addr || ++ type == ATH11K_QMI_FILE_TYPE_EEPROM) { + remaining = 0; + } else { + remaining -= req->data_len; +@@ -2039,6 +2119,7 @@ static int ath11k_qmi_load_bdf_qmi(struc + struct ath11k_board_data bd; + u32 fw_size, file_type; + int ret = 0, bdf_type; ++ const u8 *tmp; + + memset(&bd, 0, sizeof(bd)); + ret = ath11k_core_fetch_bdf(ab, &bd); +@@ -2063,31 +2144,38 @@ static int ath11k_qmi_load_bdf_qmi(struc + goto out; + } + +- /* QCA6390 does not support cal data file, skip it */ ++ /* QCA6390 does not support cal data, skip it */ + if (bdf_type == ATH11K_QMI_BDF_TYPE_ELF) + goto out; + +- file_type = ATH11K_QMI_FILE_TYPE_CALDATA; ++ if (ab->qmi.target.eeprom_caldata) { ++ file_type = ATH11K_QMI_FILE_TYPE_EEPROM; ++ tmp = filename; ++ fw_size = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; ++ } else { ++ file_type = ATH11K_QMI_FILE_TYPE_CALDATA; + +- /* cal--.bin */ +- snprintf(filename, sizeof(filename), "cal-%s-%s.bin", +- ath11k_bus_str(ab->hif.bus), dev_name(dev)); +- fw_entry = ath11k_core_firmware_request(ab, filename); +- if (!IS_ERR(fw_entry)) +- goto success; +- +- fw_entry = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_CAL_FILE); +- if (IS_ERR(fw_entry)) { +- ret = PTR_ERR(fw_entry); +- ath11k_warn(ab, +- "qmi failed to load CAL data file:%s\n", +- filename); +- goto out; ++ /* cal--.bin */ ++ snprintf(filename, sizeof(filename), "cal-%s-%s.bin", ++ ath11k_bus_str(ab->hif.bus), dev_name(dev)); ++ fw_entry = ath11k_core_firmware_request(ab, filename); ++ if (!IS_ERR(fw_entry)) ++ goto success; ++ ++ fw_entry = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_CAL_FILE); ++ if (IS_ERR(fw_entry)) { ++ ret = PTR_ERR(fw_entry); ++ ath11k_warn(ab, ++ "qmi failed to load CAL data file:%s\n", ++ filename); ++ goto out; ++ } ++success: ++ fw_size = min_t(u32, ab->hw_params.fw.board_size, fw_entry->size); ++ tmp = fw_entry->data; + } + +-success: +- fw_size = min_t(u32, ab->hw_params.fw.board_size, fw_entry->size); +- ret = ath11k_qmi_load_file_target_mem(ab, fw_entry->data, fw_size, file_type); ++ ret = ath11k_qmi_load_file_target_mem(ab, tmp, fw_size, file_type); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to load caldata\n"); + goto out_qmi_cal; +@@ -2096,7 +2184,8 @@ success: + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi caldata type: %u\n", file_type); + + out_qmi_cal: +- release_firmware(fw_entry); ++ if (!ab->qmi.target.eeprom_caldata) ++ release_firmware(fw_entry); + out: + ath11k_core_free_bdf(ab, &bd); + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi BDF download sequence completed\n"); +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -10,7 +10,7 @@ + #include + + #define ATH11K_HOST_VERSION_STRING "WIN" +-#define ATH11K_QMI_WLANFW_TIMEOUT_MS 5000 ++#define ATH11K_QMI_WLANFW_TIMEOUT_MS 10000 + #define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64 + #define ATH11K_QMI_CALDB_ADDRESS 0x4BA00000 + #define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 +@@ -42,6 +42,7 @@ struct ath11k_base; + enum ath11k_qmi_file_type { + ATH11K_QMI_FILE_TYPE_BDF_GOLDEN, + ATH11K_QMI_FILE_TYPE_CALDATA, ++ ATH11K_QMI_FILE_TYPE_EEPROM, + ATH11K_QMI_MAX_FILE_TYPE, + }; + +@@ -102,6 +103,7 @@ struct target_info { + u32 board_id; + u32 soc_id; + u32 fw_version; ++ u32 eeprom_caldata; + char fw_build_timestamp[ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1]; + char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; + char bdf_ext[ATH11K_QMI_BDF_EXT_STR_LENGTH]; +@@ -133,7 +135,7 @@ struct ath11k_qmi { + wait_queue_head_t cold_boot_waitq; + }; + +-#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 189 ++#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 261 + #define QMI_WLANFW_HOST_CAP_REQ_V01 0x0034 + #define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN 7 + #define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 +@@ -283,7 +285,7 @@ struct qmi_wlanfw_fw_cold_cal_done_ind_m + }; + + #define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 +-#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 207 ++#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 235 + #define QMI_WLANFW_CAP_REQ_V01 0x0024 + #define QMI_WLANFW_CAP_RESP_V01 0x0024 + +@@ -364,6 +366,14 @@ struct qmi_wlanfw_cap_resp_msg_v01 { + char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; + u8 num_macs_valid; + u8 num_macs; ++ u8 voltage_mv_valid; ++ u32 voltage_mv; ++ u8 time_freq_hz_valid; ++ u32 time_freq_hz; ++ u8 otp_version_valid; ++ u32 otp_version; ++ u8 eeprom_read_timeout_valid; ++ u32 eeprom_read_timeout; + }; + + struct qmi_wlanfw_cap_req_msg_v01 { diff --git a/package/kernel/mac80211/patches/ath11k/0020-ath11k-Replace-one-element-array-with-flexible-array.patch b/package/kernel/mac80211/patches/ath11k/0020-ath11k-Replace-one-element-array-with-flexible-array.patch new file mode 100644 index 00000000000000..ec23c198ebe31a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0020-ath11k-Replace-one-element-array-with-flexible-array.patch @@ -0,0 +1,144 @@ +From b2549465cdeac3847487ce88b15ca47c37b60b88 Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Tue, 28 Sep 2021 12:05:44 +0300 +Subject: [PATCH] ath11k: Replace one-element array with flexible-array member + +There is a regular need in the kernel to provide a way to declare having a +dynamically sized set of trailing elements in a structure. Kernel code +should always use "flexible array members"[1] for these cases. The older +style of one-element or zero-length arrays should no longer be used[2]. + +Refactor the code a bit according to the use of a flexible-array member in +struct scan_chan_list_params instead of a one-element array, and use the +struct_size() helper. + +Also, save 25 (too many) bytes that were being allocated: + +$ pahole -C channel_param drivers/net/wireless/ath/ath11k/reg.o +struct channel_param { + u8 chan_id; /* 0 1 */ + u8 pwr; /* 1 1 */ + u32 mhz; /* 2 4 */ + + /* Bitfield combined with next fields */ + + u32 half_rate:1; /* 4:16 4 */ + u32 quarter_rate:1; /* 4:17 4 */ + u32 dfs_set:1; /* 4:18 4 */ + u32 dfs_set_cfreq2:1; /* 4:19 4 */ + u32 is_chan_passive:1; /* 4:20 4 */ + u32 allow_ht:1; /* 4:21 4 */ + u32 allow_vht:1; /* 4:22 4 */ + u32 allow_he:1; /* 4:23 4 */ + u32 set_agile:1; /* 4:24 4 */ + u32 psc_channel:1; /* 4:25 4 */ + + /* XXX 6 bits hole, try to pack */ + + u32 phy_mode; /* 8 4 */ + u32 cfreq1; /* 12 4 */ + u32 cfreq2; /* 16 4 */ + char maxpower; /* 20 1 */ + char minpower; /* 21 1 */ + char maxregpower; /* 22 1 */ + u8 antennamax; /* 23 1 */ + u8 reg_class_id; /* 24 1 */ + + /* size: 25, cachelines: 1, members: 21 */ + /* sum members: 23 */ + /* sum bitfield members: 10 bits, bit holes: 1, sum bit holes: 6 bits */ + /* last cacheline: 25 bytes */ +} __attribute__((__packed__)); + +as previously, sizeof(struct scan_chan_list_params) was 32 bytes: + +$ pahole -C scan_chan_list_params drivers/net/wireless/ath/ath11k/reg.o +struct scan_chan_list_params { + u32 pdev_id; /* 0 4 */ + u16 nallchans; /* 4 2 */ + struct channel_param ch_param[1]; /* 6 25 */ + + /* size: 32, cachelines: 1, members: 3 */ + /* padding: 1 */ + /* last cacheline: 32 bytes */ +}; + +and now with the flexible array transformation it is just 8 bytes: + +$ pahole -C scan_chan_list_params drivers/net/wireless/ath/ath11k/reg.o +struct scan_chan_list_params { + u32 pdev_id; /* 0 4 */ + u16 nallchans; /* 4 2 */ + struct channel_param ch_param[]; /* 6 0 */ + + /* size: 8, cachelines: 1, members: 3 */ + /* padding: 2 */ + /* last cacheline: 8 bytes */ +}; + +This helps with the ongoing efforts to globally enable -Warray-bounds and +get us closer to being able to tighten the FORTIFY_SOURCE routines on +memcpy(). + +This issue was found with the help of Coccinelle and audited and fixed, +manually. + +[1] https://en.wikipedia.org/wiki/Flexible_array_member +[2] https://www.kernel.org/doc/html/v5.10/process/deprecated.html#zero-length-and-one-element-arrays + +Link: https://github.com/KSPP/linux/issues/79 +Link: https://github.com/KSPP/linux/issues/109 +Signed-off-by: Gustavo A. R. Silva +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210823172159.GA25800@embeddedor +--- + drivers/net/wireless/ath/ath11k/reg.c | 7 ++----- + drivers/net/wireless/ath/ath11k/wmi.c | 2 +- + drivers/net/wireless/ath/ath11k/wmi.h | 2 +- + 3 files changed, 4 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -97,7 +97,6 @@ int ath11k_reg_update_chan_list(struct a + struct channel_param *ch; + enum nl80211_band band; + int num_channels = 0; +- int params_len; + int i, ret; + + bands = hw->wiphy->bands; +@@ -117,10 +116,8 @@ int ath11k_reg_update_chan_list(struct a + if (WARN_ON(!num_channels)) + return -EINVAL; + +- params_len = sizeof(struct scan_chan_list_params) + +- num_channels * sizeof(struct channel_param); +- params = kzalloc(params_len, GFP_KERNEL); +- ++ params = kzalloc(struct_size(params, ch_param, num_channels), ++ GFP_KERNEL); + if (!params) + return -ENOMEM; + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -2303,7 +2303,7 @@ int ath11k_wmi_send_scan_chan_list_cmd(s + u16 num_send_chans, num_sends = 0, max_chan_limit = 0; + u32 *reg1, *reg2; + +- tchan_info = &chan_list->ch_param[0]; ++ tchan_info = chan_list->ch_param; + while (chan_list->nallchans) { + len = sizeof(*cmd) + TLV_HDR_SIZE; + max_chan_limit = (wmi->wmi_ab->max_msg_len[ar->pdev_idx] - len) / +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3659,7 +3659,7 @@ struct wmi_stop_scan_cmd { + struct scan_chan_list_params { + u32 pdev_id; + u16 nallchans; +- struct channel_param ch_param[1]; ++ struct channel_param ch_param[]; + }; + + struct wmi_scan_chan_list_cmd { diff --git a/package/kernel/mac80211/patches/ath11k/0023-ath11k-Add-support-for-RX-decapsulation-offload.patch b/package/kernel/mac80211/patches/ath11k/0023-ath11k-Add-support-for-RX-decapsulation-offload.patch new file mode 100644 index 00000000000000..06ffef92934afd --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0023-ath11k-Add-support-for-RX-decapsulation-offload.patch @@ -0,0 +1,596 @@ +From 2167fa606c0f0e64b95a04f9bc42d9fd5360838a Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Tue, 28 Sep 2021 12:05:40 +0300 +Subject: [PATCH] ath11k: Add support for RX decapsulation offload + +Add support for rx decapsulation offload by advertising +the support to mac80211 during registration. Also ensure +the frames have the RX_FLAG_8023 flag set in decap offload +frames before passing to mac80211. + +Since the packets delivered to the driver are in 802.3 format, these +can be sent to the network core with minimal processing in mac80211. +This helps in releasing some CPU cycles in the host processor and +thereby improving the performance. + +Two exceptions are made before passing decap frames, one is +for EAPOL packets since mac80211 8023 fast rx for the sta +is set only after authorization, other case is for multicast +packets to validate PN in mac80211. In both the cases the +decap frames are converted to 80211 frame and sent to mac80211. + +Ethernet decap can be enabled by using frame_mode modparam: + +insmod ath11k frame_mode=2 + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00844-QCAHKSWPL_SILICONZ-1 v2 + +Co-developed-by: Manikanta Pubbisetty +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721204217.120572-1-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 4 + + drivers/net/wireless/ath/ath11k/dp_rx.c | 194 +++++++++++++-------- + drivers/net/wireless/ath/ath11k/hal_desc.h | 2 + + drivers/net/wireless/ath/ath11k/hw.c | 43 +++++ + drivers/net/wireless/ath/ath11k/hw.h | 2 + + drivers/net/wireless/ath/ath11k/mac.c | 25 ++- + 6 files changed, 198 insertions(+), 72 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -98,6 +98,8 @@ struct ath11k_skb_rxcb { + bool is_first_msdu; + bool is_last_msdu; + bool is_continuation; ++ bool is_mcbc; ++ bool is_eapol; + struct hal_rx_desc *rx_desc; + u8 err_rel_src; + u8 err_code; +@@ -105,6 +107,8 @@ struct ath11k_skb_rxcb { + u8 unmapped; + u8 is_frag; + u8 tid; ++ u16 peer_id; ++ u16 seq_no; + }; + + enum ath11k_hw_rev { +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -270,6 +270,18 @@ static bool ath11k_dp_rx_h_attn_is_mcbc( + __le32_to_cpu(attn->info1))); + } + ++static bool ath11k_dp_rxdesc_mac_addr2_valid(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) ++{ ++ return ab->hw_params.hw_ops->rx_desc_mac_addr2_valid(desc); ++} ++ ++static u8 *ath11k_dp_rxdesc_mpdu_start_addr2(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) ++{ ++ return ab->hw_params.hw_ops->rx_desc_mpdu_start_addr2(desc); ++} ++ + static void ath11k_dp_service_mon_ring(struct timer_list *t) + { + struct ath11k_base *ab = from_timer(ab, t, mon_reap_timer); +@@ -2156,6 +2168,7 @@ static void ath11k_dp_rx_h_undecap(struc + { + u8 *first_hdr; + u8 decap; ++ struct ethhdr *ehdr; + + first_hdr = ath11k_dp_rx_h_80211_hdr(ar->ab, rx_desc); + decap = ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rx_desc); +@@ -2170,9 +2183,22 @@ static void ath11k_dp_rx_h_undecap(struc + decrypted); + break; + case DP_RX_DECAP_TYPE_ETHERNET2_DIX: +- /* TODO undecap support for middle/last msdu's of amsdu */ +- ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, +- enctype, status); ++ ehdr = (struct ethhdr *)msdu->data; ++ ++ /* mac80211 allows fast path only for authorized STA */ ++ if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) { ++ ATH11K_SKB_RXCB(msdu)->is_eapol = true; ++ ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, ++ enctype, status); ++ break; ++ } ++ ++ /* PN for mcast packets will be validated in mac80211; ++ * remove eth header and add 802.11 header. ++ */ ++ if (ATH11K_SKB_RXCB(msdu)->is_mcbc && decrypted) ++ ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, ++ enctype, status); + break; + case DP_RX_DECAP_TYPE_8023: + /* TODO: Handle undecap for these formats */ +@@ -2180,35 +2206,62 @@ static void ath11k_dp_rx_h_undecap(struc + } + } + ++static struct ath11k_peer * ++ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) ++{ ++ struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); ++ struct hal_rx_desc *rx_desc = rxcb->rx_desc; ++ struct ath11k_peer *peer = NULL; ++ ++ lockdep_assert_held(&ab->base_lock); ++ ++ if (rxcb->peer_id) ++ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); ++ ++ if (peer) ++ return peer; ++ ++ if (!rx_desc || !(ath11k_dp_rxdesc_mac_addr2_valid(ab, rx_desc))) ++ return NULL; ++ ++ peer = ath11k_peer_find_by_addr(ab, ++ ath11k_dp_rxdesc_mpdu_start_addr2(ab, rx_desc)); ++ return peer; ++} ++ + static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) + { +- bool fill_crypto_hdr, mcast; ++ bool fill_crypto_hdr; + enum hal_encrypt_type enctype; + bool is_decrypted = false; ++ struct ath11k_skb_rxcb *rxcb; + struct ieee80211_hdr *hdr; + struct ath11k_peer *peer; + struct rx_attention *rx_attention; + u32 err_bitmap; + +- hdr = (struct ieee80211_hdr *)msdu->data; +- + /* PN for multicast packets will be checked in mac80211 */ ++ rxcb = ATH11K_SKB_RXCB(msdu); ++ fill_crypto_hdr = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc); ++ rxcb->is_mcbc = fill_crypto_hdr; + +- mcast = is_multicast_ether_addr(hdr->addr1); +- fill_crypto_hdr = mcast; ++ if (rxcb->is_mcbc) { ++ rxcb->peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ar->ab, rx_desc); ++ rxcb->seq_no = ath11k_dp_rx_h_mpdu_start_seq_no(ar->ab, rx_desc); ++ } + + spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr2); ++ peer = ath11k_dp_rx_h_find_peer(ar->ab, msdu); + if (peer) { +- if (mcast) ++ if (rxcb->is_mcbc) + enctype = peer->sec_type_grp; + else + enctype = peer->sec_type; + } else { +- enctype = HAL_ENCRYPT_TYPE_OPEN; ++ enctype = ath11k_dp_rx_h_mpdu_start_enctype(ar->ab, rx_desc); + } + spin_unlock_bh(&ar->ab->base_lock); + +@@ -2247,8 +2300,11 @@ static void ath11k_dp_rx_h_mpdu(struct a + if (!is_decrypted || fill_crypto_hdr) + return; + +- hdr = (void *)msdu->data; +- hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); ++ if (ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rx_desc) != ++ DP_RX_DECAP_TYPE_ETHERNET2_DIX) { ++ hdr = (void *)msdu->data; ++ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); ++ } + } + + static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, +@@ -2365,51 +2421,49 @@ static void ath11k_dp_rx_h_ppdu(struct a + ath11k_dp_rx_h_rate(ar, rx_desc, rx_status); + } + +-static char *ath11k_print_get_tid(struct ieee80211_hdr *hdr, char *out, +- size_t size) +-{ +- u8 *qc; +- int tid; +- +- if (!ieee80211_is_data_qos(hdr->frame_control)) +- return ""; +- +- qc = ieee80211_get_qos_ctl(hdr); +- tid = *qc & IEEE80211_QOS_CTL_TID_MASK; +- snprintf(out, size, "tid %d", tid); +- +- return out; +-} +- + static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *napi, +- struct sk_buff *msdu) ++ struct sk_buff *msdu, ++ struct ieee80211_rx_status *status) + { + static const struct ieee80211_radiotap_he known = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; +- struct ieee80211_rx_status *status; +- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; ++ struct ieee80211_rx_status *rx_status; + struct ieee80211_radiotap_he *he = NULL; +- char tid[32]; +- +- status = IEEE80211_SKB_RXCB(msdu); +- if (status->encoding == RX_ENC_HE) { ++ struct ieee80211_sta *pubsta = NULL; ++ struct ath11k_peer *peer; ++ struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); ++ u8 decap = DP_RX_DECAP_TYPE_RAW; ++ bool is_mcbc = rxcb->is_mcbc; ++ bool is_eapol = rxcb->is_eapol; ++ ++ if (status->encoding == RX_ENC_HE && ++ !(status->flag & RX_FLAG_RADIOTAP_HE) && ++ !(status->flag & RX_FLAG_SKIP_MONITOR)) { + he = skb_push(msdu, sizeof(known)); + memcpy(he, &known, sizeof(known)); + status->flag |= RX_FLAG_RADIOTAP_HE; + } + ++ if (!(status->flag & RX_FLAG_ONLY_MONITOR)) ++ decap = ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rxcb->rx_desc); ++ ++ spin_lock_bh(&ar->ab->base_lock); ++ peer = ath11k_dp_rx_h_find_peer(ar->ab, msdu); ++ if (peer && peer->sta) ++ pubsta = peer->sta; ++ spin_unlock_bh(&ar->ab->base_lock); ++ + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, +- "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", ++ "rx skb %pK len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + msdu, + msdu->len, +- ieee80211_get_SA(hdr), +- ath11k_print_get_tid(hdr, tid, sizeof(tid)), +- is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? +- "mcast" : "ucast", +- (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, ++ peer ? peer->addr : NULL, ++ rxcb->tid, ++ is_mcbc ? "mcast" : "ucast", ++ rxcb->seq_no, + (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", + (status->encoding == RX_ENC_HT) ? "ht" : "", + (status->encoding == RX_ENC_VHT) ? "vht" : "", +@@ -2429,22 +2483,32 @@ static void ath11k_dp_rx_deliver_msdu(st + ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_RX, NULL, "dp rx msdu: ", + msdu->data, msdu->len); + ++ rx_status = IEEE80211_SKB_RXCB(msdu); ++ *rx_status = *status; ++ + /* TODO: trace rx packet */ + +- ieee80211_rx_napi(ar->hw, NULL, msdu, napi); ++ /* PN for multicast packets are not validate in HW, ++ * so skip 802.3 rx path ++ * Also, fast_rx expectes the STA to be authorized, hence ++ * eapol packets are sent in slow path. ++ */ ++ if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol && ++ !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) ++ rx_status->flag |= RX_FLAG_8023; ++ ++ ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); + } + + static int ath11k_dp_rx_process_msdu(struct ath11k *ar, + struct sk_buff *msdu, +- struct sk_buff_head *msdu_list) ++ struct sk_buff_head *msdu_list, ++ struct ieee80211_rx_status *rx_status) + { + struct ath11k_base *ab = ar->ab; + struct hal_rx_desc *rx_desc, *lrx_desc; + struct rx_attention *rx_attention; +- struct ieee80211_rx_status rx_status = {0}; +- struct ieee80211_rx_status *status; + struct ath11k_skb_rxcb *rxcb; +- struct ieee80211_hdr *hdr; + struct sk_buff *last_buf; + u8 l3_pad_bytes; + u8 *hdr_status; +@@ -2500,19 +2564,11 @@ static int ath11k_dp_rx_process_msdu(str + } + } + +- hdr = (struct ieee80211_hdr *)msdu->data; +- +- /* Process only data frames */ +- if (!ieee80211_is_data(hdr->frame_control)) +- return -EINVAL; +- +- ath11k_dp_rx_h_ppdu(ar, rx_desc, &rx_status); +- ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, &rx_status); ++ ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); ++ ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status); + +- rx_status.flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; ++ rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; + +- status = IEEE80211_SKB_RXCB(msdu); +- *status = rx_status; + return 0; + + free_out: +@@ -2527,6 +2583,7 @@ static void ath11k_dp_rx_process_receive + struct ath11k_skb_rxcb *rxcb; + struct sk_buff *msdu; + struct ath11k *ar; ++ struct ieee80211_rx_status rx_status = {0}; + u8 mac_id; + int ret; + +@@ -2549,7 +2606,7 @@ static void ath11k_dp_rx_process_receive + continue; + } + +- ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list); ++ ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status); + if (ret) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "Unable to process msdu %d", ret); +@@ -2557,7 +2614,7 @@ static void ath11k_dp_rx_process_receive + continue; + } + +- ath11k_dp_rx_deliver_msdu(ar, napi, msdu); ++ ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); + (*quota)--; + } + +@@ -2639,10 +2696,14 @@ try_again: + RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + rxcb->is_continuation = !!(desc.rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); +- rxcb->mac_id = mac_id; ++ rxcb->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID, ++ desc.rx_mpdu_info.meta_data); ++ rxcb->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM, ++ desc.rx_mpdu_info.info0); + rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM, + desc.info0); + ++ rxcb->mac_id = mac_id; + __skb_queue_tail(&msdu_list, msdu); + + if (total_msdu_reaped >= quota && !rxcb->is_continuation) { +@@ -3944,7 +4005,6 @@ static void ath11k_dp_rx_wbm_err(struct + { + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_rx_status rxs = {0}; +- struct ieee80211_rx_status *status; + bool drop = true; + + switch (rxcb->err_rel_src) { +@@ -3964,10 +4024,7 @@ static void ath11k_dp_rx_wbm_err(struct + return; + } + +- status = IEEE80211_SKB_RXCB(msdu); +- *status = rxs; +- +- ath11k_dp_rx_deliver_msdu(ar, napi, msdu); ++ ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rxs); + } + + int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab, +@@ -4851,7 +4908,7 @@ static int ath11k_dp_rx_mon_deliver(stru + { + struct ath11k_pdev_dp *dp = &ar->dp; + struct sk_buff *mon_skb, *skb_next, *header; +- struct ieee80211_rx_status *rxs = &dp->rx_status, *status; ++ struct ieee80211_rx_status *rxs = &dp->rx_status; + + mon_skb = ath11k_dp_rx_mon_merg_msdus(ar, mac_id, head_msdu, + tail_msdu, rxs); +@@ -4877,10 +4934,7 @@ static int ath11k_dp_rx_mon_deliver(stru + } + rxs->flag |= RX_FLAG_ONLY_MONITOR; + +- status = IEEE80211_SKB_RXCB(mon_skb); +- *status = *rxs; +- +- ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb); ++ ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb, rxs); + mon_skb = skb_next; + } while (mon_skb); + rxs->flag = 0; +--- a/drivers/net/wireless/ath/ath11k/hal_desc.h ++++ b/drivers/net/wireless/ath/ath11k/hal_desc.h +@@ -496,6 +496,8 @@ struct hal_tlv_hdr { + #define RX_MPDU_DESC_INFO0_DA_IDX_TIMEOUT BIT(29) + #define RX_MPDU_DESC_INFO0_RAW_MPDU BIT(30) + ++#define RX_MPDU_DESC_META_DATA_PEER_ID GENMASK(15, 0) ++ + struct rx_mpdu_desc { + u32 info0; /* %RX_MPDU_DESC_INFO */ + u32 meta_data; +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -374,6 +374,17 @@ static void ath11k_hw_ipq8074_rx_desc_se + desc->u.ipq8074.msdu_start.info1 = __cpu_to_le32(info); + } + ++static bool ath11k_hw_ipq8074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) ++{ ++ return __le32_to_cpu(desc->u.ipq8074.mpdu_start.info1) & ++ RX_MPDU_START_INFO1_MAC_ADDR2_VALID; ++} ++ ++static u8 *ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) ++{ ++ return desc->u.ipq8074.mpdu_start.addr2; ++} ++ + static + struct rx_attention *ath11k_hw_ipq8074_rx_desc_get_attention(struct hal_rx_desc *desc) + { +@@ -545,6 +556,17 @@ static u8 *ath11k_hw_qcn9074_rx_desc_get + return &desc->u.qcn9074.msdu_payload[0]; + } + ++static bool ath11k_hw_ipq9074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) ++{ ++ return __le32_to_cpu(desc->u.qcn9074.mpdu_start.info11) & ++ RX_MPDU_START_INFO11_MAC_ADDR2_VALID; ++} ++ ++static u8 *ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) ++{ ++ return desc->u.qcn9074.mpdu_start.addr2; ++} ++ + static bool ath11k_hw_wcn6855_rx_desc_get_first_msdu(struct hal_rx_desc *desc) + { + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU_WCN6855, +@@ -705,6 +727,17 @@ static u8 *ath11k_hw_wcn6855_rx_desc_get + return &desc->u.wcn6855.msdu_payload[0]; + } + ++static bool ath11k_hw_wcn6855_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) ++{ ++ return __le32_to_cpu(desc->u.wcn6855.mpdu_start.info1) & ++ RX_MPDU_START_INFO1_MAC_ADDR2_VALID; ++} ++ ++static u8 *ath11k_hw_wcn6855_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) ++{ ++ return desc->u.wcn6855.mpdu_start.addr2; ++} ++ + static void ath11k_hw_wcn6855_reo_setup(struct ath11k_base *ab) + { + u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; +@@ -801,6 +834,8 @@ const struct ath11k_hw_ops ipq8074_ops = + .rx_desc_get_msdu_payload = ath11k_hw_ipq8074_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_ipq8074_reo_setup, + .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, ++ .rx_desc_mac_addr2_valid = ath11k_hw_ipq8074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + }; + + const struct ath11k_hw_ops ipq6018_ops = { +@@ -837,6 +872,8 @@ const struct ath11k_hw_ops ipq6018_ops = + .rx_desc_get_msdu_payload = ath11k_hw_ipq8074_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_ipq8074_reo_setup, + .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, ++ .rx_desc_mac_addr2_valid = ath11k_hw_ipq8074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + }; + + const struct ath11k_hw_ops qca6390_ops = { +@@ -873,6 +910,8 @@ const struct ath11k_hw_ops qca6390_ops = + .rx_desc_get_msdu_payload = ath11k_hw_ipq8074_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_ipq8074_reo_setup, + .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, ++ .rx_desc_mac_addr2_valid = ath11k_hw_ipq8074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + }; + + const struct ath11k_hw_ops qcn9074_ops = { +@@ -909,6 +948,8 @@ const struct ath11k_hw_ops qcn9074_ops = + .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_ipq8074_reo_setup, + .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, ++ .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, + }; + + const struct ath11k_hw_ops wcn6855_ops = { +@@ -945,6 +986,8 @@ const struct ath11k_hw_ops wcn6855_ops = + .rx_desc_get_msdu_payload = ath11k_hw_wcn6855_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_wcn6855_reo_setup, + .mpdu_info_get_peerid = ath11k_hw_wcn6855_mpdu_info_get_peerid, ++ .rx_desc_mac_addr2_valid = ath11k_hw_wcn6855_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_wcn6855_rx_desc_mpdu_start_addr2, + }; + + #define ATH11K_TX_RING_MASK_0 0x1 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -209,6 +209,8 @@ struct ath11k_hw_ops { + u8 *(*rx_desc_get_msdu_payload)(struct hal_rx_desc *desc); + void (*reo_setup)(struct ath11k_base *ab); + u16 (*mpdu_info_get_peerid)(u8 *tlv_data); ++ bool (*rx_desc_mac_addr2_valid)(struct hal_rx_desc *desc); ++ u8* (*rx_desc_mpdu_start_addr2)(struct hal_rx_desc *desc); + }; + + extern const struct ath11k_hw_ops ipq8074_ops; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5380,7 +5380,8 @@ static void ath11k_mac_op_update_vif_off + if (ath11k_frame_mode != ATH11K_HW_TXRX_ETHERNET || + (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP)) +- vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; ++ vif->offload_flags &= ~(IEEE80211_OFFLOAD_ENCAP_ENABLED | ++ IEEE80211_OFFLOAD_DECAP_ENABLED); + + if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) + param_value = ATH11K_HW_TXRX_ETHERNET; +@@ -5396,6 +5397,22 @@ static void ath11k_mac_op_update_vif_off + arvif->vdev_id, ret); + vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; + } ++ ++ param_id = WMI_VDEV_PARAM_RX_DECAP_TYPE; ++ if (vif->offload_flags & IEEE80211_OFFLOAD_DECAP_ENABLED) ++ param_value = ATH11K_HW_TXRX_ETHERNET; ++ else if (test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) ++ param_value = ATH11K_HW_TXRX_RAW; ++ else ++ param_value = ATH11K_HW_TXRX_NATIVE_WIFI; ++ ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ param_id, param_value); ++ if (ret) { ++ ath11k_warn(ab, "failed to set vdev %d rx decap mode: %d\n", ++ arvif->vdev_id, ret); ++ vif->offload_flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED; ++ } + } + + static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, +@@ -7560,7 +7577,11 @@ static int __ath11k_mac_register(struct + ieee80211_hw_set(ar->hw, QUEUE_CONTROL); + ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG); + ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK); +- ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD); ++ ++ if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET) { ++ ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD); ++ ieee80211_hw_set(ar->hw, SUPPORTS_RX_DECAP_OFFLOAD); ++ } + + if (cap->nss_ratio_enabled) + ieee80211_hw_set(ar->hw, SUPPORTS_VHT_EXT_NSS_BW); diff --git a/package/kernel/mac80211/patches/ath11k/0024-ath11k-Fix-pktlog-lite-rx-events.patch b/package/kernel/mac80211/patches/ath11k/0024-ath11k-Fix-pktlog-lite-rx-events.patch new file mode 100644 index 00000000000000..01c1e8a2610632 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0024-ath11k-Fix-pktlog-lite-rx-events.patch @@ -0,0 +1,186 @@ +From ab18e3bc1c138f2b4358c6905a45afb7289d5086 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Tue, 28 Sep 2021 12:05:40 +0300 +Subject: [PATCH] ath11k: Fix pktlog lite rx events + +Fix sending rx_buf_sz to ath11k_dp_tx_htt_rx_filter_setup() +to enable pktlog full or lite mode. Depending on mode update the +trace buffer with log type full/lite. + +Pktlog lite is a lighter version of pktlog. This can be used to capture +PPDU stats. These are useful for firmware performance debugging. + +pktlog lite dumps are enabled using, + echo "0x0 1" > ath11k/IPQ8074 hw2.0/mac0/pktlog_filter + +Tested On: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01233-QCAHKSWPL_SILICONZ-1 v2 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721212029.142388-1-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/debugfs.c | 25 +++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/dp.h | 1 + + drivers/net/wireless/ath/ath11k/dp_rx.c | 16 ++++++++++++--- + drivers/net/wireless/ath/ath11k/trace.h | 11 ++++++---- + 4 files changed, 42 insertions(+), 11 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -902,7 +902,7 @@ static ssize_t ath11k_write_pktlog_filte + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 rx_filter = 0, ring_id, filter, mode; + u8 buf[128] = {0}; +- int i, ret; ++ int i, ret, rx_buf_sz = 0; + ssize_t rc; + + mutex_lock(&ar->conf_mutex); +@@ -940,6 +940,17 @@ static ssize_t ath11k_write_pktlog_filte + } + } + ++ /* Clear rx filter set for monitor mode and rx status */ ++ for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { ++ ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; ++ ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, ++ HAL_RXDMA_MONITOR_STATUS, ++ rx_buf_sz, &tlv_filter); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); ++ goto out; ++ } ++ } + #define HTT_RX_FILTER_TLV_LITE_MODE \ + (HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ +@@ -955,6 +966,7 @@ static ssize_t ath11k_write_pktlog_filte + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | + HTT_RX_FILTER_TLV_FLAGS_ATTENTION; ++ rx_buf_sz = DP_RX_BUFFER_SIZE; + } else if (mode == ATH11K_PKTLOG_MODE_LITE) { + ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, + HTT_PPDU_STATS_TAG_PKTLOG); +@@ -964,7 +976,12 @@ static ssize_t ath11k_write_pktlog_filte + } + + rx_filter = HTT_RX_FILTER_TLV_LITE_MODE; ++ rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; + } else { ++ rx_buf_sz = DP_RX_BUFFER_SIZE; ++ tlv_filter = ath11k_mac_mon_status_filter_default; ++ rx_filter = tlv_filter.rx_filter; ++ + ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, + HTT_PPDU_STATS_TAG_DEFAULT); + if (ret) { +@@ -988,7 +1005,7 @@ static ssize_t ath11k_write_pktlog_filte + ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_STATUS, +- DP_RX_BUFFER_SIZE, &tlv_filter); ++ rx_buf_sz, &tlv_filter); + + if (ret) { + ath11k_warn(ab, "failed to set rx filter for monitor status ring\n"); +@@ -996,8 +1013,8 @@ static ssize_t ath11k_write_pktlog_filte + } + } + +- ath11k_dbg(ab, ATH11K_DBG_WMI, "pktlog filter %d mode %s\n", +- filter, ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite")); ++ ath11k_info(ab, "pktlog mode %s\n", ++ ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite")); + + ar->debug.pktlog_filter = filter; + ar->debug.pktlog_mode = mode; +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -195,6 +195,7 @@ struct ath11k_pdev_dp { + #define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 + + #define DP_RX_BUFFER_SIZE 2048 ++#define DP_RX_BUFFER_SIZE_LITE 1024 + #define DP_RX_BUFFER_ALIGN_SIZE 128 + + #define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0) +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -3033,6 +3033,8 @@ int ath11k_dp_rx_process_mon_status(stru + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + int num_buffs_reaped = 0; ++ u32 rx_buf_sz; ++ u16 log_type = 0; + + __skb_queue_head_init(&skb_list); + +@@ -3045,8 +3047,16 @@ int ath11k_dp_rx_process_mon_status(stru + memset(&ppdu_info, 0, sizeof(ppdu_info)); + ppdu_info.peer_id = HAL_INVALID_PEERID; + +- if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) +- trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE); ++ if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { ++ log_type = ATH11K_PKTLOG_TYPE_LITE_RX; ++ rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; ++ } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { ++ log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; ++ rx_buf_sz = DP_RX_BUFFER_SIZE; ++ } ++ ++ if (log_type) ++ trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + + hal_status = ath11k_hal_rx_parse_mon_status(ab, &ppdu_info, skb); + +@@ -3074,7 +3084,7 @@ int ath11k_dp_rx_process_mon_status(stru + ath11k_dp_rx_update_peer_stats(arsta, &ppdu_info); + + if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) +- trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE); ++ trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +--- a/drivers/net/wireless/ath/ath11k/trace.h ++++ b/drivers/net/wireless/ath/ath11k/trace.h +@@ -79,14 +79,15 @@ TRACE_EVENT(ath11k_htt_ppdu_stats, + ); + + TRACE_EVENT(ath11k_htt_rxdesc, +- TP_PROTO(struct ath11k *ar, const void *data, size_t len), ++ TP_PROTO(struct ath11k *ar, const void *data, size_t log_type, size_t len), + +- TP_ARGS(ar, data, len), ++ TP_ARGS(ar, data, log_type, len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->ab->dev)) + __string(driver, dev_driver_string(ar->ab->dev)) + __field(u16, len) ++ __field(u16, log_type) + __dynamic_array(u8, rxdesc, len) + ), + +@@ -94,14 +95,16 @@ TRACE_EVENT(ath11k_htt_rxdesc, + __assign_str(device, dev_name(ar->ab->dev)); + __assign_str(driver, dev_driver_string(ar->ab->dev)); + __entry->len = len; ++ __entry->log_type = log_type; + memcpy(__get_dynamic_array(rxdesc), data, len); + ), + + TP_printk( +- "%s %s rxdesc len %d", ++ "%s %s rxdesc len %d type %d", + __get_str(driver), + __get_str(device), +- __entry->len ++ __entry->len, ++ __entry->log_type + ) + ); + diff --git a/package/kernel/mac80211/patches/ath11k/0025-ath11k-Update-pdev-tx-and-rx-firmware-stats.patch b/package/kernel/mac80211/patches/ath11k/0025-ath11k-Update-pdev-tx-and-rx-firmware-stats.patch new file mode 100644 index 00000000000000..cd654eca4686bc --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0025-ath11k-Update-pdev-tx-and-rx-firmware-stats.patch @@ -0,0 +1,250 @@ +From f394e4eae8e2c0579063e5473f1e321d22d3fe43 Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Tue, 28 Sep 2021 12:05:40 +0300 +Subject: [PATCH] ath11k: Update pdev tx and rx firmware stats + +Update the fields of pdev tx and tx firmware stats structure. +Missing fields resulted in wrong fw stats to be displayed as below. + +root@OpenWrt:/# cat /sys/kernel/debug/ath11k/ + ipq8074\ hw2.0/mac0/fw_stats/pdev_stats | grep Illegal +Illegal rate phy errors 36839112 + +Note that this struct was missing its members from initial driver +support and this change doesn't introduce/modify the structure for +firmware changes. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 v2 + +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721212029.142388-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 29 ++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 41 ++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/wmi.h | 42 ++++++++++++++++++++++++++ + 3 files changed, 111 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -819,12 +819,15 @@ struct ath11k_fw_stats_pdev { + s32 hw_reaped; + /* Num underruns */ + s32 underrun; ++ /* Num hw paused */ ++ u32 hw_paused; + /* Num PPDUs cleaned up in TX abort */ + s32 tx_abort; + /* Num MPDUs requeued by SW */ + s32 mpdus_requeued; + /* excessive retries */ + u32 tx_ko; ++ u32 tx_xretry; + /* data hw rate code */ + u32 data_rc; + /* Scheduler self triggers */ +@@ -845,6 +848,30 @@ struct ath11k_fw_stats_pdev { + u32 phy_underrun; + /* MPDU is more than txop limit */ + u32 txop_ovf; ++ /* Num sequences posted */ ++ u32 seq_posted; ++ /* Num sequences failed in queueing */ ++ u32 seq_failed_queueing; ++ /* Num sequences completed */ ++ u32 seq_completed; ++ /* Num sequences restarted */ ++ u32 seq_restarted; ++ /* Num of MU sequences posted */ ++ u32 mu_seq_posted; ++ /* Num MPDUs flushed by SW, HWPAUSED, SW TXABORT ++ * (Reset,channel change) ++ */ ++ s32 mpdus_sw_flush; ++ /* Num MPDUs filtered by HW, all filter condition (TTL expired) */ ++ s32 mpdus_hw_filter; ++ /* Num MPDUs truncated by PDG (TXOP, TBTT, ++ * PPDU_duration based on rate, dyn_bw) ++ */ ++ s32 mpdus_truncated; ++ /* Num MPDUs that was tried but didn't receive ACK or BA */ ++ s32 mpdus_ack_failed; ++ /* Num MPDUs that was dropped du to expiry. */ ++ s32 mpdus_expired; + + /* PDEV RX stats */ + /* Cnts any change in ring routing mid-ppdu */ +@@ -870,6 +897,8 @@ struct ath11k_fw_stats_pdev { + s32 phy_err_drop; + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + s32 mpdu_errs; ++ /* Num overflow errors */ ++ s32 rx_ovfl_errs; + }; + + struct ath11k_fw_stats_vdev { +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -5252,9 +5252,11 @@ ath11k_wmi_pull_pdev_stats_tx(const stru + dst->hw_queued = src->hw_queued; + dst->hw_reaped = src->hw_reaped; + dst->underrun = src->underrun; ++ dst->hw_paused = src->hw_paused; + dst->tx_abort = src->tx_abort; + dst->mpdus_requeued = src->mpdus_requeued; + dst->tx_ko = src->tx_ko; ++ dst->tx_xretry = src->tx_xretry; + dst->data_rc = src->data_rc; + dst->self_triggers = src->self_triggers; + dst->sw_retry_failure = src->sw_retry_failure; +@@ -5265,6 +5267,16 @@ ath11k_wmi_pull_pdev_stats_tx(const stru + dst->stateless_tid_alloc_failure = src->stateless_tid_alloc_failure; + dst->phy_underrun = src->phy_underrun; + dst->txop_ovf = src->txop_ovf; ++ dst->seq_posted = src->seq_posted; ++ dst->seq_failed_queueing = src->seq_failed_queueing; ++ dst->seq_completed = src->seq_completed; ++ dst->seq_restarted = src->seq_restarted; ++ dst->mu_seq_posted = src->mu_seq_posted; ++ dst->mpdus_sw_flush = src->mpdus_sw_flush; ++ dst->mpdus_hw_filter = src->mpdus_hw_filter; ++ dst->mpdus_truncated = src->mpdus_truncated; ++ dst->mpdus_ack_failed = src->mpdus_ack_failed; ++ dst->mpdus_expired = src->mpdus_expired; + } + + static void ath11k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src, +@@ -5284,6 +5296,7 @@ static void ath11k_wmi_pull_pdev_stats_r + dst->phy_errs = src->phy_errs; + dst->phy_err_drop = src->phy_err_drop; + dst->mpdu_errs = src->mpdu_errs; ++ dst->rx_ovfl_errs = src->rx_ovfl_errs; + } + + static void +@@ -5521,11 +5534,15 @@ ath11k_wmi_fw_pdev_tx_stats_fill(const s + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", pdev->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", ++ "Num HW Paused", pdev->hw_paused); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", pdev->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requeued", pdev->mpdus_requeued); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", +- "Excessive retries", pdev->tx_ko); ++ "PPDU OK", pdev->tx_ko); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Excessive retries", pdev->tx_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "HW rate", pdev->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", +@@ -5549,6 +5566,26 @@ ath11k_wmi_fw_pdev_tx_stats_fill(const s + "PHY underrun", pdev->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MPDU is more than txop limit", pdev->txop_ovf); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num sequences posted", pdev->seq_posted); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num seq failed queueing ", pdev->seq_failed_queueing); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num sequences completed ", pdev->seq_completed); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num sequences restarted ", pdev->seq_restarted); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num of MU sequences posted ", pdev->mu_seq_posted); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num of MPDUS SW flushed ", pdev->mpdus_sw_flush); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num of MPDUS HW filtered ", pdev->mpdus_hw_filter); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num of MPDUS truncated ", pdev->mpdus_truncated); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num of MPDUS ACK failed ", pdev->mpdus_ack_failed); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", ++ "Num of MPDUS expired ", pdev->mpdus_expired); + *length = len; + } + +@@ -5593,6 +5630,8 @@ ath11k_wmi_fw_pdev_rx_stats_fill(const s + "PHY errors drops", pdev->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); ++ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", ++ "Overflow errors", pdev->rx_ovfl_errs); + *length = len; + } + +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -4223,6 +4223,9 @@ struct wmi_pdev_stats_tx { + /* Num underruns */ + s32 underrun; + ++ /* Num hw paused */ ++ u32 hw_paused; ++ + /* Num PPDUs cleaned up in TX abort */ + s32 tx_abort; + +@@ -4232,6 +4235,8 @@ struct wmi_pdev_stats_tx { + /* excessive retries */ + u32 tx_ko; + ++ u32 tx_xretry; ++ + /* data hw rate code */ + u32 data_rc; + +@@ -4261,6 +4266,40 @@ struct wmi_pdev_stats_tx { + + /* MPDU is more than txop limit */ + u32 txop_ovf; ++ ++ /* Num sequences posted */ ++ u32 seq_posted; ++ ++ /* Num sequences failed in queueing */ ++ u32 seq_failed_queueing; ++ ++ /* Num sequences completed */ ++ u32 seq_completed; ++ ++ /* Num sequences restarted */ ++ u32 seq_restarted; ++ ++ /* Num of MU sequences posted */ ++ u32 mu_seq_posted; ++ ++ /* Num MPDUs flushed by SW, HWPAUSED, SW TXABORT ++ * (Reset,channel change) ++ */ ++ s32 mpdus_sw_flush; ++ ++ /* Num MPDUs filtered by HW, all filter condition (TTL expired) */ ++ s32 mpdus_hw_filter; ++ ++ /* Num MPDUs truncated by PDG (TXOP, TBTT, ++ * PPDU_duration based on rate, dyn_bw) ++ */ ++ s32 mpdus_truncated; ++ ++ /* Num MPDUs that was tried but didn't receive ACK or BA */ ++ s32 mpdus_ack_failed; ++ ++ /* Num MPDUs that was dropped du to expiry. */ ++ s32 mpdus_expired; + } __packed; + + struct wmi_pdev_stats_rx { +@@ -4295,6 +4334,9 @@ struct wmi_pdev_stats_rx { + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + s32 mpdu_errs; ++ ++ /* Num overflow errors */ ++ s32 rx_ovfl_errs; + } __packed; + + struct wmi_pdev_stats { diff --git a/package/kernel/mac80211/patches/ath11k/0028-ath11k-Add-vdev-start-flag-to-disable-hardware-encry.patch b/package/kernel/mac80211/patches/ath11k/0028-ath11k-Add-vdev-start-flag-to-disable-hardware-encry.patch new file mode 100644 index 00000000000000..740f5de62a6350 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0028-ath11k-Add-vdev-start-flag-to-disable-hardware-encry.patch @@ -0,0 +1,47 @@ +From 8717db7ee802b71fa3f2a79b265b1325bc61210c Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 28 Sep 2021 12:05:40 +0300 +Subject: [PATCH] ath11k: Add vdev start flag to disable hardware encryption + +Firmware blocks all data traffic until the key is plumbed. But, with +software encryption mode, key is never plumbed to firmware. Due to this, +a traffic failure in software encryption mode has been observed. Hence, +firmware has introduced a flag to differentiate software encryption +mode. This flag can be passed during vdev_start command. + +Enable WMI_VDEV_START_HW_ENCRYPTION_DISABLED flag in vdev_start command +to notify firmware to disable hardware encryption for a vdev. This is set +if raw mode software encryption is enabled. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01421-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721212029.142388-5-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/wmi.c | 2 ++ + drivers/net/wireless/ath/ath11k/wmi.h | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -884,6 +884,8 @@ int ath11k_wmi_vdev_start(struct ath11k + } + + cmd->flags |= WMI_VDEV_START_LDPC_RX_ENABLED; ++ if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags)) ++ cmd->flags |= WMI_VDEV_START_HW_ENCRYPTION_DISABLED; + + ptr = skb->data + sizeof(*cmd); + chan = ptr; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -2577,6 +2577,7 @@ struct wmi_vdev_down_cmd { + #define WMI_VDEV_START_HIDDEN_SSID BIT(0) + #define WMI_VDEV_START_PMF_ENABLED BIT(1) + #define WMI_VDEV_START_LDPC_RX_ENABLED BIT(3) ++#define WMI_VDEV_START_HW_ENCRYPTION_DISABLED BIT(4) + + struct wmi_ssid { + u32 ssid_len; diff --git a/package/kernel/mac80211/patches/ath11k/0029-ath11k-Assign-free_vdev_map-value-before-ieee80211_r.patch b/package/kernel/mac80211/patches/ath11k/0029-ath11k-Assign-free_vdev_map-value-before-ieee80211_r.patch new file mode 100644 index 00000000000000..a2e2cbb8fef681 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0029-ath11k-Assign-free_vdev_map-value-before-ieee80211_r.patch @@ -0,0 +1,62 @@ +From 3c79cb4d63c0d58462d439efa0db328008354deb Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 28 Sep 2021 12:05:40 +0300 +Subject: [PATCH] ath11k: Assign free_vdev_map value before + ieee80211_register_hw + +Firmware crash is seen randomly, because of sending wrong vdev_id +in vdev_create command. This is due to free_vdev_map value being 0. +free_vdev_map is getting assigned after ieee80211_register_hw. In +some race conditions, add_interface api is getting called before +assigning value to free_vdev_map. Fix this by assigning free_vdev_map +before ieee80211_register_hw. + +Also, moved ar->cc_freq_hz and ar->txmgmt_idr initialization before +ieee80211_register_hw to avoid such race conditions. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00948-QCAHKSWPL_SILICONZ-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721212029.142388-6-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7711,6 +7711,10 @@ int ath11k_mac_register(struct ath11k_ba + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) + return 0; + ++ /* Initialize channel counters frequency value in hertz */ ++ ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; ++ ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; ++ + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; +@@ -7721,18 +7725,14 @@ int ath11k_mac_register(struct ath11k_ba + ar->mac_addr[4] += i; + } + ++ idr_init(&ar->txmgmt_idr); ++ spin_lock_init(&ar->txmgmt_idr_lock); ++ + ret = __ath11k_mac_register(ar); + if (ret) + goto err_cleanup; +- +- idr_init(&ar->txmgmt_idr); +- spin_lock_init(&ar->txmgmt_idr_lock); + } + +- /* Initialize channel counters frequency value in hertz */ +- ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; +- ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; +- + return 0; + + err_cleanup: diff --git a/package/kernel/mac80211/patches/ath11k/0030-ath11k-Fix-crash-during-firmware-recovery-on-reo-cmd.patch b/package/kernel/mac80211/patches/ath11k/0030-ath11k-Fix-crash-during-firmware-recovery-on-reo-cmd.patch new file mode 100644 index 00000000000000..38f3064fc6e313 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0030-ath11k-Fix-crash-during-firmware-recovery-on-reo-cmd.patch @@ -0,0 +1,63 @@ +From 8ee8d38ca4727667e05a1dedf546162207bde9fa Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Tue, 28 Sep 2021 12:05:40 +0300 +Subject: [PATCH] ath11k: Fix crash during firmware recovery on reo cmd ring + access + +In scenario when a peer is disassociating, there could be +multiple places where a reo cmd ring is accessed, such as +during aggregation teardown, tid queue cleanup, etc. + +When this happens during firmware recovery where accessing of FW/HW +resources/registers is not recommended, accessing reo cmd ring in +this case could lead to crash or undefined behaviour. + +Hence avoid this by checking for corresponding flag to avoid +accessing reo cmd ring during firmware recovery. + +Sample crash: + +[ 3936.456050] Unhandled fault: imprecise external abort (0x1c06) at 0x54bb842a +[ 3936.456411] WARN: Access Violation!!!, Run "cat /sys/kernel/debug/qcom_debug_logs/tz_log" for more details +[ 3936.467997] pgd = b4474000 +[ 3936.477440] [54bb842a] *pgd=6fa61831, *pte=7f95d59f, *ppte=7f95de7e + +[ 3937.177436] [<8030ab10>] (_raw_spin_unlock_bh) from [<7f5e9eb8>] (ath11k_hal_reo_cmd_send+0x440/0x458 [ath11k]) +[ 3937.185730] [<7f5e9eb8>] (ath11k_hal_reo_cmd_send [ath11k]) from [<7f601c4c>] (ath11k_dp_tx_send_reo_cmd+0x2c/0xcc [ath11k]) +[ 3937.195524] [<7f601c4c>] (ath11k_dp_tx_send_reo_cmd [ath11k]) from [<7f602f10>] (ath11k_peer_rx_tid_reo_update+0x84/0xbc [ath11k]) +[ 3937.206984] [<7f602f10>] (ath11k_peer_rx_tid_reo_update [ath11k]) from [<7f605a9c>] (ath11k_dp_rx_ampdu_stop+0xa8/0x130 [ath11k]) +[ 3937.218532] [<7f605a9c>] (ath11k_dp_rx_ampdu_stop [ath11k]) from [<7f5f6730>] (ath11k_mac_op_ampdu_action+0x6c/0x98 [ath11k]) +[ 3937.230250] [<7f5f6730>] (ath11k_mac_op_ampdu_action [ath11k]) from [] (___ieee80211_stop_rx_ba_session+0x98/0x144 [mac80211]) +[ 3937.241499] [] (___ieee80211_stop_rx_ba_session [mac80211]) from [] (ieee80211_sta_tear_down_BA_sessions+0x4c/0xf4 [) +[ 3937.253833] [] (ieee80211_sta_tear_down_BA_sessions [mac80211]) from [] (ieee80211_sta_eosp+0x5b8/0x960 [mac80211]) +[ 3937.266764] [] (ieee80211_sta_eosp [mac80211]) from [] (__sta_info_flush+0x9c/0x134 [mac80211]) +[ 3937.278826] [] (__sta_info_flush [mac80211]) from [] (ieee80211_stop_ap+0x14c/0x28c [mac80211]) +[ 3937.289240] [] (ieee80211_stop_ap [mac80211]) from [<7f509cf0>] (__cfg80211_stop_ap+0x4c/0xd8 [cfg80211]) +[ 3937.299629] [<7f509cf0>] (__cfg80211_stop_ap [cfg80211]) from [<7f4dddec>] (cfg80211_leave+0x24/0x30 [cfg80211]) +[ 3937.310041] [<7f4dddec>] (cfg80211_leave [cfg80211]) from [<7f4de03c>] (cfg80211_netdev_notifier_call+0x174/0x48c [cfg80211]) +[ 3937.320457] [<7f4de03c>] (cfg80211_netdev_notifier_call [cfg80211]) from [<80339928>] (notifier_call_chain+0x40/0x68) +[ 3937.331636] [<80339928>] (notifier_call_chain) from [<803399a8>] (raw_notifier_call_chain+0x14/0x1c) +[ 3937.342221] [<803399a8>] (raw_notifier_call_chain) from [<8073bb00>] (call_netdevice_notifiers+0xc/0x14) + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01240-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721212029.142388-7-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -622,6 +622,9 @@ int ath11k_dp_tx_send_reo_cmd(struct ath + struct hal_srng *cmd_ring; + int cmd_num; + ++ if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) ++ return -ESHUTDOWN; ++ + cmd_ring = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id]; + cmd_num = ath11k_hal_reo_cmd_send(ab, cmd_ring, type, cmd); + diff --git a/package/kernel/mac80211/patches/ath11k/0031-ath11k-Avoid-No-VIF-found-warning-message.patch b/package/kernel/mac80211/patches/ath11k/0031-ath11k-Avoid-No-VIF-found-warning-message.patch new file mode 100644 index 00000000000000..0798c0d438e25a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0031-ath11k-Avoid-No-VIF-found-warning-message.patch @@ -0,0 +1,44 @@ +From 79feedfea7793d91293ab72fac5fc66aae0c6a85 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Tue, 28 Sep 2021 12:05:41 +0300 +Subject: [PATCH] ath11k: Avoid "No VIF found" warning message + +Facing below warning prints when we do wifi down in multiple VAPs scenario. + +warning print: + +ath11k c000000.wifi: No VIF found for vdev 2 +... +ath11k c000000.wifi: No VIF found for vdev 0 + +In ath11k_mac_get_arvif_by_vdev_id(), we iterate all the radio to get the +arvif for the requested vdev_id through ath11k_mac_get_arvif(). +ath11k_mac_get_arvif() throws a warning message if the given vdev_id is +not found in the given radio. So to avoid the warning message, add +the allocated_vdev_map cross check against the given vdev_id before using +ath11k_mac_get_arvif() to ensure that vdev_id is allocated in the +given radio. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01492-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ6018 hw1.0 AHB WLAN.HK.2.4.0.1-00330-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721212029.142388-8-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -501,7 +501,8 @@ struct ath11k_vif *ath11k_mac_get_arvif_ + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); +- if (pdev && pdev->ar) { ++ if (pdev && pdev->ar && ++ (pdev->ar->allocated_vdev_map & (1LL << vdev_id))) { + arvif = ath11k_mac_get_arvif(pdev->ar, vdev_id); + if (arvif) + return arvif; diff --git a/package/kernel/mac80211/patches/ath11k/0032-ath11k-Add-wmi-peer-create-conf-event-in-wmi_tlv_eve.patch b/package/kernel/mac80211/patches/ath11k/0032-ath11k-Add-wmi-peer-create-conf-event-in-wmi_tlv_eve.patch new file mode 100644 index 00000000000000..89b68fd2225b85 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0032-ath11k-Add-wmi-peer-create-conf-event-in-wmi_tlv_eve.patch @@ -0,0 +1,50 @@ +From 94a6df31dcf042f74db8209680d04546ce964ad5 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Tue, 28 Sep 2021 12:05:41 +0300 +Subject: [PATCH] ath11k: Add wmi peer create conf event in wmi_tlv_event_id + +When the driver sends a peer create cmd, the firmware responds with +WMI_PEER_CREATE_CONF_EVENTID to confirm the firmware received +WMI_PEER_CREATE_CMDID. Since the peer create conf event is not handled +in ath11k_wmi_tlv_op_rx, we are getting unknown event id warning prints +during peer creation. + +Add WMI_PEER_CREATE_CONF_EVENTID in wmi_tlv_event_id and handle +the same as unsupported event id under wmi logs. + +warning prints: +[ 4382.230817] ath11k_pci 0000:01:00.0: Unknown eventid: 0x601a + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: P Praneesh +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210721212029.142388-9-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/wmi.c | 1 + + drivers/net/wireless/ath/ath11k/wmi.h | 3 +++ + 2 files changed, 4 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7137,6 +7137,7 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_TWT_ENABLE_EVENTID: + case WMI_TWT_DISABLE_EVENTID: + case WMI_PDEV_DMA_RING_CFG_RSP_EVENTID: ++ case WMI_PEER_CREATE_CONF_EVENTID: + ath11k_dbg(ab, ATH11K_DBG_WMI, + "ignoring unsupported event 0x%x\n", id); + break; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -663,6 +663,9 @@ enum wmi_tlv_event_id { + WMI_PEER_RESERVED9_EVENTID, + WMI_PEER_RESERVED10_EVENTID, + WMI_PEER_OPER_MODE_CHANGE_EVENTID, ++ WMI_PEER_TX_PN_RESPONSE_EVENTID, ++ WMI_PEER_CFR_CAPTURE_EVENTID, ++ WMI_PEER_CREATE_CONF_EVENTID, + WMI_MGMT_RX_EVENTID = WMI_TLV_CMD(WMI_GRP_MGMT), + WMI_HOST_SWBA_EVENTID, + WMI_TBTTOFFSET_UPDATE_EVENTID, diff --git a/package/kernel/mac80211/patches/ath11k/0033-ath11k-add-channel-2-into-6-GHz-channel-list.patch b/package/kernel/mac80211/patches/ath11k/0033-ath11k-add-channel-2-into-6-GHz-channel-list.patch new file mode 100644 index 00000000000000..4e3d5d4fe4b4c5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0033-ath11k-add-channel-2-into-6-GHz-channel-list.patch @@ -0,0 +1,43 @@ +From 4a9550f536cc9c62210f77d875f000e560fc64b1 Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Tue, 28 Sep 2021 14:00:43 +0300 +Subject: [PATCH] ath11k: add channel 2 into 6 GHz channel list + +Add support for the 6 GHz channel 2 with center frequency 5935 MHz and +operating class 136 per IEEE Std 802.11ax-2021, Table E-4. + +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210722102054.43419-1-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 4 ++-- + drivers/net/wireless/ath/ath11k/mac.c | 3 +++ + 2 files changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -393,9 +393,9 @@ struct ath11k_sta { + }; + + #define ATH11K_MIN_5G_FREQ 4150 +-#define ATH11K_MIN_6G_FREQ 5945 ++#define ATH11K_MIN_6G_FREQ 5925 + #define ATH11K_MAX_6G_FREQ 7115 +-#define ATH11K_NUM_CHANS 100 ++#define ATH11K_NUM_CHANS 101 + #define ATH11K_MAX_5G_CHAN 173 + + enum ath11k_state { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -151,6 +151,9 @@ static const struct ieee80211_channel at + CHAN6G(225, 7075, 0), + CHAN6G(229, 7095, 0), + CHAN6G(233, 7115, 0), ++ ++ /* new addition in IEEE Std 802.11ax-2021 */ ++ CHAN6G(2, 5935, 0), + }; + + static struct ieee80211_rate ath11k_legacy_rates[] = { diff --git a/package/kernel/mac80211/patches/ath11k/0035-ath11k-fix-survey-dump-collection-in-6-GHz.patch b/package/kernel/mac80211/patches/ath11k/0035-ath11k-fix-survey-dump-collection-in-6-GHz.patch new file mode 100644 index 00000000000000..8d612b3af21e64 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0035-ath11k-fix-survey-dump-collection-in-6-GHz.patch @@ -0,0 +1,35 @@ +From b6b142f644d2d88e2ceabe0aa4479e0a09ba1ea9 Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Tue, 28 Sep 2021 14:00:43 +0300 +Subject: [PATCH] ath11k: fix survey dump collection in 6 GHz + +When ath11k receives survey request, choose the 6 GHz band when enabled. +Without this, survey request does not include any 6 GHz band results, +thereby causing auto channel selection to fail. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01386-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210722102054.43419-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7182,7 +7182,13 @@ static int ath11k_mac_op_get_survey(stru + + if (!sband) + sband = hw->wiphy->bands[NL80211_BAND_5GHZ]; ++ if (sband && idx >= sband->n_channels) { ++ idx -= sband->n_channels; ++ sband = NULL; ++ } + ++ if (!sband) ++ sband = hw->wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband || idx >= sband->n_channels) { + ret = -ENOENT; + goto exit; diff --git a/package/kernel/mac80211/patches/ath11k/0036-ath11k-re-enable-ht_cap-vht_cap-for-5G-band-for-WCN6.patch b/package/kernel/mac80211/patches/ath11k/0036-ath11k-re-enable-ht_cap-vht_cap-for-5G-band-for-WCN6.patch new file mode 100644 index 00000000000000..a0c1288a878323 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0036-ath11k-re-enable-ht_cap-vht_cap-for-5G-band-for-WCN6.patch @@ -0,0 +1,34 @@ +From 54f40f552afd5a07e635a52221ec4b0ce765c374 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 28 Sep 2021 14:00:43 +0300 +Subject: [PATCH] ath11k: re-enable ht_cap/vht_cap for 5G band for WCN6855 + +WCN6855 uses single_pdev_only, so it supports both the 5G and 6G bands +in the same ath11k/pdev and it needs to enable ht_cap/vht_cap for the 5G +band, otherwise it will downgrade to non-HT mode for the 5G band. Some +chips like QCN9074 only support the 6G band, not the 5G band, and use +the flag ar->supports_6ghz which is true to discard ht_cap/vht_cap. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210804181217.88751-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4551,7 +4551,9 @@ static void ath11k_mac_setup_ht_vht_cap( + rate_cap_rx_chainmask); + } + +- if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && !ar->supports_6ghz) { ++ if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && ++ (ar->ab->hw_params.single_pdev_only || ++ !ar->supports_6ghz)) { + band = &ar->mac.sbands[NL80211_BAND_5GHZ]; + ht_cap = cap->band[NL80211_BAND_5GHZ].ht_cap_info; + if (ht_cap_info) diff --git a/package/kernel/mac80211/patches/ath11k/0037-ath11k-enable-6G-channels-for-WCN6855.patch b/package/kernel/mac80211/patches/ath11k/0037-ath11k-enable-6G-channels-for-WCN6855.patch new file mode 100644 index 00000000000000..550f9d3c31af19 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0037-ath11k-enable-6G-channels-for-WCN6855.patch @@ -0,0 +1,98 @@ +From 74bba5e5ba45d511a944082d76b64cc1849e4c2e Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 28 Sep 2021 14:00:43 +0300 +Subject: [PATCH] ath11k: enable 6G channels for WCN6855 + +For some chips such as WCN6855, single_pdev_only is set in struct +ath11k_hw_params which means ath11k calls ieee80211_register_hw() only +once and create only one device interface, and that device interface +supports all 2G/5G/6G channels. + +ath11k_mac_setup_channels_rates() sets up the channels and it is called +for each device interface. It is called only once for single_pdev_only, +and then set up all channels for 2G/5G/6G. The logic of +ath11k_mac_setup_channels_rates() is not suitable for single_pdev_only, +it leads to all 6G channels being disabled for the device interface +which is single_pdev_only such as WCN6855. + +Add channel frequency checks for the 6G band and enable the 6G channels +properly based on what is supported by the chip. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210804181217.88751-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 25 ++++++++++++++++--------- + 1 file changed, 16 insertions(+), 9 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7322,7 +7322,7 @@ static int ath11k_mac_setup_channels_rat + u32 supported_bands) + { + struct ieee80211_supported_band *band; +- struct ath11k_hal_reg_capabilities_ext *reg_cap; ++ struct ath11k_hal_reg_capabilities_ext *reg_cap, *temp_reg_cap; + void *channels; + u32 phy_id; + +@@ -7332,6 +7332,7 @@ static int ath11k_mac_setup_channels_rat + ATH11K_NUM_CHANS); + + reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx]; ++ temp_reg_cap = reg_cap; + + if (supported_bands & WMI_HOST_WLAN_2G_CAP) { + channels = kmemdup(ath11k_2ghz_channels, +@@ -7350,11 +7351,11 @@ static int ath11k_mac_setup_channels_rat + + if (ar->ab->hw_params.single_pdev_only) { + phy_id = ath11k_get_phy_id(ar, WMI_HOST_WLAN_2G_CAP); +- reg_cap = &ar->ab->hal_reg_cap[phy_id]; ++ temp_reg_cap = &ar->ab->hal_reg_cap[phy_id]; + } + ath11k_mac_update_ch_list(ar, band, +- reg_cap->low_2ghz_chan, +- reg_cap->high_2ghz_chan); ++ temp_reg_cap->low_2ghz_chan, ++ temp_reg_cap->high_2ghz_chan); + } + + if (supported_bands & WMI_HOST_WLAN_5G_CAP) { +@@ -7374,9 +7375,15 @@ static int ath11k_mac_setup_channels_rat + band->n_bitrates = ath11k_a_rates_size; + band->bitrates = ath11k_a_rates; + ar->hw->wiphy->bands[NL80211_BAND_6GHZ] = band; ++ ++ if (ar->ab->hw_params.single_pdev_only) { ++ phy_id = ath11k_get_phy_id(ar, WMI_HOST_WLAN_5G_CAP); ++ temp_reg_cap = &ar->ab->hal_reg_cap[phy_id]; ++ } ++ + ath11k_mac_update_ch_list(ar, band, +- reg_cap->low_5ghz_chan, +- reg_cap->high_5ghz_chan); ++ temp_reg_cap->low_5ghz_chan, ++ temp_reg_cap->high_5ghz_chan); + } + + if (reg_cap->low_5ghz_chan < ATH11K_MIN_6G_FREQ) { +@@ -7399,12 +7406,12 @@ static int ath11k_mac_setup_channels_rat + + if (ar->ab->hw_params.single_pdev_only) { + phy_id = ath11k_get_phy_id(ar, WMI_HOST_WLAN_5G_CAP); +- reg_cap = &ar->ab->hal_reg_cap[phy_id]; ++ temp_reg_cap = &ar->ab->hal_reg_cap[phy_id]; + } + + ath11k_mac_update_ch_list(ar, band, +- reg_cap->low_5ghz_chan, +- reg_cap->high_5ghz_chan); ++ temp_reg_cap->low_5ghz_chan, ++ temp_reg_cap->high_5ghz_chan); + } + } + diff --git a/package/kernel/mac80211/patches/ath11k/0038-ath11k-copy-cap-info-of-6G-band-under-WMI_HOST_WLAN_.patch b/package/kernel/mac80211/patches/ath11k/0038-ath11k-copy-cap-info-of-6G-band-under-WMI_HOST_WLAN_.patch new file mode 100644 index 00000000000000..71bc680623704d --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0038-ath11k-copy-cap-info-of-6G-band-under-WMI_HOST_WLAN_.patch @@ -0,0 +1,54 @@ +From 0f17ae43823b237c73ff138bc067229f7c57e3a2 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 28 Sep 2021 14:00:43 +0300 +Subject: [PATCH] ath11k: copy cap info of 6G band under WMI_HOST_WLAN_5G_CAP + for WCN6855 + +WCN6855 has 2 phys, one is 2G, another is 5G/6G, so it should copy the +cap info of 6G band under the check of WMI_HOST_WLAN_5G_CAP as well as +for the 5G band. Some chips like QCN9074 only have 6G, not have 2G and +5G, and this 6G capability is also under WMI_HOST_WLAN_5G_CAP, so this +change will not disturb it. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210804181217.88751-4-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/wmi.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -407,18 +407,18 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(st + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, + sizeof(struct ath11k_ppe_threshold)); +- } + +- cap_band = &pdev_cap->band[NL80211_BAND_6GHZ]; +- cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; +- cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; +- cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; +- cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; +- cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; +- memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, +- sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); +- memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, +- sizeof(struct ath11k_ppe_threshold)); ++ cap_band = &pdev_cap->band[NL80211_BAND_6GHZ]; ++ cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; ++ cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; ++ cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; ++ cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; ++ cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; ++ memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, ++ sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); ++ memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, ++ sizeof(struct ath11k_ppe_threshold)); ++ } + + return 0; + } diff --git a/package/kernel/mac80211/patches/ath11k/0039-ath11k-Drop-MSDU-with-length-error-in-DP-rx-path.patch b/package/kernel/mac80211/patches/ath11k/0039-ath11k-Drop-MSDU-with-length-error-in-DP-rx-path.patch new file mode 100644 index 00000000000000..ac19edd81c28e0 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0039-ath11k-Drop-MSDU-with-length-error-in-DP-rx-path.patch @@ -0,0 +1,57 @@ +From cd18ed4cf8051ceb8590263f5914cb9bb58b0f25 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Tue, 28 Sep 2021 14:00:43 +0300 +Subject: [PATCH] ath11k: Drop MSDU with length error in DP rx path + +There are MSDUs whose length are invalid. For example, +attackers may inject on purpose truncated A-MSDUs with +invalid MSDU length. + +Such MSDUs are marked with an err bit set in rx attention +tlvs, so we can check and drop them. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913180246.193388-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -142,6 +142,18 @@ static u32 ath11k_dp_rx_h_attn_mpdu_err( + return errmap; + } + ++static bool ath11k_dp_rx_h_attn_msdu_len_err(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) ++{ ++ struct rx_attention *rx_attention; ++ u32 errmap; ++ ++ rx_attention = ath11k_dp_rx_get_attention(ab, desc); ++ errmap = ath11k_dp_rx_h_attn_mpdu_err(rx_attention); ++ ++ return errmap & DP_RX_MPDU_ERR_MSDU_LEN; ++} ++ + static u16 ath11k_dp_rx_h_msdu_start_msdu_len(struct ath11k_base *ab, + struct hal_rx_desc *desc) + { +@@ -2525,6 +2537,12 @@ static int ath11k_dp_rx_process_msdu(str + } + + rx_desc = (struct hal_rx_desc *)msdu->data; ++ if (ath11k_dp_rx_h_attn_msdu_len_err(ab, rx_desc)) { ++ ath11k_warn(ar->ab, "msdu len not valid\n"); ++ ret = -EIO; ++ goto free_out; ++ } ++ + lrx_desc = (struct hal_rx_desc *)last_buf->data; + rx_attention = ath11k_dp_rx_get_attention(ab, lrx_desc); + if (!ath11k_dp_rx_h_attn_msdu_done(rx_attention)) { diff --git a/package/kernel/mac80211/patches/ath11k/0040-ath11k-Fix-inaccessible-debug-registers.patch b/package/kernel/mac80211/patches/ath11k/0040-ath11k-Fix-inaccessible-debug-registers.patch new file mode 100644 index 00000000000000..3563d0028e516a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0040-ath11k-Fix-inaccessible-debug-registers.patch @@ -0,0 +1,46 @@ +From 8a0b899f169d6b6102918327d026922140194fff Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Tue, 28 Sep 2021 14:00:44 +0300 +Subject: [PATCH] ath11k: Fix inaccessible debug registers + +Current code clears debug registers after SOC global reset performed +in ath11k_pci_sw_reset. However at that time those registers are +not accessible due to reset, thus they are actually not cleared at all. +For WCN6855, it may cause target fail to initialize. This issue can be +fixed by moving clear action ahead. + +In addition, on some specific platforms, need to add delay to wait +those registers to become accessible. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913180246.193388-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -430,6 +430,8 @@ static void ath11k_pci_force_wake(struct + + static void ath11k_pci_sw_reset(struct ath11k_base *ab, bool power_on) + { ++ mdelay(100); ++ + if (power_on) { + ath11k_pci_enable_ltssm(ab); + ath11k_pci_clear_all_intrs(ab); +@@ -439,9 +441,9 @@ static void ath11k_pci_sw_reset(struct a + } + + ath11k_mhi_clear_vector(ab); ++ ath11k_pci_clear_dbg_registers(ab); + ath11k_pci_soc_global_reset(ab); + ath11k_mhi_set_mhictrl_reset(ab); +- ath11k_pci_clear_dbg_registers(ab); + } + + int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector) diff --git a/package/kernel/mac80211/patches/ath11k/0042-ath11k-Rename-macro-ARRAY_TO_STRING-to-PRINT_ARRAY_T.patch b/package/kernel/mac80211/patches/ath11k/0042-ath11k-Rename-macro-ARRAY_TO_STRING-to-PRINT_ARRAY_T.patch new file mode 100644 index 00000000000000..3a6dd985f9943d --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0042-ath11k-Rename-macro-ARRAY_TO_STRING-to-PRINT_ARRAY_T.patch @@ -0,0 +1,852 @@ +From 9e2e2d7a4dd490ff6e95e37611070d3b3a9cf58b Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 28 Sep 2021 14:00:44 +0300 +Subject: [PATCH] ath11k: Rename macro ARRAY_TO_STRING to PRINT_ARRAY_TO_BUF + +Renaming of macro is done to describe the macro functionality +better as the macro functionality is modified in next patch-sets. +No functional changes are done. + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913223148.208026-2-jouni@codeaurora.org +--- + .../wireless/ath/ath11k/debugfs_htt_stats.c | 378 +++++++++--------- + 1 file changed, 189 insertions(+), 189 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +@@ -18,7 +18,7 @@ + + #define HTT_TLV_HDR_LEN 4 + +-#define ARRAY_TO_STRING(out, arr, len) \ ++#define PRINT_ARRAY_TO_BUF(out, arr, len) \ + do { \ + int index = 0; u8 i; \ + for (i = 0; i < len; i++) { \ +@@ -195,7 +195,7 @@ htt_print_tx_pdev_stats_urrn_tlv_v(const + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_URRN_TLV_V:"); + +- ARRAY_TO_STRING(urrn_stats, htt_stats_buf->urrn_stats, num_elems); ++ PRINT_ARRAY_TO_BUF(urrn_stats, htt_stats_buf->urrn_stats, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "urrn_stats = %s\n", urrn_stats); + + if (len >= buf_len) +@@ -220,7 +220,7 @@ htt_print_tx_pdev_stats_flush_tlv_v(cons + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_FLUSH_TLV_V:"); + +- ARRAY_TO_STRING(flush_errs, htt_stats_buf->flush_errs, num_elems); ++ PRINT_ARRAY_TO_BUF(flush_errs, htt_stats_buf->flush_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_errs = %s\n", flush_errs); + + if (len >= buf_len) +@@ -245,7 +245,7 @@ htt_print_tx_pdev_stats_sifs_tlv_v(const + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_SIFS_TLV_V:"); + +- ARRAY_TO_STRING(sifs_status, htt_stats_buf->sifs_status, num_elems); ++ PRINT_ARRAY_TO_BUF(sifs_status, htt_stats_buf->sifs_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_status = %s\n", + sifs_status); + +@@ -271,7 +271,7 @@ htt_print_tx_pdev_stats_phy_err_tlv_v(co + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_PHY_ERR_TLV_V:"); + +- ARRAY_TO_STRING(phy_errs, htt_stats_buf->phy_errs, num_elems); ++ PRINT_ARRAY_TO_BUF(phy_errs, htt_stats_buf->phy_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) +@@ -297,7 +297,7 @@ htt_print_tx_pdev_stats_sifs_hist_tlv_v( + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_HIST_TLV_V:"); + +- ARRAY_TO_STRING(sifs_hist_status, htt_stats_buf->sifs_hist_status, num_elems); ++ PRINT_ARRAY_TO_BUF(sifs_hist_status, htt_stats_buf->sifs_hist_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_hist_status = %s\n", + sifs_hist_status); + +@@ -363,9 +363,9 @@ htt_print_tx_pdev_stats_tried_mpdu_cnt_h + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { +- ARRAY_TO_STRING(tried_mpdu_cnt_hist, +- htt_stats_buf->tried_mpdu_cnt_hist, +- num_elements); ++ PRINT_ARRAY_TO_BUF(tried_mpdu_cnt_hist, ++ htt_stats_buf->tried_mpdu_cnt_hist, ++ num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); + } else { +@@ -667,9 +667,9 @@ static inline void htt_print_counter_tlv + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_COUNTER_TLV:"); + +- ARRAY_TO_STRING(counter_name, +- htt_stats_buf->counter_name, +- HTT_MAX_COUNTER_NAME); ++ PRINT_ARRAY_TO_BUF(counter_name, ++ htt_stats_buf->counter_name, ++ HTT_MAX_COUNTER_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "counter_name = %s ", counter_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", + htt_stats_buf->count); +@@ -794,54 +794,54 @@ static inline void htt_print_tx_peer_rat + htt_stats_buf->ack_rssi); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mcs, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_su_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_su_mcs, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_su_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mu_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mu_mcs, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mu_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, +- htt_stats_buf->tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, ++ htt_stats_buf->tx_nss, ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, +- htt_stats_buf->tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, ++ htt_stats_buf->tx_bw, ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_stbc, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, +- HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_pream, ++ HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(tx_gi[j], +- htt_stats_buf->tx_gi[j], +- HTT_TX_PEER_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(tx_gi[j], ++ htt_stats_buf->tx_gi[j], ++ HTT_TX_PEER_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", +- j, tx_gi[j]); ++ j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, +- htt_stats_buf->tx_dcm, +- HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, ++ htt_stats_buf->tx_dcm, ++ HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + if (len >= buf_len) +@@ -895,47 +895,47 @@ static inline void htt_print_rx_peer_rat + htt_stats_buf->rssi_comb); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_mcs, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, +- HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_nss, ++ HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, +- HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_dcm, ++ HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_stbc, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_bw, ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { +- ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], +- HTT_RX_PEER_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(rssi_chain[j], htt_stats_buf->rssi_chain[j], ++ HTT_RX_PEER_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->rx_gi[j], ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", +- j, rx_gi[j]); ++ j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, +- HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_pream, ++ HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s\n", str_buf); + + if (len >= buf_len) +@@ -1115,10 +1115,10 @@ htt_print_tx_hwq_difs_latency_stats_tlv_ + len += HTT_DBG_OUT(buf + len, buf_len - len, "hist_intvl = %u", + htt_stats_buf->hist_intvl); + +- ARRAY_TO_STRING(difs_latency_hist, htt_stats_buf->difs_latency_hist, +- data_len); ++ PRINT_ARRAY_TO_BUF(difs_latency_hist, htt_stats_buf->difs_latency_hist, ++ data_len); + len += HTT_DBG_OUT(buf + len, buf_len - len, "difs_latency_hist = %s\n", +- difs_latency_hist); ++ difs_latency_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1145,7 +1145,7 @@ htt_print_tx_hwq_cmd_result_stats_tlv_v( + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_CMD_RESULT_STATS_TLV_V:"); + +- ARRAY_TO_STRING(cmd_result, htt_stats_buf->cmd_result, data_len); ++ PRINT_ARRAY_TO_BUF(cmd_result, htt_stats_buf->cmd_result, data_len); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_result = %s\n", cmd_result); + +@@ -1173,7 +1173,7 @@ htt_print_tx_hwq_cmd_stall_stats_tlv_v(c + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_CMD_STALL_STATS_TLV_V:"); + +- ARRAY_TO_STRING(cmd_stall_status, htt_stats_buf->cmd_stall_status, num_elems); ++ PRINT_ARRAY_TO_BUF(cmd_stall_status, htt_stats_buf->cmd_stall_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_stall_status = %s\n", + cmd_stall_status); + +@@ -1202,7 +1202,7 @@ htt_print_tx_hwq_fes_result_stats_tlv_v( + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_FES_RESULT_STATS_TLV_V:"); + +- ARRAY_TO_STRING(fes_result, htt_stats_buf->fes_result, num_elems); ++ PRINT_ARRAY_TO_BUF(fes_result, htt_stats_buf->fes_result, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fes_result = %s\n", fes_result); + + if (len >= buf_len) +@@ -1233,9 +1233,9 @@ htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { +- ARRAY_TO_STRING(tried_mpdu_cnt_hist, +- htt_stats_buf->tried_mpdu_cnt_hist, +- num_elements); ++ PRINT_ARRAY_TO_BUF(tried_mpdu_cnt_hist, ++ htt_stats_buf->tried_mpdu_cnt_hist, ++ num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); +@@ -1269,9 +1269,9 @@ htt_print_tx_hwq_txop_used_cnt_hist_tlv_ + "HTT_TX_HWQ_TXOP_USED_CNT_HIST_TLV_V:"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { +- ARRAY_TO_STRING(txop_used_cnt_hist, +- htt_stats_buf->txop_used_cnt_hist, +- num_elements); ++ PRINT_ARRAY_TO_BUF(txop_used_cnt_hist, ++ htt_stats_buf->txop_used_cnt_hist, ++ num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_used_cnt_hist = %s\n", + txop_used_cnt_hist); + } else { +@@ -1790,8 +1790,8 @@ htt_print_sched_txq_cmd_posted_tlv_v(con + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV_V:"); + +- ARRAY_TO_STRING(sched_cmd_posted, htt_stats_buf->sched_cmd_posted, +- num_elements); ++ PRINT_ARRAY_TO_BUF(sched_cmd_posted, htt_stats_buf->sched_cmd_posted, ++ num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_posted = %s\n", + sched_cmd_posted); + +@@ -1817,8 +1817,8 @@ htt_print_sched_txq_cmd_reaped_tlv_v(con + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV_V:"); + +- ARRAY_TO_STRING(sched_cmd_reaped, htt_stats_buf->sched_cmd_reaped, +- num_elements); ++ PRINT_ARRAY_TO_BUF(sched_cmd_reaped, htt_stats_buf->sched_cmd_reaped, ++ num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_reaped = %s\n", + sched_cmd_reaped); + +@@ -1847,8 +1847,8 @@ htt_print_sched_txq_sched_order_su_tlv_v + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV_V:"); + +- ARRAY_TO_STRING(sched_order_su, htt_stats_buf->sched_order_su, +- sched_order_su_num_entries); ++ PRINT_ARRAY_TO_BUF(sched_order_su, htt_stats_buf->sched_order_su, ++ sched_order_su_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_order_su = %s\n", + sched_order_su); + +@@ -1876,8 +1876,8 @@ htt_print_sched_txq_sched_ineligibility_ + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_INELIGIBILITY_V:"); + +- ARRAY_TO_STRING(sched_ineligibility, htt_stats_buf->sched_ineligibility, +- sched_ineligibility_num_entries); ++ PRINT_ARRAY_TO_BUF(sched_ineligibility, htt_stats_buf->sched_ineligibility, ++ sched_ineligibility_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_ineligibility = %s\n", + sched_ineligibility); + +@@ -1992,8 +1992,8 @@ htt_print_tx_tqm_gen_mpdu_stats_tlv_v(co + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV_V:"); + +- ARRAY_TO_STRING(gen_mpdu_end_reason, htt_stats_buf->gen_mpdu_end_reason, +- num_elements); ++ PRINT_ARRAY_TO_BUF(gen_mpdu_end_reason, htt_stats_buf->gen_mpdu_end_reason, ++ num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_end_reason = %s\n", + gen_mpdu_end_reason); + +@@ -2020,8 +2020,8 @@ htt_print_tx_tqm_list_mpdu_stats_tlv_v(c + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_TQM_LIST_MPDU_STATS_TLV_V:"); + +- ARRAY_TO_STRING(list_mpdu_end_reason, htt_stats_buf->list_mpdu_end_reason, +- num_elems); ++ PRINT_ARRAY_TO_BUF(list_mpdu_end_reason, htt_stats_buf->list_mpdu_end_reason, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_end_reason = %s\n", + list_mpdu_end_reason); + if (len >= buf_len) +@@ -2047,8 +2047,8 @@ htt_print_tx_tqm_list_mpdu_cnt_tlv_v(con + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:"); + +- ARRAY_TO_STRING(list_mpdu_cnt_hist, htt_stats_buf->list_mpdu_cnt_hist, +- num_elems); ++ PRINT_ARRAY_TO_BUF(list_mpdu_cnt_hist, htt_stats_buf->list_mpdu_cnt_hist, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist = %s\n", + list_mpdu_cnt_hist); + +@@ -2539,9 +2539,9 @@ htt_print_tx_de_fw2wbm_ring_full_hist_tl + "HTT_TX_DE_FW2WBM_RING_FULL_HIST_TLV"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { +- ARRAY_TO_STRING(fw2wbm_ring_full_hist, +- htt_stats_buf->fw2wbm_ring_full_hist, +- num_elements); ++ PRINT_ARRAY_TO_BUF(fw2wbm_ring_full_hist, ++ htt_stats_buf->fw2wbm_ring_full_hist, ++ num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "fw2wbm_ring_full_hist = %s\n", + fw2wbm_ring_full_hist); +@@ -2634,13 +2634,13 @@ static inline void htt_print_ring_if_sta + len += HTT_DBG_OUT(buf + len, buf_len - len, "cons_blockwait_count = %u", + htt_stats_buf->cons_blockwait_count); + +- ARRAY_TO_STRING(low_wm_hit_count, htt_stats_buf->low_wm_hit_count, +- HTT_STATS_LOW_WM_BINS); ++ PRINT_ARRAY_TO_BUF(low_wm_hit_count, htt_stats_buf->low_wm_hit_count, ++ HTT_STATS_LOW_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "low_wm_hit_count = %s ", + low_wm_hit_count); + +- ARRAY_TO_STRING(high_wm_hit_count, htt_stats_buf->high_wm_hit_count, +- HTT_STATS_HIGH_WM_BINS); ++ PRINT_ARRAY_TO_BUF(high_wm_hit_count, htt_stats_buf->high_wm_hit_count, ++ HTT_STATS_HIGH_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "high_wm_hit_count = %s\n", + high_wm_hit_count); + +@@ -2687,9 +2687,9 @@ static inline void htt_print_sfm_client_ + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV_V:"); + +- ARRAY_TO_STRING(dwords_used_by_user_n, +- htt_stats_buf->dwords_used_by_user_n, +- num_elems); ++ PRINT_ARRAY_TO_BUF(dwords_used_by_user_n, ++ htt_stats_buf->dwords_used_by_user_n, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dwords_used_by_user_n = %s\n", + dwords_used_by_user_n); + +@@ -2889,73 +2889,73 @@ static inline void htt_print_tx_pdev_rat + htt_stats_buf->tx_legacy_ofdm_rate[7]); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mcs, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_mcs, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_mcs, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_mcs, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_nss, ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_nss, ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_nss, ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_nss, ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_bw, ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_bw, ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_bw, ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_bw, ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_stbc, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, +- HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_pream, ++ HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u", +@@ -2965,16 +2965,16 @@ static inline void htt_print_tx_pdev_rat + + /* SU GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->tx_gi[j], ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* AC MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ac_mu_mimo_tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ac_mu_mimo_tx_gi[j], ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); +@@ -2982,8 +2982,8 @@ static inline void htt_print_tx_pdev_rat + + /* AX MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ax_mu_mimo_tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ax_mu_mimo_tx_gi[j], ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); +@@ -2991,15 +2991,15 @@ static inline void htt_print_tx_pdev_rat + + /* DL OFDMA GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ofdma_tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ofdma_tx_gi[j], ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_dcm, +- HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_dcm, ++ HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + if (len >= buf_len) +@@ -3064,28 +3064,28 @@ static inline void htt_print_rx_pdev_rat + htt_stats_buf->rssi_in_dbm); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_mcs, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, +- HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_nss, ++ HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, +- HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_dcm, ++ HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_stbc, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_bw, ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_evm_nss_count = %u", + htt_stats_buf->nss_count); +@@ -3115,22 +3115,22 @@ static inline void htt_print_rx_pdev_rat + len += HTT_DBG_OUT(buf + len, buf_len - len, "pilot_evm_dB_mean = %s ", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(rssi_chain[j], htt_stats_buf->rssi_chain[j], ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->rx_gi[j], ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, +- HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_pream, ++ HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_su_ext = %u", +@@ -3145,14 +3145,14 @@ static inline void htt_print_rx_pdev_rat + htt_stats_buf->txbf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_cck_rate, +- HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_legacy_cck_rate, ++ HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_cck_rate = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_ofdm_rate, +- HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_legacy_ofdm_rate, ++ HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_ofdm_rate = %s ", + str_buf); + +@@ -3164,25 +3164,25 @@ static inline void htt_print_rx_pdev_rat + htt_stats_buf->rx_11ax_ul_ofdma); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ul_ofdma_rx_mcs, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_mcs, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_mcs = %s ", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->ul_ofdma_rx_gi[j], +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); ++ PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->ul_ofdma_rx_gi[j], ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ul_ofdma_rx_nss, +- HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_nss, ++ HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->ul_ofdma_rx_bw, +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_bw, ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_bw = %s ", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u", +@@ -3191,25 +3191,25 @@ static inline void htt_print_rx_pdev_rat + htt_stats_buf->ul_ofdma_rx_ldpc); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_non_data_ppdu, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_non_data_ppdu, ++ HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_data_ppdu, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_data_ppdu, ++ HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_data_ppdu = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_mpdu_ok, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_mpdu_ok, ++ HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_ulofdma_mpdu_fail, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); ++ PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_mpdu_fail, ++ HTT_RX_PDEV_MAX_OFDMA_NUM_USER); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail = %s", + str_buf); + +@@ -3320,9 +3320,9 @@ htt_print_rx_soc_fw_refill_ring_empty_tl + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_EMPTY_TLV_V:"); + +- ARRAY_TO_STRING(refill_ring_empty_cnt, +- htt_stats_buf->refill_ring_empty_cnt, +- num_elems); ++ PRINT_ARRAY_TO_BUF(refill_ring_empty_cnt, ++ htt_stats_buf->refill_ring_empty_cnt, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_empty_cnt = %s\n", + refill_ring_empty_cnt); + +@@ -3350,9 +3350,9 @@ htt_print_rx_soc_fw_refill_ring_num_rxdm + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_RXDMA_ERR_TLV_V:"); + +- ARRAY_TO_STRING(rxdma_err_cnt, +- htt_stats_buf->rxdma_err, +- num_elems); ++ PRINT_ARRAY_TO_BUF(rxdma_err_cnt, ++ htt_stats_buf->rxdma_err, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdma_err = %s\n", + rxdma_err_cnt); + +@@ -3379,9 +3379,9 @@ htt_print_rx_soc_fw_refill_ring_num_reo_ + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REO_ERR_TLV_V:"); + +- ARRAY_TO_STRING(reo_err_cnt, +- htt_stats_buf->reo_err, +- num_elems); ++ PRINT_ARRAY_TO_BUF(reo_err_cnt, ++ htt_stats_buf->reo_err, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "reo_err = %s\n", + reo_err_cnt); + +@@ -3447,9 +3447,9 @@ htt_print_rx_soc_fw_refill_ring_num_refi + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REFILL_TLV_V:"); + +- ARRAY_TO_STRING(refill_ring_num_refill, +- htt_stats_buf->refill_ring_num_refill, +- num_elems); ++ PRINT_ARRAY_TO_BUF(refill_ring_num_refill, ++ htt_stats_buf->refill_ring_num_refill, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_num_refill = %s\n", + refill_ring_num_refill); + +@@ -3491,15 +3491,15 @@ static inline void htt_print_rx_pdev_fw_ + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_ind = %u", + htt_stats_buf->fw_ring_mpdu_ind); + +- ARRAY_TO_STRING(fw_ring_mgmt_subtype, +- htt_stats_buf->fw_ring_mgmt_subtype, +- HTT_STATS_SUBTYPE_MAX); ++ PRINT_ARRAY_TO_BUF(fw_ring_mgmt_subtype, ++ htt_stats_buf->fw_ring_mgmt_subtype, ++ HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mgmt_subtype = %s ", + fw_ring_mgmt_subtype); + +- ARRAY_TO_STRING(fw_ring_ctrl_subtype, +- htt_stats_buf->fw_ring_ctrl_subtype, +- HTT_STATS_SUBTYPE_MAX); ++ PRINT_ARRAY_TO_BUF(fw_ring_ctrl_subtype, ++ htt_stats_buf->fw_ring_ctrl_subtype, ++ HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ctrl_subtype = %s ", + fw_ring_ctrl_subtype); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mcast_data_msdu = %u", +@@ -3597,9 +3597,9 @@ htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_PDEV_FW_RING_MPDU_ERR_TLV_V:"); + +- ARRAY_TO_STRING(fw_ring_mpdu_err, +- htt_stats_buf->fw_ring_mpdu_err, +- HTT_RX_STATS_RXDMA_MAX_ERR); ++ PRINT_ARRAY_TO_BUF(fw_ring_mpdu_err, ++ htt_stats_buf->fw_ring_mpdu_err, ++ HTT_RX_STATS_RXDMA_MAX_ERR); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_err = %s\n", + fw_ring_mpdu_err); + +@@ -3625,9 +3625,9 @@ htt_print_rx_pdev_fw_mpdu_drop_tlv_v(con + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_MPDU_DROP_TLV_V:"); + +- ARRAY_TO_STRING(fw_mpdu_drop, +- htt_stats_buf->fw_mpdu_drop, +- num_elems); ++ PRINT_ARRAY_TO_BUF(fw_mpdu_drop, ++ htt_stats_buf->fw_mpdu_drop, ++ num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_mpdu_drop = %s\n", fw_mpdu_drop); + + if (len >= buf_len) +@@ -3654,9 +3654,9 @@ htt_print_rx_pdev_fw_stats_phy_err_tlv(c + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_phy_err_nct = %u", + htt_stats_buf->total_phy_err_cnt); + +- ARRAY_TO_STRING(phy_errs, +- htt_stats_buf->phy_err, +- HTT_STATS_PHY_ERR_MAX); ++ PRINT_ARRAY_TO_BUF(phy_errs, ++ htt_stats_buf->phy_err, ++ HTT_STATS_PHY_ERR_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) diff --git a/package/kernel/mac80211/patches/ath11k/0043-ath11k-Replace-HTT_DBG_OUT-with-scnprintf.patch b/package/kernel/mac80211/patches/ath11k/0043-ath11k-Replace-HTT_DBG_OUT-with-scnprintf.patch new file mode 100644 index 00000000000000..cb2495a00c4bd0 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0043-ath11k-Replace-HTT_DBG_OUT-with-scnprintf.patch @@ -0,0 +1,4451 @@ +From 6f442799bcfd62931ca100c7c5916bb5fc034302 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 28 Sep 2021 14:00:44 +0300 +Subject: [PATCH] ath11k: Replace HTT_DBG_OUT with scnprintf + +Get rid of macro HTT_DBG_OUT and replace it with scnprintf(). +The macro does not do anything else. Added required new line +characters to scnprintf() for proper display. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01105-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913223148.208026-3-jouni@codeaurora.org +--- + .../wireless/ath/ath11k/debugfs_htt_stats.c | 3370 ++++++++--------- + 1 file changed, 1683 insertions(+), 1687 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +@@ -10,9 +10,6 @@ + #include "debug.h" + #include "debugfs_htt_stats.h" + +-#define HTT_DBG_OUT(buf, len, fmt, ...) \ +- scnprintf(buf, len, fmt "\n", ##__VA_ARGS__) +- + #define HTT_MAX_STRING_LEN 256 + #define HTT_MAX_PRINT_CHAR_PER_ELEM 15 + +@@ -43,17 +40,17 @@ static inline void htt_print_stats_strin + + tag_len = tag_len >> 2; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:\n"); + + for (i = 0; i < tag_len; i++) { + index += scnprintf(&data[index], +- HTT_MAX_STRING_LEN - index, +- "%.*s", 4, (char *)&(htt_stats_buf->data[i])); ++ HTT_MAX_STRING_LEN - index, ++ "%.*s", 4, (char *)&(htt_stats_buf->data[i])); + if (index >= HTT_MAX_STRING_LEN) + break; + } + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "data = %s\n", data); ++ len += scnprintf(buf + len, buf_len - len, "data = %s\n\n", data); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -71,107 +68,107 @@ static inline void htt_print_tx_pdev_sta + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_CMN_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", +- htt_stats_buf->hw_queued); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", +- htt_stats_buf->hw_reaped); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun = %u", +- htt_stats_buf->underrun); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_paused = %u", +- htt_stats_buf->hw_paused); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_flush = %u", +- htt_stats_buf->hw_flush); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_filt = %u", +- htt_stats_buf->hw_filt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", +- htt_stats_buf->tx_abort); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_requeued = %u", +- htt_stats_buf->mpdu_requeued); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_xretry = %u", +- htt_stats_buf->tx_xretry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "data_rc = %u", +- htt_stats_buf->data_rc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_dropped_xretry = %u", +- htt_stats_buf->mpdu_dropped_xretry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "illegal_rate_phy_err = %u", +- htt_stats_buf->illgl_rate_phy_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cont_xretry = %u", +- htt_stats_buf->cont_xretry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_timeout = %u", +- htt_stats_buf->tx_timeout); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_resets = %u", +- htt_stats_buf->pdev_resets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_underrun = %u", +- htt_stats_buf->phy_underrun); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_ovf = %u", +- htt_stats_buf->txop_ovf); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted = %u", +- htt_stats_buf->seq_posted); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_failed_queueing = %u", +- htt_stats_buf->seq_failed_queueing); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_completed = %u", +- htt_stats_buf->seq_completed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_restarted = %u", +- htt_stats_buf->seq_restarted); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_seq_posted = %u", +- htt_stats_buf->mu_seq_posted); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_switch_hw_paused = %u", +- htt_stats_buf->seq_switch_hw_paused); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "next_seq_posted_dsr = %u", +- htt_stats_buf->next_seq_posted_dsr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted_isr = %u", +- htt_stats_buf->seq_posted_isr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_ctrl_cached = %u", +- htt_stats_buf->seq_ctrl_cached); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count_tqm = %u", +- htt_stats_buf->mpdu_count_tqm); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count_tqm = %u", +- htt_stats_buf->msdu_count_tqm); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_removed_tqm = %u", +- htt_stats_buf->mpdu_removed_tqm); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_removed_tqm = %u", +- htt_stats_buf->msdu_removed_tqm); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_sw_flush = %u", +- htt_stats_buf->mpdus_sw_flush); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", +- htt_stats_buf->mpdus_hw_filter); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_truncated = %u", +- htt_stats_buf->mpdus_truncated); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_ack_failed = %u", +- htt_stats_buf->mpdus_ack_failed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_expired = %u", +- htt_stats_buf->mpdus_expired); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_seq_hw_retry = %u", +- htt_stats_buf->mpdus_seq_hw_retry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", +- htt_stats_buf->ack_tlv_proc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt_valid = %u", +- htt_stats_buf->coex_abort_mpdu_cnt_valid); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt = %u", +- htt_stats_buf->coex_abort_mpdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_total_ppdus_tried_ota = %u", +- htt_stats_buf->num_total_ppdus_tried_ota); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_tried_ota = %u", +- htt_stats_buf->num_data_ppdus_tried_ota); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_enqued = %u", +- htt_stats_buf->local_ctrl_mgmt_enqued); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_freed = %u", +- htt_stats_buf->local_ctrl_mgmt_freed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_enqued = %u", +- htt_stats_buf->local_data_enqued); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_freed = %u", +- htt_stats_buf->local_data_freed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried = %u", +- htt_stats_buf->mpdu_tried); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "isr_wait_seq_posted = %u", +- htt_stats_buf->isr_wait_seq_posted); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_low = %u", +- htt_stats_buf->tx_active_dur_us_low); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_high = %u\n", +- htt_stats_buf->tx_active_dur_us_high); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_CMN_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "hw_queued = %u\n", ++ htt_stats_buf->hw_queued); ++ len += scnprintf(buf + len, buf_len - len, "hw_reaped = %u\n", ++ htt_stats_buf->hw_reaped); ++ len += scnprintf(buf + len, buf_len - len, "underrun = %u\n", ++ htt_stats_buf->underrun); ++ len += scnprintf(buf + len, buf_len - len, "hw_paused = %u\n", ++ htt_stats_buf->hw_paused); ++ len += scnprintf(buf + len, buf_len - len, "hw_flush = %u\n", ++ htt_stats_buf->hw_flush); ++ len += scnprintf(buf + len, buf_len - len, "hw_filt = %u\n", ++ htt_stats_buf->hw_filt); ++ len += scnprintf(buf + len, buf_len - len, "tx_abort = %u\n", ++ htt_stats_buf->tx_abort); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_requeued = %u\n", ++ htt_stats_buf->mpdu_requeued); ++ len += scnprintf(buf + len, buf_len - len, "tx_xretry = %u\n", ++ htt_stats_buf->tx_xretry); ++ len += scnprintf(buf + len, buf_len - len, "data_rc = %u\n", ++ htt_stats_buf->data_rc); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_dropped_xretry = %u\n", ++ htt_stats_buf->mpdu_dropped_xretry); ++ len += scnprintf(buf + len, buf_len - len, "illegal_rate_phy_err = %u\n", ++ htt_stats_buf->illgl_rate_phy_err); ++ len += scnprintf(buf + len, buf_len - len, "cont_xretry = %u\n", ++ htt_stats_buf->cont_xretry); ++ len += scnprintf(buf + len, buf_len - len, "tx_timeout = %u\n", ++ htt_stats_buf->tx_timeout); ++ len += scnprintf(buf + len, buf_len - len, "pdev_resets = %u\n", ++ htt_stats_buf->pdev_resets); ++ len += scnprintf(buf + len, buf_len - len, "phy_underrun = %u\n", ++ htt_stats_buf->phy_underrun); ++ len += scnprintf(buf + len, buf_len - len, "txop_ovf = %u\n", ++ htt_stats_buf->txop_ovf); ++ len += scnprintf(buf + len, buf_len - len, "seq_posted = %u\n", ++ htt_stats_buf->seq_posted); ++ len += scnprintf(buf + len, buf_len - len, "seq_failed_queueing = %u\n", ++ htt_stats_buf->seq_failed_queueing); ++ len += scnprintf(buf + len, buf_len - len, "seq_completed = %u\n", ++ htt_stats_buf->seq_completed); ++ len += scnprintf(buf + len, buf_len - len, "seq_restarted = %u\n", ++ htt_stats_buf->seq_restarted); ++ len += scnprintf(buf + len, buf_len - len, "mu_seq_posted = %u\n", ++ htt_stats_buf->mu_seq_posted); ++ len += scnprintf(buf + len, buf_len - len, "seq_switch_hw_paused = %u\n", ++ htt_stats_buf->seq_switch_hw_paused); ++ len += scnprintf(buf + len, buf_len - len, "next_seq_posted_dsr = %u\n", ++ htt_stats_buf->next_seq_posted_dsr); ++ len += scnprintf(buf + len, buf_len - len, "seq_posted_isr = %u\n", ++ htt_stats_buf->seq_posted_isr); ++ len += scnprintf(buf + len, buf_len - len, "seq_ctrl_cached = %u\n", ++ htt_stats_buf->seq_ctrl_cached); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_count_tqm = %u\n", ++ htt_stats_buf->mpdu_count_tqm); ++ len += scnprintf(buf + len, buf_len - len, "msdu_count_tqm = %u\n", ++ htt_stats_buf->msdu_count_tqm); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_removed_tqm = %u\n", ++ htt_stats_buf->mpdu_removed_tqm); ++ len += scnprintf(buf + len, buf_len - len, "msdu_removed_tqm = %u\n", ++ htt_stats_buf->msdu_removed_tqm); ++ len += scnprintf(buf + len, buf_len - len, "mpdus_sw_flush = %u\n", ++ htt_stats_buf->mpdus_sw_flush); ++ len += scnprintf(buf + len, buf_len - len, "mpdus_hw_filter = %u\n", ++ htt_stats_buf->mpdus_hw_filter); ++ len += scnprintf(buf + len, buf_len - len, "mpdus_truncated = %u\n", ++ htt_stats_buf->mpdus_truncated); ++ len += scnprintf(buf + len, buf_len - len, "mpdus_ack_failed = %u\n", ++ htt_stats_buf->mpdus_ack_failed); ++ len += scnprintf(buf + len, buf_len - len, "mpdus_expired = %u\n", ++ htt_stats_buf->mpdus_expired); ++ len += scnprintf(buf + len, buf_len - len, "mpdus_seq_hw_retry = %u\n", ++ htt_stats_buf->mpdus_seq_hw_retry); ++ len += scnprintf(buf + len, buf_len - len, "ack_tlv_proc = %u\n", ++ htt_stats_buf->ack_tlv_proc); ++ len += scnprintf(buf + len, buf_len - len, "coex_abort_mpdu_cnt_valid = %u\n", ++ htt_stats_buf->coex_abort_mpdu_cnt_valid); ++ len += scnprintf(buf + len, buf_len - len, "coex_abort_mpdu_cnt = %u\n", ++ htt_stats_buf->coex_abort_mpdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "num_total_ppdus_tried_ota = %u\n", ++ htt_stats_buf->num_total_ppdus_tried_ota); ++ len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_tried_ota = %u\n", ++ htt_stats_buf->num_data_ppdus_tried_ota); ++ len += scnprintf(buf + len, buf_len - len, "local_ctrl_mgmt_enqued = %u\n", ++ htt_stats_buf->local_ctrl_mgmt_enqued); ++ len += scnprintf(buf + len, buf_len - len, "local_ctrl_mgmt_freed = %u\n", ++ htt_stats_buf->local_ctrl_mgmt_freed); ++ len += scnprintf(buf + len, buf_len - len, "local_data_enqued = %u\n", ++ htt_stats_buf->local_data_enqued); ++ len += scnprintf(buf + len, buf_len - len, "local_data_freed = %u\n", ++ htt_stats_buf->local_data_freed); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_tried = %u\n", ++ htt_stats_buf->mpdu_tried); ++ len += scnprintf(buf + len, buf_len - len, "isr_wait_seq_posted = %u\n", ++ htt_stats_buf->isr_wait_seq_posted); ++ len += scnprintf(buf + len, buf_len - len, "tx_active_dur_us_low = %u\n", ++ htt_stats_buf->tx_active_dur_us_low); ++ len += scnprintf(buf + len, buf_len - len, "tx_active_dur_us_high = %u\n\n", ++ htt_stats_buf->tx_active_dur_us_high); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -193,10 +190,10 @@ htt_print_tx_pdev_stats_urrn_tlv_v(const + char urrn_stats[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_URRN_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_URRN_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_URRN_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(urrn_stats, htt_stats_buf->urrn_stats, num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "urrn_stats = %s\n", urrn_stats); ++ len += scnprintf(buf + len, buf_len - len, "urrn_stats = %s\n\n", urrn_stats); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -218,10 +215,10 @@ htt_print_tx_pdev_stats_flush_tlv_v(cons + char flush_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_FLUSH_REASON_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_FLUSH_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_FLUSH_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(flush_errs, htt_stats_buf->flush_errs, num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_errs = %s\n", flush_errs); ++ len += scnprintf(buf + len, buf_len - len, "flush_errs = %s\n\n", flush_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -243,11 +240,11 @@ htt_print_tx_pdev_stats_sifs_tlv_v(const + char sifs_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_SIFS_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_SIFS_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(sifs_status, htt_stats_buf->sifs_status, num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_status = %s\n", +- sifs_status); ++ len += scnprintf(buf + len, buf_len - len, "sifs_status = %s\n\n", ++ sifs_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -269,10 +266,10 @@ htt_print_tx_pdev_stats_phy_err_tlv_v(co + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_PHY_ERR_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_PHY_ERR_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_PHY_ERR_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(phy_errs, htt_stats_buf->phy_errs, num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); ++ len += scnprintf(buf + len, buf_len - len, "phy_errs = %s\n\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -294,12 +291,12 @@ htt_print_tx_pdev_stats_sifs_hist_tlv_v( + char sifs_hist_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_STATS_SIFS_HIST_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_STATS_SIFS_HIST_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(sifs_hist_status, htt_stats_buf->sifs_hist_status, num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_hist_status = %s\n", +- sifs_hist_status); ++ len += scnprintf(buf + len, buf_len - len, "sifs_hist_status = %s\n\n", ++ sifs_hist_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -318,23 +315,23 @@ htt_print_tx_pdev_stats_tx_ppdu_stats_tl + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_STATS_TX_PPDU_STATS_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_STATS_TX_PPDU_STATS_TLV_V:\n"); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_legacy_su = %u", +- htt_stats_buf->num_data_ppdus_legacy_su); ++ len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_legacy_su = %u\n", ++ htt_stats_buf->num_data_ppdus_legacy_su); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su = %u", +- htt_stats_buf->num_data_ppdus_ac_su); ++ len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_ac_su = %u\n", ++ htt_stats_buf->num_data_ppdus_ac_su); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su = %u", +- htt_stats_buf->num_data_ppdus_ax_su); ++ len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_ax_su = %u\n", ++ htt_stats_buf->num_data_ppdus_ax_su); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su_txbf = %u", +- htt_stats_buf->num_data_ppdus_ac_su_txbf); ++ len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_ac_su_txbf = %u\n", ++ htt_stats_buf->num_data_ppdus_ac_su_txbf); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su_txbf = %u\n", +- htt_stats_buf->num_data_ppdus_ax_su_txbf); ++ len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_ax_su_txbf = %u\n\n", ++ htt_stats_buf->num_data_ppdus_ax_su_txbf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -357,20 +354,20 @@ htt_print_tx_pdev_stats_tried_mpdu_cnt_h + u32 num_elements = ((tag_len - sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_STATS_TRIED_MPDU_CNT_HIST_TLV_V:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", +- htt_stats_buf->hist_bin_size); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_STATS_TRIED_MPDU_CNT_HIST_TLV_V:\n"); ++ len += scnprintf(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u\n", ++ htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + PRINT_ARRAY_TO_BUF(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tried_mpdu_cnt_hist = %s\n", +- tried_mpdu_cnt_hist); ++ len += scnprintf(buf + len, buf_len - len, "tried_mpdu_cnt_hist = %s\n\n", ++ tried_mpdu_cnt_hist); + } else { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "INSUFFICIENT PRINT BUFFER\n\n"); + } + + if (len >= buf_len) +@@ -390,14 +387,14 @@ static inline void htt_print_hw_stats_in + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_intr_name[HTT_STATS_MAX_HW_INTR_NAME_LEN + 1] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_INTR_MISC_TLV:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_INTR_MISC_TLV:\n"); + memcpy(hw_intr_name, &(htt_stats_buf->hw_intr_name[0]), + HTT_STATS_MAX_HW_INTR_NAME_LEN); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_intr_name = %s ", hw_intr_name); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mask = %u", +- htt_stats_buf->mask); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", +- htt_stats_buf->count); ++ len += scnprintf(buf + len, buf_len - len, "hw_intr_name = %s\n", hw_intr_name); ++ len += scnprintf(buf + len, buf_len - len, "mask = %u\n", ++ htt_stats_buf->mask); ++ len += scnprintf(buf + len, buf_len - len, "count = %u\n\n", ++ htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -417,13 +414,13 @@ htt_print_hw_stats_wd_timeout_tlv(const + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_module_name[HTT_STATS_MAX_HW_MODULE_NAME_LEN + 1] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_WD_TIMEOUT_TLV:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_WD_TIMEOUT_TLV:\n"); + memcpy(hw_module_name, &(htt_stats_buf->hw_module_name[0]), + HTT_STATS_MAX_HW_MODULE_NAME_LEN); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_module_name = %s ", +- hw_module_name); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u", +- htt_stats_buf->count); ++ len += scnprintf(buf + len, buf_len - len, "hw_module_name = %s\n", ++ hw_module_name); ++ len += scnprintf(buf + len, buf_len - len, "count = %u\n", ++ htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -441,29 +438,29 @@ static inline void htt_print_hw_stats_pd + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_PDEV_ERRS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", +- htt_stats_buf->tx_abort); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort_fail_count = %u", +- htt_stats_buf->tx_abort_fail_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort = %u", +- htt_stats_buf->rx_abort); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort_fail_count = %u", +- htt_stats_buf->rx_abort_fail_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "warm_reset = %u", +- htt_stats_buf->warm_reset); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cold_reset = %u", +- htt_stats_buf->cold_reset); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flush = %u", +- htt_stats_buf->tx_flush); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_glb_reset = %u", +- htt_stats_buf->tx_glb_reset); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_txq_reset = %u", +- htt_stats_buf->tx_txq_reset); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_timeout_reset = %u\n", +- htt_stats_buf->rx_timeout_reset); ++ len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_PDEV_ERRS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "tx_abort = %u\n", ++ htt_stats_buf->tx_abort); ++ len += scnprintf(buf + len, buf_len - len, "tx_abort_fail_count = %u\n", ++ htt_stats_buf->tx_abort_fail_count); ++ len += scnprintf(buf + len, buf_len - len, "rx_abort = %u\n", ++ htt_stats_buf->rx_abort); ++ len += scnprintf(buf + len, buf_len - len, "rx_abort_fail_count = %u\n", ++ htt_stats_buf->rx_abort_fail_count); ++ len += scnprintf(buf + len, buf_len - len, "warm_reset = %u\n", ++ htt_stats_buf->warm_reset); ++ len += scnprintf(buf + len, buf_len - len, "cold_reset = %u\n", ++ htt_stats_buf->cold_reset); ++ len += scnprintf(buf + len, buf_len - len, "tx_flush = %u\n", ++ htt_stats_buf->tx_flush); ++ len += scnprintf(buf + len, buf_len - len, "tx_glb_reset = %u\n", ++ htt_stats_buf->tx_glb_reset); ++ len += scnprintf(buf + len, buf_len - len, "tx_txq_reset = %u\n", ++ htt_stats_buf->tx_txq_reset); ++ len += scnprintf(buf + len, buf_len - len, "rx_timeout_reset = %u\n\n", ++ htt_stats_buf->rx_timeout_reset); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -481,35 +478,34 @@ static inline void htt_print_msdu_flow_s + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_MSDU_FLOW_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_update_timestamp = %u", +- htt_stats_buf->last_update_timestamp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_add_timestamp = %u", +- htt_stats_buf->last_add_timestamp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_remove_timestamp = %u", +- htt_stats_buf->last_remove_timestamp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "total_processed_msdu_count = %u", +- htt_stats_buf->total_processed_msdu_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cur_msdu_count_in_flowq = %u", +- htt_stats_buf->cur_msdu_count_in_flowq); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", +- htt_stats_buf->sw_peer_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flow_no = %u", +- htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", +- (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xF0000) >> +- 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_rule = %u", +- (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0x100000) >> +- 20); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_enqueue_count = %u", +- htt_stats_buf->last_cycle_enqueue_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_dequeue_count = %u", +- htt_stats_buf->last_cycle_dequeue_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_drop_count = %u", +- htt_stats_buf->last_cycle_drop_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "current_drop_th = %u\n", +- htt_stats_buf->current_drop_th); ++ len += scnprintf(buf + len, buf_len - len, "HTT_MSDU_FLOW_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "last_update_timestamp = %u\n", ++ htt_stats_buf->last_update_timestamp); ++ len += scnprintf(buf + len, buf_len - len, "last_add_timestamp = %u\n", ++ htt_stats_buf->last_add_timestamp); ++ len += scnprintf(buf + len, buf_len - len, "last_remove_timestamp = %u\n", ++ htt_stats_buf->last_remove_timestamp); ++ len += scnprintf(buf + len, buf_len - len, "total_processed_msdu_count = %u\n", ++ htt_stats_buf->total_processed_msdu_count); ++ len += scnprintf(buf + len, buf_len - len, "cur_msdu_count_in_flowq = %u\n", ++ htt_stats_buf->cur_msdu_count_in_flowq); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", ++ htt_stats_buf->sw_peer_id); ++ len += scnprintf(buf + len, buf_len - len, "tx_flow_no = %u\n", ++ htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", ++ (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "drop_rule = %u\n", ++ (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0x100000) >> ++ 20); ++ len += scnprintf(buf + len, buf_len - len, "last_cycle_enqueue_count = %u\n", ++ htt_stats_buf->last_cycle_enqueue_count); ++ len += scnprintf(buf + len, buf_len - len, "last_cycle_dequeue_count = %u\n", ++ htt_stats_buf->last_cycle_dequeue_count); ++ len += scnprintf(buf + len, buf_len - len, "last_cycle_drop_count = %u\n", ++ htt_stats_buf->last_cycle_drop_count); ++ len += scnprintf(buf + len, buf_len - len, "current_drop_th = %u\n\n", ++ htt_stats_buf->current_drop_th); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -528,38 +524,38 @@ static inline void htt_print_tx_tid_stat + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_TLV:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TID_STATS_TLV:\n"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", +- htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", +- (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", +- htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", +- (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & +- 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", +- htt_stats_buf->tid_flags); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", +- htt_stats_buf->hw_queued); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", +- htt_stats_buf->hw_reaped); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", +- htt_stats_buf->mpdus_hw_filter); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", +- htt_stats_buf->qdepth_bytes); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", +- htt_stats_buf->qdepth_num_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", +- htt_stats_buf->qdepth_num_mpdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", +- htt_stats_buf->last_scheduled_tsmp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", +- htt_stats_buf->pause_module_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u\n", +- htt_stats_buf->block_module_id); ++ len += scnprintf(buf + len, buf_len - len, "tid_name = %s\n", tid_name); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", ++ htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", ++ (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "num_sched_pending = %u\n", ++ htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "num_ppdu_in_hwq = %u\n", ++ (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & ++ 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "tid_flags = 0x%x\n", ++ htt_stats_buf->tid_flags); ++ len += scnprintf(buf + len, buf_len - len, "hw_queued = %u\n", ++ htt_stats_buf->hw_queued); ++ len += scnprintf(buf + len, buf_len - len, "hw_reaped = %u\n", ++ htt_stats_buf->hw_reaped); ++ len += scnprintf(buf + len, buf_len - len, "mpdus_hw_filter = %u\n", ++ htt_stats_buf->mpdus_hw_filter); ++ len += scnprintf(buf + len, buf_len - len, "qdepth_bytes = %u\n", ++ htt_stats_buf->qdepth_bytes); ++ len += scnprintf(buf + len, buf_len - len, "qdepth_num_msdu = %u\n", ++ htt_stats_buf->qdepth_num_msdu); ++ len += scnprintf(buf + len, buf_len - len, "qdepth_num_mpdu = %u\n", ++ htt_stats_buf->qdepth_num_mpdu); ++ len += scnprintf(buf + len, buf_len - len, "last_scheduled_tsmp = %u\n", ++ htt_stats_buf->last_scheduled_tsmp); ++ len += scnprintf(buf + len, buf_len - len, "pause_module_id = %u\n", ++ htt_stats_buf->pause_module_id); ++ len += scnprintf(buf + len, buf_len - len, "block_module_id = %u\n\n", ++ htt_stats_buf->block_module_id); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -578,42 +574,42 @@ static inline void htt_print_tx_tid_stat + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_V1_TLV:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TID_STATS_V1_TLV:\n"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", +- htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", +- (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", +- htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", +- (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & +- 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", +- htt_stats_buf->tid_flags); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_bytes = %u", +- htt_stats_buf->max_qdepth_bytes); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_n_msdus = %u", +- htt_stats_buf->max_qdepth_n_msdus); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rsvd = %u", +- htt_stats_buf->rsvd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", +- htt_stats_buf->qdepth_bytes); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", +- htt_stats_buf->qdepth_num_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", +- htt_stats_buf->qdepth_num_mpdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", +- htt_stats_buf->last_scheduled_tsmp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", +- htt_stats_buf->pause_module_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u", +- htt_stats_buf->block_module_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "allow_n_flags = 0x%x", +- htt_stats_buf->allow_n_flags); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sendn_frms_allowed = %u\n", +- htt_stats_buf->sendn_frms_allowed); ++ len += scnprintf(buf + len, buf_len - len, "tid_name = %s\n", tid_name); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", ++ htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", ++ (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "num_sched_pending = %u\n", ++ htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "num_ppdu_in_hwq = %u\n", ++ (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & ++ 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "tid_flags = 0x%x\n", ++ htt_stats_buf->tid_flags); ++ len += scnprintf(buf + len, buf_len - len, "max_qdepth_bytes = %u\n", ++ htt_stats_buf->max_qdepth_bytes); ++ len += scnprintf(buf + len, buf_len - len, "max_qdepth_n_msdus = %u\n", ++ htt_stats_buf->max_qdepth_n_msdus); ++ len += scnprintf(buf + len, buf_len - len, "rsvd = %u\n", ++ htt_stats_buf->rsvd); ++ len += scnprintf(buf + len, buf_len - len, "qdepth_bytes = %u\n", ++ htt_stats_buf->qdepth_bytes); ++ len += scnprintf(buf + len, buf_len - len, "qdepth_num_msdu = %u\n", ++ htt_stats_buf->qdepth_num_msdu); ++ len += scnprintf(buf + len, buf_len - len, "qdepth_num_mpdu = %u\n", ++ htt_stats_buf->qdepth_num_mpdu); ++ len += scnprintf(buf + len, buf_len - len, "last_scheduled_tsmp = %u\n", ++ htt_stats_buf->last_scheduled_tsmp); ++ len += scnprintf(buf + len, buf_len - len, "pause_module_id = %u\n", ++ htt_stats_buf->pause_module_id); ++ len += scnprintf(buf + len, buf_len - len, "block_module_id = %u\n", ++ htt_stats_buf->block_module_id); ++ len += scnprintf(buf + len, buf_len - len, "allow_n_flags = 0x%x\n", ++ htt_stats_buf->allow_n_flags); ++ len += scnprintf(buf + len, buf_len - len, "sendn_frms_allowed = %u\n\n", ++ htt_stats_buf->sendn_frms_allowed); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -632,21 +628,21 @@ static inline void htt_print_rx_tid_stat + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_TID_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", +- htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", +- (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_TID_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", ++ htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", ++ (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_in_reorder = %u", +- htt_stats_buf->dup_in_reorder); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_outside_window = %u", +- htt_stats_buf->dup_past_outside_window); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_within_window = %u", +- htt_stats_buf->dup_past_within_window); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdesc_err_decrypt = %u\n", +- htt_stats_buf->rxdesc_err_decrypt); ++ len += scnprintf(buf + len, buf_len - len, "tid_name = %s\n", tid_name); ++ len += scnprintf(buf + len, buf_len - len, "dup_in_reorder = %u\n", ++ htt_stats_buf->dup_in_reorder); ++ len += scnprintf(buf + len, buf_len - len, "dup_past_outside_window = %u\n", ++ htt_stats_buf->dup_past_outside_window); ++ len += scnprintf(buf + len, buf_len - len, "dup_past_within_window = %u\n", ++ htt_stats_buf->dup_past_within_window); ++ len += scnprintf(buf + len, buf_len - len, "rxdesc_err_decrypt = %u\n\n", ++ htt_stats_buf->rxdesc_err_decrypt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -665,14 +661,14 @@ static inline void htt_print_counter_tlv + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char counter_name[HTT_MAX_STRING_LEN] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_COUNTER_TLV:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_COUNTER_TLV:\n"); + + PRINT_ARRAY_TO_BUF(counter_name, + htt_stats_buf->counter_name, + HTT_MAX_COUNTER_NAME); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "counter_name = %s ", counter_name); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", +- htt_stats_buf->count); ++ len += scnprintf(buf + len, buf_len - len, "counter_name = %s\n", counter_name); ++ len += scnprintf(buf + len, buf_len - len, "count = %u\n\n", ++ htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -690,35 +686,35 @@ static inline void htt_print_peer_stats_ + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PEER_STATS_CMN_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_cnt = %u", +- htt_stats_buf->ppdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt = %u", +- htt_stats_buf->mpdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_cnt = %u", +- htt_stats_buf->msdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_bitmap = %u", +- htt_stats_buf->pause_bitmap); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "block_bitmap = %u", +- htt_stats_buf->block_bitmap); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_rssi = %d", +- htt_stats_buf->rssi); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_count = %llu", +- htt_stats_buf->peer_enqueued_count_low | +- ((u64)htt_stats_buf->peer_enqueued_count_high << 32)); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dequeued_count = %llu", +- htt_stats_buf->peer_dequeued_count_low | +- ((u64)htt_stats_buf->peer_dequeued_count_high << 32)); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dropped_count = %llu", +- htt_stats_buf->peer_dropped_count_low | +- ((u64)htt_stats_buf->peer_dropped_count_high << 32)); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "transmitted_ppdu_bytes = %llu", +- htt_stats_buf->ppdu_transmitted_bytes_low | +- ((u64)htt_stats_buf->ppdu_transmitted_bytes_high << 32)); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ttl_removed_count = %u", +- htt_stats_buf->peer_ttl_removed_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "inactive_time = %u\n", +- htt_stats_buf->inactive_time); ++ len += scnprintf(buf + len, buf_len - len, "HTT_PEER_STATS_CMN_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "ppdu_cnt = %u\n", ++ htt_stats_buf->ppdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_cnt = %u\n", ++ htt_stats_buf->mpdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "msdu_cnt = %u\n", ++ htt_stats_buf->msdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "pause_bitmap = %u\n", ++ htt_stats_buf->pause_bitmap); ++ len += scnprintf(buf + len, buf_len - len, "block_bitmap = %u\n", ++ htt_stats_buf->block_bitmap); ++ len += scnprintf(buf + len, buf_len - len, "last_rssi = %d\n", ++ htt_stats_buf->rssi); ++ len += scnprintf(buf + len, buf_len - len, "enqueued_count = %llu\n", ++ htt_stats_buf->peer_enqueued_count_low | ++ ((u64)htt_stats_buf->peer_enqueued_count_high << 32)); ++ len += scnprintf(buf + len, buf_len - len, "dequeued_count = %llu\n", ++ htt_stats_buf->peer_dequeued_count_low | ++ ((u64)htt_stats_buf->peer_dequeued_count_high << 32)); ++ len += scnprintf(buf + len, buf_len - len, "dropped_count = %llu\n", ++ htt_stats_buf->peer_dropped_count_low | ++ ((u64)htt_stats_buf->peer_dropped_count_high << 32)); ++ len += scnprintf(buf + len, buf_len - len, "transmitted_ppdu_bytes = %llu\n", ++ htt_stats_buf->ppdu_transmitted_bytes_low | ++ ((u64)htt_stats_buf->ppdu_transmitted_bytes_high << 32)); ++ len += scnprintf(buf + len, buf_len - len, "ttl_removed_count = %u\n", ++ htt_stats_buf->peer_ttl_removed_count); ++ len += scnprintf(buf + len, buf_len - len, "inactive_time = %u\n\n", ++ htt_stats_buf->inactive_time); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -736,29 +732,29 @@ static inline void htt_print_peer_detail + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PEER_DETAILS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_type = %u", +- htt_stats_buf->peer_type); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", +- htt_stats_buf->sw_peer_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", +- htt_stats_buf->vdev_pdev_ast_idx & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", +- (htt_stats_buf->vdev_pdev_ast_idx & 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ast_idx = %u", +- (htt_stats_buf->vdev_pdev_ast_idx & 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "mac_addr = %02x:%02x:%02x:%02x:%02x:%02x", +- htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF, +- (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF00) >> 8, +- (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF0000) >> 16, +- (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF000000) >> 24, +- (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF), +- (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_flags = 0x%x", +- htt_stats_buf->peer_flags); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_flags = 0x%x\n", +- htt_stats_buf->qpeer_flags); ++ len += scnprintf(buf + len, buf_len - len, "HTT_PEER_DETAILS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "peer_type = %u\n", ++ htt_stats_buf->peer_type); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", ++ htt_stats_buf->sw_peer_id); ++ len += scnprintf(buf + len, buf_len - len, "vdev_id = %u\n", ++ htt_stats_buf->vdev_pdev_ast_idx & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", ++ (htt_stats_buf->vdev_pdev_ast_idx & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "ast_idx = %u\n", ++ (htt_stats_buf->vdev_pdev_ast_idx & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, ++ "mac_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", ++ htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF, ++ (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF00) >> 8, ++ (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF0000) >> 16, ++ (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF000000) >> 24, ++ (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF), ++ (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "peer_flags = 0x%x\n", ++ htt_stats_buf->peer_flags); ++ len += scnprintf(buf + len, buf_len - len, "qpeer_flags = 0x%x\n\n", ++ htt_stats_buf->qpeer_flags); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -785,64 +781,64 @@ static inline void htt_print_tx_peer_rat + goto fail; + } + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PEER_RATE_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", +- htt_stats_buf->tx_ldpc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", +- htt_stats_buf->rts_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", +- htt_stats_buf->ack_rssi); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_PEER_RATE_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n", ++ htt_stats_buf->tx_ldpc); ++ len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", ++ htt_stats_buf->rts_cnt); ++ len += scnprintf(buf + len, buf_len - len, "ack_rssi = %u\n", ++ htt_stats_buf->ack_rssi); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_su_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_su_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_su_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mu_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mu_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_mu_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, + htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, + htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_bw = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_stbc = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_pream = %s\n", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(tx_gi[j], + htt_stats_buf->tx_gi[j], + HTT_TX_PEER_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, "tx_gi[%u] = %s\n", ++ j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, + htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_dcm = %s\n\n", str_buf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -880,63 +876,63 @@ static inline void htt_print_rx_peer_rat + goto fail; + } + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PEER_RATE_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", +- htt_stats_buf->nsts); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", +- htt_stats_buf->rx_ldpc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", +- htt_stats_buf->rts_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", +- htt_stats_buf->rssi_mgmt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", +- htt_stats_buf->rssi_data); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", +- htt_stats_buf->rssi_comb); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_PEER_RATE_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "nsts = %u\n", ++ htt_stats_buf->nsts); ++ len += scnprintf(buf + len, buf_len - len, "rx_ldpc = %u\n", ++ htt_stats_buf->rx_ldpc); ++ len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", ++ htt_stats_buf->rts_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rssi_mgmt = %u\n", ++ htt_stats_buf->rssi_mgmt); ++ len += scnprintf(buf + len, buf_len - len, "rssi_data = %u\n", ++ htt_stats_buf->rssi_data); ++ len += scnprintf(buf + len, buf_len - len, "rssi_comb = %u\n", ++ htt_stats_buf->rssi_comb); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_dcm = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_stbc = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_bw = %s\n", str_buf); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { + PRINT_ARRAY_TO_BUF(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PEER_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", +- j, rssi_chain[j]); ++ len += scnprintf(buf + len, buf_len - len, "rssi_chain[%u] = %s\n", ++ j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", +- j, rx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, "rx_gi[%u] = %s\n", ++ j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s\n", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_pream = %s\n\n", str_buf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -962,13 +958,13 @@ htt_print_tx_hwq_mu_mimo_sch_stats_tlv(c + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_SCH_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", +- htt_stats_buf->mu_mimo_sch_posted); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", +- htt_stats_buf->mu_mimo_sch_failed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", +- htt_stats_buf->mu_mimo_ppdu_posted); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_SCH_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_sch_posted = %u\n", ++ htt_stats_buf->mu_mimo_sch_posted); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_sch_failed = %u\n", ++ htt_stats_buf->mu_mimo_sch_failed); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n\n", ++ htt_stats_buf->mu_mimo_ppdu_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -987,22 +983,22 @@ htt_print_tx_hwq_mu_mimo_mpdu_stats_tlv( + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_HWQ_MU_MIMO_MPDU_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_queued_usr = %u", +- htt_stats_buf->mu_mimo_mpdus_queued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_tried_usr = %u", +- htt_stats_buf->mu_mimo_mpdus_tried_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_failed_usr = %u", +- htt_stats_buf->mu_mimo_mpdus_failed_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_requeued_usr = %u", +- htt_stats_buf->mu_mimo_mpdus_requeued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_err_no_ba_usr = %u", +- htt_stats_buf->mu_mimo_err_no_ba_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdu_underrun_usr = %u", +- htt_stats_buf->mu_mimo_mpdu_underrun_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ampdu_underrun_usr = %u\n", +- htt_stats_buf->mu_mimo_ampdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_HWQ_MU_MIMO_MPDU_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_mpdus_queued_usr = %u\n", ++ htt_stats_buf->mu_mimo_mpdus_queued_usr); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_mpdus_tried_usr = %u\n", ++ htt_stats_buf->mu_mimo_mpdus_tried_usr); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_mpdus_failed_usr = %u\n", ++ htt_stats_buf->mu_mimo_mpdus_failed_usr); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_mpdus_requeued_usr = %u\n", ++ htt_stats_buf->mu_mimo_mpdus_requeued_usr); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_err_no_ba_usr = %u\n", ++ htt_stats_buf->mu_mimo_err_no_ba_usr); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_mpdu_underrun_usr = %u\n", ++ htt_stats_buf->mu_mimo_mpdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_ampdu_underrun_usr = %u\n\n", ++ htt_stats_buf->mu_mimo_ampdu_underrun_usr); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1021,11 +1017,11 @@ htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(c + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_CMN_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__hwq_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u\n", +- (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_CMN_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__hwq_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "hwq_id = %u\n\n", ++ (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1044,51 +1040,51 @@ htt_print_tx_hwq_stats_cmn_tlv(const voi + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + /* TODO: HKDBG */ +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_STATS_CMN_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__hwq_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u", +- (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "xretry = %u", +- htt_stats_buf->xretry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_cnt = %u", +- htt_stats_buf->underrun_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cnt = %u", +- htt_stats_buf->flush_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "filt_cnt = %u", +- htt_stats_buf->filt_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_bmap = %u", +- htt_stats_buf->null_mpdu_bmap); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "user_ack_failure = %u", +- htt_stats_buf->user_ack_failure); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", +- htt_stats_buf->ack_tlv_proc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_id_proc = %u", +- htt_stats_buf->sched_id_proc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_tx_count = %u", +- htt_stats_buf->null_mpdu_tx_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_bmap_not_recvd = %u", +- htt_stats_buf->mpdu_bmap_not_recvd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_bar = %u", +- htt_stats_buf->num_bar); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", +- htt_stats_buf->rts); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", +- htt_stats_buf->cts2self); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", +- htt_stats_buf->qos_null); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried_cnt = %u", +- htt_stats_buf->mpdu_tried_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queued_cnt = %u", +- htt_stats_buf->mpdu_queued_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_ack_fail_cnt = %u", +- htt_stats_buf->mpdu_ack_fail_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_filt_cnt = %u", +- htt_stats_buf->mpdu_filt_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "false_mpdu_ack_count = %u", +- htt_stats_buf->false_mpdu_ack_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_timeout = %u\n", +- htt_stats_buf->txq_timeout); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_HWQ_STATS_CMN_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__hwq_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "hwq_id = %u\n", ++ (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "xretry = %u\n", ++ htt_stats_buf->xretry); ++ len += scnprintf(buf + len, buf_len - len, "underrun_cnt = %u\n", ++ htt_stats_buf->underrun_cnt); ++ len += scnprintf(buf + len, buf_len - len, "flush_cnt = %u\n", ++ htt_stats_buf->flush_cnt); ++ len += scnprintf(buf + len, buf_len - len, "filt_cnt = %u\n", ++ htt_stats_buf->filt_cnt); ++ len += scnprintf(buf + len, buf_len - len, "null_mpdu_bmap = %u\n", ++ htt_stats_buf->null_mpdu_bmap); ++ len += scnprintf(buf + len, buf_len - len, "user_ack_failure = %u\n", ++ htt_stats_buf->user_ack_failure); ++ len += scnprintf(buf + len, buf_len - len, "ack_tlv_proc = %u\n", ++ htt_stats_buf->ack_tlv_proc); ++ len += scnprintf(buf + len, buf_len - len, "sched_id_proc = %u\n", ++ htt_stats_buf->sched_id_proc); ++ len += scnprintf(buf + len, buf_len - len, "null_mpdu_tx_count = %u\n", ++ htt_stats_buf->null_mpdu_tx_count); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_bmap_not_recvd = %u\n", ++ htt_stats_buf->mpdu_bmap_not_recvd); ++ len += scnprintf(buf + len, buf_len - len, "num_bar = %u\n", ++ htt_stats_buf->num_bar); ++ len += scnprintf(buf + len, buf_len - len, "rts = %u\n", ++ htt_stats_buf->rts); ++ len += scnprintf(buf + len, buf_len - len, "cts2self = %u\n", ++ htt_stats_buf->cts2self); ++ len += scnprintf(buf + len, buf_len - len, "qos_null = %u\n", ++ htt_stats_buf->qos_null); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_tried_cnt = %u\n", ++ htt_stats_buf->mpdu_tried_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_queued_cnt = %u\n", ++ htt_stats_buf->mpdu_queued_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_ack_fail_cnt = %u\n", ++ htt_stats_buf->mpdu_ack_fail_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_filt_cnt = %u\n", ++ htt_stats_buf->mpdu_filt_cnt); ++ len += scnprintf(buf + len, buf_len - len, "false_mpdu_ack_count = %u\n", ++ htt_stats_buf->false_mpdu_ack_count); ++ len += scnprintf(buf + len, buf_len - len, "txq_timeout = %u\n\n", ++ htt_stats_buf->txq_timeout); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1110,15 +1106,15 @@ htt_print_tx_hwq_difs_latency_stats_tlv_ + u16 data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS); + char difs_latency_hist[HTT_MAX_STRING_LEN] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_HWQ_DIFS_LATENCY_STATS_TLV_V:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hist_intvl = %u", +- htt_stats_buf->hist_intvl); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_HWQ_DIFS_LATENCY_STATS_TLV_V:\n"); ++ len += scnprintf(buf + len, buf_len - len, "hist_intvl = %u\n", ++ htt_stats_buf->hist_intvl); + + PRINT_ARRAY_TO_BUF(difs_latency_hist, htt_stats_buf->difs_latency_hist, + data_len); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "difs_latency_hist = %s\n", +- difs_latency_hist); ++ len += scnprintf(buf + len, buf_len - len, "difs_latency_hist = %s\n\n", ++ difs_latency_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1142,12 +1138,12 @@ htt_print_tx_hwq_cmd_result_stats_tlv_v( + + data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_RESULT_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_HWQ_CMD_RESULT_STATS_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_HWQ_CMD_RESULT_STATS_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(cmd_result, htt_stats_buf->cmd_result, data_len); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_result = %s\n", cmd_result); ++ len += scnprintf(buf + len, buf_len - len, "cmd_result = %s\n\n", cmd_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1171,11 +1167,11 @@ htt_print_tx_hwq_cmd_stall_stats_tlv_v(c + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_STALL_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_CMD_STALL_STATS_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_HWQ_CMD_STALL_STATS_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(cmd_stall_status, htt_stats_buf->cmd_stall_status, num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_stall_status = %s\n", +- cmd_stall_status); ++ len += scnprintf(buf + len, buf_len - len, "cmd_stall_status = %s\n\n", ++ cmd_stall_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1199,11 +1195,11 @@ htt_print_tx_hwq_fes_result_stats_tlv_v( + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_FES_RESULT_STATS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_HWQ_FES_RESULT_STATS_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_HWQ_FES_RESULT_STATS_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(fes_result, htt_stats_buf->fes_result, num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fes_result = %s\n", fes_result); ++ len += scnprintf(buf + len, buf_len - len, "fes_result = %s\n\n", fes_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1227,21 +1223,21 @@ htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv + sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_HWQ_TRIED_MPDU_CNT_HIST_TLV_V:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", +- htt_stats_buf->hist_bin_size); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_HWQ_TRIED_MPDU_CNT_HIST_TLV_V:\n"); ++ len += scnprintf(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u\n", ++ htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + PRINT_ARRAY_TO_BUF(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "tried_mpdu_cnt_hist = %s\n", +- tried_mpdu_cnt_hist); ++ len += scnprintf(buf + len, buf_len - len, ++ "tried_mpdu_cnt_hist = %s\n\n", ++ tried_mpdu_cnt_hist); + } else { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER "); ++ len += scnprintf(buf + len, buf_len - len, ++ "INSUFFICIENT PRINT BUFFER\n"); + } + + if (len >= buf_len) +@@ -1265,18 +1261,18 @@ htt_print_tx_hwq_txop_used_cnt_hist_tlv_ + u32 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_HWQ_TXOP_USED_CNT_HIST_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_HWQ_TXOP_USED_CNT_HIST_TLV_V:\n"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + PRINT_ARRAY_TO_BUF(txop_used_cnt_hist, + htt_stats_buf->txop_used_cnt_hist, + num_elements); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_used_cnt_hist = %s\n", +- txop_used_cnt_hist); ++ len += scnprintf(buf + len, buf_len - len, "txop_used_cnt_hist = %s\n\n", ++ txop_used_cnt_hist); + } else { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER "); ++ len += scnprintf(buf + len, buf_len - len, ++ "INSUFFICIENT PRINT BUFFER\n"); + } + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1300,86 +1296,86 @@ static inline void htt_print_tx_sounding + const u32 *cbf_160 = htt_stats_buf->cbf_160; + + if (htt_stats_buf->tx_sounding_mode == HTT_TX_AC_SOUNDING_MODE) { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "\nHTT_TX_AC_SOUNDING_STATS_TLV:\n"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", +- cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", +- cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", +- cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", +- cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "\nHTT_TX_AC_SOUNDING_STATS_TLV:\n\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", +- i, +- htt_stats_buf->sounding[0], +- htt_stats_buf->sounding[1], +- htt_stats_buf->sounding[2], +- htt_stats_buf->sounding[3]); ++ len += scnprintf(buf + len, buf_len - len, ++ "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u\n", ++ i, ++ htt_stats_buf->sounding[0], ++ htt_stats_buf->sounding[1], ++ htt_stats_buf->sounding[2], ++ htt_stats_buf->sounding[3]); + } + } else if (htt_stats_buf->tx_sounding_mode == HTT_TX_AX_SOUNDING_MODE) { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", +- cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", +- cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", +- cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", +- cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], +- cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u\n", ++ cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], ++ cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", +- i, +- htt_stats_buf->sounding[0], +- htt_stats_buf->sounding[1], +- htt_stats_buf->sounding[2], +- htt_stats_buf->sounding[3]); ++ len += scnprintf(buf + len, buf_len - len, ++ "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u\n", ++ i, ++ htt_stats_buf->sounding[0], ++ htt_stats_buf->sounding[1], ++ htt_stats_buf->sounding[2], ++ htt_stats_buf->sounding[3]); + } + } + +@@ -1400,31 +1396,31 @@ htt_print_tx_selfgen_cmn_stats_tlv(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_CMN_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "su_bar = %u", +- htt_stats_buf->su_bar); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", +- htt_stats_buf->rts); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", +- htt_stats_buf->cts2self); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", +- htt_stats_buf->qos_null); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_1 = %u", +- htt_stats_buf->delayed_bar_1); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_2 = %u", +- htt_stats_buf->delayed_bar_2); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_3 = %u", +- htt_stats_buf->delayed_bar_3); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_4 = %u", +- htt_stats_buf->delayed_bar_4); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_5 = %u", +- htt_stats_buf->delayed_bar_5); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_6 = %u", +- htt_stats_buf->delayed_bar_6); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_7 = %u\n", +- htt_stats_buf->delayed_bar_7); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_CMN_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "su_bar = %u\n", ++ htt_stats_buf->su_bar); ++ len += scnprintf(buf + len, buf_len - len, "rts = %u\n", ++ htt_stats_buf->rts); ++ len += scnprintf(buf + len, buf_len - len, "cts2self = %u\n", ++ htt_stats_buf->cts2self); ++ len += scnprintf(buf + len, buf_len - len, "qos_null = %u\n", ++ htt_stats_buf->qos_null); ++ len += scnprintf(buf + len, buf_len - len, "delayed_bar_1 = %u\n", ++ htt_stats_buf->delayed_bar_1); ++ len += scnprintf(buf + len, buf_len - len, "delayed_bar_2 = %u\n", ++ htt_stats_buf->delayed_bar_2); ++ len += scnprintf(buf + len, buf_len - len, "delayed_bar_3 = %u\n", ++ htt_stats_buf->delayed_bar_3); ++ len += scnprintf(buf + len, buf_len - len, "delayed_bar_4 = %u\n", ++ htt_stats_buf->delayed_bar_4); ++ len += scnprintf(buf + len, buf_len - len, "delayed_bar_5 = %u\n", ++ htt_stats_buf->delayed_bar_5); ++ len += scnprintf(buf + len, buf_len - len, "delayed_bar_6 = %u\n", ++ htt_stats_buf->delayed_bar_6); ++ len += scnprintf(buf + len, buf_len - len, "delayed_bar_7 = %u\n\n", ++ htt_stats_buf->delayed_bar_7); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1443,21 +1439,21 @@ htt_print_tx_selfgen_ac_stats_tlv(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa = %u", +- htt_stats_buf->ac_su_ndpa); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp = %u", +- htt_stats_buf->ac_su_ndp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa = %u", +- htt_stats_buf->ac_mu_mimo_ndpa); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp = %u", +- htt_stats_buf->ac_mu_mimo_ndp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_1 = %u", +- htt_stats_buf->ac_mu_mimo_brpoll_1); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_2 = %u", +- htt_stats_buf->ac_mu_mimo_brpoll_2); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_3 = %u\n", +- htt_stats_buf->ac_mu_mimo_brpoll_3); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "ac_su_ndpa = %u\n", ++ htt_stats_buf->ac_su_ndpa); ++ len += scnprintf(buf + len, buf_len - len, "ac_su_ndp = %u\n", ++ htt_stats_buf->ac_su_ndp); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndpa = %u\n", ++ htt_stats_buf->ac_mu_mimo_ndpa); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndp = %u\n", ++ htt_stats_buf->ac_mu_mimo_ndp); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brpoll_1 = %u\n", ++ htt_stats_buf->ac_mu_mimo_brpoll_1); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brpoll_2 = %u\n", ++ htt_stats_buf->ac_mu_mimo_brpoll_2); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brpoll_3 = %u\n\n", ++ htt_stats_buf->ac_mu_mimo_brpoll_3); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1476,37 +1472,37 @@ htt_print_tx_selfgen_ax_stats_tlv(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa = %u", +- htt_stats_buf->ax_su_ndpa); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp = %u", +- htt_stats_buf->ax_su_ndp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa = %u", +- htt_stats_buf->ax_mu_mimo_ndpa); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp = %u", +- htt_stats_buf->ax_mu_mimo_ndp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_1 = %u", +- htt_stats_buf->ax_mu_mimo_brpoll_1); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_2 = %u", +- htt_stats_buf->ax_mu_mimo_brpoll_2); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_3 = %u", +- htt_stats_buf->ax_mu_mimo_brpoll_3); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_4 = %u", +- htt_stats_buf->ax_mu_mimo_brpoll_4); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_5 = %u", +- htt_stats_buf->ax_mu_mimo_brpoll_5); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_6 = %u", +- htt_stats_buf->ax_mu_mimo_brpoll_6); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_7 = %u", +- htt_stats_buf->ax_mu_mimo_brpoll_7); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger = %u", +- htt_stats_buf->ax_basic_trigger); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger = %u", +- htt_stats_buf->ax_bsr_trigger); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger = %u", +- htt_stats_buf->ax_mu_bar_trigger); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger = %u\n", +- htt_stats_buf->ax_mu_rts_trigger); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "ax_su_ndpa = %u\n", ++ htt_stats_buf->ax_su_ndpa); ++ len += scnprintf(buf + len, buf_len - len, "ax_su_ndp = %u\n", ++ htt_stats_buf->ax_su_ndp); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndpa = %u\n", ++ htt_stats_buf->ax_mu_mimo_ndpa); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndp = %u\n", ++ htt_stats_buf->ax_mu_mimo_ndp); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brpoll_1 = %u\n", ++ htt_stats_buf->ax_mu_mimo_brpoll_1); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brpoll_2 = %u\n", ++ htt_stats_buf->ax_mu_mimo_brpoll_2); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brpoll_3 = %u\n", ++ htt_stats_buf->ax_mu_mimo_brpoll_3); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brpoll_4 = %u\n", ++ htt_stats_buf->ax_mu_mimo_brpoll_4); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brpoll_5 = %u\n", ++ htt_stats_buf->ax_mu_mimo_brpoll_5); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brpoll_6 = %u\n", ++ htt_stats_buf->ax_mu_mimo_brpoll_6); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brpoll_7 = %u\n", ++ htt_stats_buf->ax_mu_mimo_brpoll_7); ++ len += scnprintf(buf + len, buf_len - len, "ax_basic_trigger = %u\n", ++ htt_stats_buf->ax_basic_trigger); ++ len += scnprintf(buf + len, buf_len - len, "ax_bsr_trigger = %u\n", ++ htt_stats_buf->ax_bsr_trigger); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_bar_trigger = %u\n", ++ htt_stats_buf->ax_mu_bar_trigger); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_rts_trigger = %u\n\n", ++ htt_stats_buf->ax_mu_rts_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1525,21 +1521,21 @@ htt_print_tx_selfgen_ac_err_stats_tlv(co + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_ERR_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp_err = %u", +- htt_stats_buf->ac_su_ndp_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa_err = %u", +- htt_stats_buf->ac_su_ndpa_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa_err = %u", +- htt_stats_buf->ac_mu_mimo_ndpa_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp_err = %u", +- htt_stats_buf->ac_mu_mimo_ndp_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp1_err = %u", +- htt_stats_buf->ac_mu_mimo_brp1_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp2_err = %u", +- htt_stats_buf->ac_mu_mimo_brp2_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp3_err = %u\n", +- htt_stats_buf->ac_mu_mimo_brp3_err); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_ERR_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "ac_su_ndp_err = %u\n", ++ htt_stats_buf->ac_su_ndp_err); ++ len += scnprintf(buf + len, buf_len - len, "ac_su_ndpa_err = %u\n", ++ htt_stats_buf->ac_su_ndpa_err); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndpa_err = %u\n", ++ htt_stats_buf->ac_mu_mimo_ndpa_err); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_ndp_err = %u\n", ++ htt_stats_buf->ac_mu_mimo_ndp_err); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brp1_err = %u\n", ++ htt_stats_buf->ac_mu_mimo_brp1_err); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brp2_err = %u\n", ++ htt_stats_buf->ac_mu_mimo_brp2_err); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_brp3_err = %u\n\n", ++ htt_stats_buf->ac_mu_mimo_brp3_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1558,37 +1554,37 @@ htt_print_tx_selfgen_ax_err_stats_tlv(co + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_ERR_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp_err = %u", +- htt_stats_buf->ax_su_ndp_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa_err = %u", +- htt_stats_buf->ax_su_ndpa_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa_err = %u", +- htt_stats_buf->ax_mu_mimo_ndpa_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp_err = %u", +- htt_stats_buf->ax_mu_mimo_ndp_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp1_err = %u", +- htt_stats_buf->ax_mu_mimo_brp1_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp2_err = %u", +- htt_stats_buf->ax_mu_mimo_brp2_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp3_err = %u", +- htt_stats_buf->ax_mu_mimo_brp3_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp4_err = %u", +- htt_stats_buf->ax_mu_mimo_brp4_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp5_err = %u", +- htt_stats_buf->ax_mu_mimo_brp5_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp6_err = %u", +- htt_stats_buf->ax_mu_mimo_brp6_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp7_err = %u", +- htt_stats_buf->ax_mu_mimo_brp7_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger_err = %u", +- htt_stats_buf->ax_basic_trigger_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger_err = %u", +- htt_stats_buf->ax_bsr_trigger_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger_err = %u", +- htt_stats_buf->ax_mu_bar_trigger_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger_err = %u\n", +- htt_stats_buf->ax_mu_rts_trigger_err); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_ERR_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "ax_su_ndp_err = %u\n", ++ htt_stats_buf->ax_su_ndp_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_su_ndpa_err = %u\n", ++ htt_stats_buf->ax_su_ndpa_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndpa_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_ndpa_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_ndp_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_ndp_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brp1_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_brp1_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brp2_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_brp2_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brp3_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_brp3_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brp4_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_brp4_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brp5_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_brp5_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brp6_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_brp6_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_brp7_err = %u\n", ++ htt_stats_buf->ax_mu_mimo_brp7_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_basic_trigger_err = %u\n", ++ htt_stats_buf->ax_basic_trigger_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_bsr_trigger_err = %u\n", ++ htt_stats_buf->ax_bsr_trigger_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_bar_trigger_err = %u\n", ++ htt_stats_buf->ax_mu_bar_trigger_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_rts_trigger_err = %u\n\n", ++ htt_stats_buf->ax_mu_rts_trigger_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1608,35 +1604,35 @@ htt_print_tx_pdev_mu_mimo_sch_stats_tlv( + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_MU_MIMO_SCH_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", +- htt_stats_buf->mu_mimo_sch_posted); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", +- htt_stats_buf->mu_mimo_sch_failed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", +- htt_stats_buf->mu_mimo_ppdu_posted); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_MU_MIMO_SCH_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_sch_posted = %u\n", ++ htt_stats_buf->mu_mimo_sch_posted); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_sch_failed = %u\n", ++ htt_stats_buf->mu_mimo_sch_failed); ++ len += scnprintf(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n\n", ++ htt_stats_buf->mu_mimo_ppdu_posted); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "11ac MU_MIMO SCH STATS:"); ++ len += scnprintf(buf + len, buf_len - len, "11ac MU_MIMO SCH STATS:\n"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_sch_nusers_%u = %u", +- i, htt_stats_buf->ac_mu_mimo_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ac_mu_mimo_sch_nusers[i]); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax MU_MIMO SCH STATS:"); ++ len += scnprintf(buf + len, buf_len - len, "\n11ax MU_MIMO SCH STATS:\n"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_sch_nusers_%u = %u", +- i, htt_stats_buf->ax_mu_mimo_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_mu_mimo_sch_nusers[i]); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax OFDMA SCH STATS:"); ++ len += scnprintf(buf + len, buf_len - len, "\n11ax OFDMA SCH STATS:\n"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_ofdma_sch_nusers_%u = %u", +- i, htt_stats_buf->ax_ofdma_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_ofdma_sch_nusers[i]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1657,114 +1653,114 @@ htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC) { + if (!htt_stats_buf->user_index) +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_MU_MIMO_AC_MPDU_STATS:\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_MU_MIMO_AC_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS) { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_mpdus_queued_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_queued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_mpdus_tried_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_tried_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_mpdus_failed_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_failed_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_mpdus_requeued_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_requeued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_err_no_ba_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->err_no_ba_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_mpdu_underrun_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdu_underrun_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_ampdu_underrun_usr_%u = %u\n", +- htt_stats_buf->user_index, +- htt_stats_buf->ampdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_mpdus_queued_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_queued_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_mpdus_tried_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_tried_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_mpdus_failed_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_failed_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_mpdus_requeued_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_requeued_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_err_no_ba_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->err_no_ba_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_mpdu_underrun_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_ampdu_underrun_usr_%u = %u\n\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX) { + if (!htt_stats_buf->user_index) +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_MU_MIMO_AX_MPDU_STATS:\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_MU_MIMO_AX_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS) { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_mpdus_queued_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_queued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_mpdus_tried_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_tried_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_mpdus_failed_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_failed_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_mpdus_requeued_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_requeued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_err_no_ba_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->err_no_ba_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_mpdu_underrun_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdu_underrun_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_ampdu_underrun_usr_%u = %u\n", +- htt_stats_buf->user_index, +- htt_stats_buf->ampdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_mpdus_queued_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_queued_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_mpdus_tried_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_tried_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_mpdus_failed_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_failed_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_mpdus_requeued_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_requeued_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_err_no_ba_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->err_no_ba_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_mpdu_underrun_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_ampdu_underrun_usr_%u = %u\n\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX) { + if (!htt_stats_buf->user_index) +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_AX_MU_OFDMA_MPDU_STATS:\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_AX_MU_OFDMA_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS) { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_ofdma_mpdus_queued_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_queued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_ofdma_mpdus_tried_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_tried_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_ofdma_mpdus_failed_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_failed_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_ofdma_mpdus_requeued_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdus_requeued_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_ofdma_err_no_ba_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->err_no_ba_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_ofdma_mpdu_underrun_usr_%u = %u", +- htt_stats_buf->user_index, +- htt_stats_buf->mpdu_underrun_usr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_ofdma_ampdu_underrun_usr_%u = %u\n", +- htt_stats_buf->user_index, +- htt_stats_buf->ampdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_ofdma_mpdus_queued_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_queued_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_ofdma_mpdus_tried_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_tried_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_ofdma_mpdus_failed_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_failed_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_ofdma_mpdus_requeued_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdus_requeued_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_ofdma_err_no_ba_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->err_no_ba_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_ofdma_mpdu_underrun_usr_%u = %u\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->mpdu_underrun_usr); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_ofdma_ampdu_underrun_usr_%u = %u\n\n", ++ htt_stats_buf->user_index, ++ htt_stats_buf->ampdu_underrun_usr); + } + } + +@@ -1788,12 +1784,12 @@ htt_print_sched_txq_cmd_posted_tlv_v(con + char sched_cmd_posted[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(sched_cmd_posted, htt_stats_buf->sched_cmd_posted, + num_elements); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_posted = %s\n", +- sched_cmd_posted); ++ len += scnprintf(buf + len, buf_len - len, "sched_cmd_posted = %s\n\n", ++ sched_cmd_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1815,12 +1811,12 @@ htt_print_sched_txq_cmd_reaped_tlv_v(con + char sched_cmd_reaped[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(sched_cmd_reaped, htt_stats_buf->sched_cmd_reaped, + num_elements); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_reaped = %s\n", +- sched_cmd_reaped); ++ len += scnprintf(buf + len, buf_len - len, "sched_cmd_reaped = %s\n\n", ++ sched_cmd_reaped); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1844,13 +1840,13 @@ htt_print_sched_txq_sched_order_su_tlv_v + u32 sched_order_su_num_entries = + min_t(u32, (tag_len >> 2), HTT_TX_PDEV_NUM_SCHED_ORDER_LOG); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(sched_order_su, htt_stats_buf->sched_order_su, + sched_order_su_num_entries); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_order_su = %s\n", +- sched_order_su); ++ len += scnprintf(buf + len, buf_len - len, "sched_order_su = %s\n\n", ++ sched_order_su); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1873,13 +1869,13 @@ htt_print_sched_txq_sched_ineligibility_ + /* each entry is u32, i.e. 4 bytes */ + u32 sched_ineligibility_num_entries = tag_len >> 2; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_SCHED_TXQ_SCHED_INELIGIBILITY_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_SCHED_TXQ_SCHED_INELIGIBILITY_V:\n"); + + PRINT_ARRAY_TO_BUF(sched_ineligibility, htt_stats_buf->sched_ineligibility, + sched_ineligibility_num_entries); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_ineligibility = %s\n", +- sched_ineligibility); ++ len += scnprintf(buf + len, buf_len - len, "sched_ineligibility = %s\n\n", ++ sched_ineligibility); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1898,54 +1894,54 @@ htt_print_tx_pdev_stats_sched_per_txq_tl + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__txq_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_id = %u", +- (htt_stats_buf->mac_id__txq_id__word & 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_policy = %u", +- htt_stats_buf->sched_policy); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "last_sched_cmd_posted_timestamp = %u", +- htt_stats_buf->last_sched_cmd_posted_timestamp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "last_sched_cmd_compl_timestamp = %u", +- htt_stats_buf->last_sched_cmd_compl_timestamp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_lwm_count = %u", +- htt_stats_buf->sched_2_tac_lwm_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_ring_full = %u", +- htt_stats_buf->sched_2_tac_ring_full); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_post_failure = %u", +- htt_stats_buf->sched_cmd_post_failure); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_active_tids = %u", +- htt_stats_buf->num_active_tids); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ps_schedules = %u", +- htt_stats_buf->num_ps_schedules); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmds_pending = %u", +- htt_stats_buf->sched_cmds_pending); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_register = %u", +- htt_stats_buf->num_tid_register); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_unregister = %u", +- htt_stats_buf->num_tid_unregister); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_qstats_queried = %u", +- htt_stats_buf->num_qstats_queried); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qstats_update_pending = %u", +- htt_stats_buf->qstats_update_pending); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_qstats_query_timestamp = %u", +- htt_stats_buf->last_qstats_query_timestamp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_cmdq_full = %u", +- htt_stats_buf->num_tqm_cmdq_full); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_de_sched_algo_trigger = %u", +- htt_stats_buf->num_de_sched_algo_trigger); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_rt_sched_algo_trigger = %u", +- htt_stats_buf->num_rt_sched_algo_trigger); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_sched_algo_trigger = %u", +- htt_stats_buf->num_tqm_sched_algo_trigger); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_sched = %u\n", +- htt_stats_buf->notify_sched); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dur_based_sendn_term = %u\n", +- htt_stats_buf->dur_based_sendn_term); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__txq_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "txq_id = %u\n", ++ (htt_stats_buf->mac_id__txq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "sched_policy = %u\n", ++ htt_stats_buf->sched_policy); ++ len += scnprintf(buf + len, buf_len - len, ++ "last_sched_cmd_posted_timestamp = %u\n", ++ htt_stats_buf->last_sched_cmd_posted_timestamp); ++ len += scnprintf(buf + len, buf_len - len, ++ "last_sched_cmd_compl_timestamp = %u\n", ++ htt_stats_buf->last_sched_cmd_compl_timestamp); ++ len += scnprintf(buf + len, buf_len - len, "sched_2_tac_lwm_count = %u\n", ++ htt_stats_buf->sched_2_tac_lwm_count); ++ len += scnprintf(buf + len, buf_len - len, "sched_2_tac_ring_full = %u\n", ++ htt_stats_buf->sched_2_tac_ring_full); ++ len += scnprintf(buf + len, buf_len - len, "sched_cmd_post_failure = %u\n", ++ htt_stats_buf->sched_cmd_post_failure); ++ len += scnprintf(buf + len, buf_len - len, "num_active_tids = %u\n", ++ htt_stats_buf->num_active_tids); ++ len += scnprintf(buf + len, buf_len - len, "num_ps_schedules = %u\n", ++ htt_stats_buf->num_ps_schedules); ++ len += scnprintf(buf + len, buf_len - len, "sched_cmds_pending = %u\n", ++ htt_stats_buf->sched_cmds_pending); ++ len += scnprintf(buf + len, buf_len - len, "num_tid_register = %u\n", ++ htt_stats_buf->num_tid_register); ++ len += scnprintf(buf + len, buf_len - len, "num_tid_unregister = %u\n", ++ htt_stats_buf->num_tid_unregister); ++ len += scnprintf(buf + len, buf_len - len, "num_qstats_queried = %u\n", ++ htt_stats_buf->num_qstats_queried); ++ len += scnprintf(buf + len, buf_len - len, "qstats_update_pending = %u\n", ++ htt_stats_buf->qstats_update_pending); ++ len += scnprintf(buf + len, buf_len - len, "last_qstats_query_timestamp = %u\n", ++ htt_stats_buf->last_qstats_query_timestamp); ++ len += scnprintf(buf + len, buf_len - len, "num_tqm_cmdq_full = %u\n", ++ htt_stats_buf->num_tqm_cmdq_full); ++ len += scnprintf(buf + len, buf_len - len, "num_de_sched_algo_trigger = %u\n", ++ htt_stats_buf->num_de_sched_algo_trigger); ++ len += scnprintf(buf + len, buf_len - len, "num_rt_sched_algo_trigger = %u\n", ++ htt_stats_buf->num_rt_sched_algo_trigger); ++ len += scnprintf(buf + len, buf_len - len, "num_tqm_sched_algo_trigger = %u\n", ++ htt_stats_buf->num_tqm_sched_algo_trigger); ++ len += scnprintf(buf + len, buf_len - len, "notify_sched = %u\n\n", ++ htt_stats_buf->notify_sched); ++ len += scnprintf(buf + len, buf_len - len, "dur_based_sendn_term = %u\n\n", ++ htt_stats_buf->dur_based_sendn_term); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1963,11 +1959,11 @@ static inline void htt_print_stats_tx_sc + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_TX_SCHED_CMN_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "current_timestamp = %u\n", +- htt_stats_buf->current_timestamp); ++ len += scnprintf(buf + len, buf_len - len, "HTT_STATS_TX_SCHED_CMN_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "current_timestamp = %u\n\n", ++ htt_stats_buf->current_timestamp); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1990,12 +1986,12 @@ htt_print_tx_tqm_gen_mpdu_stats_tlv_v(co + u16 num_elements = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(gen_mpdu_end_reason, htt_stats_buf->gen_mpdu_end_reason, + num_elements); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_end_reason = %s\n", +- gen_mpdu_end_reason); ++ len += scnprintf(buf + len, buf_len - len, "gen_mpdu_end_reason = %s\n\n", ++ gen_mpdu_end_reason); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2017,13 +2013,13 @@ htt_print_tx_tqm_list_mpdu_stats_tlv_v(c + char list_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_TQM_LIST_MPDU_STATS_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_TQM_LIST_MPDU_STATS_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(list_mpdu_end_reason, htt_stats_buf->list_mpdu_end_reason, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_end_reason = %s\n", +- list_mpdu_end_reason); ++ len += scnprintf(buf + len, buf_len - len, "list_mpdu_end_reason = %s\n\n", ++ list_mpdu_end_reason); + if (len >= buf_len) + buf[buf_len - 1] = 0; + else +@@ -2045,12 +2041,12 @@ htt_print_tx_tqm_list_mpdu_cnt_tlv_v(con + u16 num_elems = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(list_mpdu_cnt_hist, htt_stats_buf->list_mpdu_cnt_hist, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist = %s\n", +- list_mpdu_cnt_hist); ++ len += scnprintf(buf + len, buf_len - len, "list_mpdu_cnt_hist = %s\n\n", ++ list_mpdu_cnt_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2069,69 +2065,69 @@ htt_print_tx_tqm_pdev_stats_tlv_v(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_PDEV_STATS_TLV_V:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count = %u", +- htt_stats_buf->msdu_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count = %u", +- htt_stats_buf->mpdu_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu = %u", +- htt_stats_buf->remove_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu = %u", +- htt_stats_buf->remove_mpdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl = %u", +- htt_stats_buf->remove_msdu_ttl); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "send_bar = %u", +- htt_stats_buf->send_bar); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "bar_sync = %u", +- htt_stats_buf->bar_sync); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu = %u", +- htt_stats_buf->notify_mpdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", +- htt_stats_buf->sync_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", +- htt_stats_buf->write_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_trigger = %u", +- htt_stats_buf->hwsch_trigger); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", +- htt_stats_buf->ack_tlv_proc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", +- htt_stats_buf->gen_mpdu_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_list_cmd = %u", +- htt_stats_buf->gen_list_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", +- htt_stats_buf->remove_mpdu_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_tried_cmd = %u", +- htt_stats_buf->remove_mpdu_tried_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", +- htt_stats_buf->mpdu_queue_stats_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", +- htt_stats_buf->mpdu_head_info_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", +- htt_stats_buf->msdu_flow_stats_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", +- htt_stats_buf->remove_msdu_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl_cmd = %u", +- htt_stats_buf->remove_msdu_ttl_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", +- htt_stats_buf->flush_cache_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", +- htt_stats_buf->update_mpduq_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue = %u", +- htt_stats_buf->enqueue); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue_notify = %u", +- htt_stats_buf->enqueue_notify); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_at_head = %u", +- htt_stats_buf->notify_mpdu_at_head); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_state_valid = %u", +- htt_stats_buf->notify_mpdu_state_valid); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify1 = %u", +- htt_stats_buf->sched_udp_notify1); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify2 = %u", +- htt_stats_buf->sched_udp_notify2); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify1 = %u", +- htt_stats_buf->sched_nonudp_notify1); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify2 = %u\n", +- htt_stats_buf->sched_nonudp_notify2); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_PDEV_STATS_TLV_V:\n"); ++ len += scnprintf(buf + len, buf_len - len, "msdu_count = %u\n", ++ htt_stats_buf->msdu_count); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_count = %u\n", ++ htt_stats_buf->mpdu_count); ++ len += scnprintf(buf + len, buf_len - len, "remove_msdu = %u\n", ++ htt_stats_buf->remove_msdu); ++ len += scnprintf(buf + len, buf_len - len, "remove_mpdu = %u\n", ++ htt_stats_buf->remove_mpdu); ++ len += scnprintf(buf + len, buf_len - len, "remove_msdu_ttl = %u\n", ++ htt_stats_buf->remove_msdu_ttl); ++ len += scnprintf(buf + len, buf_len - len, "send_bar = %u\n", ++ htt_stats_buf->send_bar); ++ len += scnprintf(buf + len, buf_len - len, "bar_sync = %u\n", ++ htt_stats_buf->bar_sync); ++ len += scnprintf(buf + len, buf_len - len, "notify_mpdu = %u\n", ++ htt_stats_buf->notify_mpdu); ++ len += scnprintf(buf + len, buf_len - len, "sync_cmd = %u\n", ++ htt_stats_buf->sync_cmd); ++ len += scnprintf(buf + len, buf_len - len, "write_cmd = %u\n", ++ htt_stats_buf->write_cmd); ++ len += scnprintf(buf + len, buf_len - len, "hwsch_trigger = %u\n", ++ htt_stats_buf->hwsch_trigger); ++ len += scnprintf(buf + len, buf_len - len, "ack_tlv_proc = %u\n", ++ htt_stats_buf->ack_tlv_proc); ++ len += scnprintf(buf + len, buf_len - len, "gen_mpdu_cmd = %u\n", ++ htt_stats_buf->gen_mpdu_cmd); ++ len += scnprintf(buf + len, buf_len - len, "gen_list_cmd = %u\n", ++ htt_stats_buf->gen_list_cmd); ++ len += scnprintf(buf + len, buf_len - len, "remove_mpdu_cmd = %u\n", ++ htt_stats_buf->remove_mpdu_cmd); ++ len += scnprintf(buf + len, buf_len - len, "remove_mpdu_tried_cmd = %u\n", ++ htt_stats_buf->remove_mpdu_tried_cmd); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u\n", ++ htt_stats_buf->mpdu_queue_stats_cmd); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_head_info_cmd = %u\n", ++ htt_stats_buf->mpdu_head_info_cmd); ++ len += scnprintf(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u\n", ++ htt_stats_buf->msdu_flow_stats_cmd); ++ len += scnprintf(buf + len, buf_len - len, "remove_msdu_cmd = %u\n", ++ htt_stats_buf->remove_msdu_cmd); ++ len += scnprintf(buf + len, buf_len - len, "remove_msdu_ttl_cmd = %u\n", ++ htt_stats_buf->remove_msdu_ttl_cmd); ++ len += scnprintf(buf + len, buf_len - len, "flush_cache_cmd = %u\n", ++ htt_stats_buf->flush_cache_cmd); ++ len += scnprintf(buf + len, buf_len - len, "update_mpduq_cmd = %u\n", ++ htt_stats_buf->update_mpduq_cmd); ++ len += scnprintf(buf + len, buf_len - len, "enqueue = %u\n", ++ htt_stats_buf->enqueue); ++ len += scnprintf(buf + len, buf_len - len, "enqueue_notify = %u\n", ++ htt_stats_buf->enqueue_notify); ++ len += scnprintf(buf + len, buf_len - len, "notify_mpdu_at_head = %u\n", ++ htt_stats_buf->notify_mpdu_at_head); ++ len += scnprintf(buf + len, buf_len - len, "notify_mpdu_state_valid = %u\n", ++ htt_stats_buf->notify_mpdu_state_valid); ++ len += scnprintf(buf + len, buf_len - len, "sched_udp_notify1 = %u\n", ++ htt_stats_buf->sched_udp_notify1); ++ len += scnprintf(buf + len, buf_len - len, "sched_udp_notify2 = %u\n", ++ htt_stats_buf->sched_udp_notify2); ++ len += scnprintf(buf + len, buf_len - len, "sched_nonudp_notify1 = %u\n", ++ htt_stats_buf->sched_nonudp_notify1); ++ len += scnprintf(buf + len, buf_len - len, "sched_nonudp_notify2 = %u\n\n", ++ htt_stats_buf->sched_nonudp_notify2); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2149,23 +2145,23 @@ static inline void htt_print_tx_tqm_cmn_ + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_CMN_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "max_cmdq_id = %u", +- htt_stats_buf->max_cmdq_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u", +- htt_stats_buf->list_mpdu_cnt_hist_intvl); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu = %u", +- htt_stats_buf->add_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty = %u", +- htt_stats_buf->q_empty); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty = %u", +- htt_stats_buf->q_not_empty); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_notification = %u", +- htt_stats_buf->drop_notification); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "desc_threshold = %u\n", +- htt_stats_buf->desc_threshold); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_CMN_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "max_cmdq_id = %u\n", ++ htt_stats_buf->max_cmdq_id); ++ len += scnprintf(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u\n", ++ htt_stats_buf->list_mpdu_cnt_hist_intvl); ++ len += scnprintf(buf + len, buf_len - len, "add_msdu = %u\n", ++ htt_stats_buf->add_msdu); ++ len += scnprintf(buf + len, buf_len - len, "q_empty = %u\n", ++ htt_stats_buf->q_empty); ++ len += scnprintf(buf + len, buf_len - len, "q_not_empty = %u\n", ++ htt_stats_buf->q_not_empty); ++ len += scnprintf(buf + len, buf_len - len, "drop_notification = %u\n", ++ htt_stats_buf->drop_notification); ++ len += scnprintf(buf + len, buf_len - len, "desc_threshold = %u\n\n", ++ htt_stats_buf->desc_threshold); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2183,13 +2179,13 @@ static inline void htt_print_tx_tqm_erro + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_ERROR_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty_failure = %u", +- htt_stats_buf->q_empty_failure); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty_failure = %u", +- htt_stats_buf->q_not_empty_failure); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu_failure = %u\n", +- htt_stats_buf->add_msdu_failure); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_ERROR_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "q_empty_failure = %u\n", ++ htt_stats_buf->q_empty_failure); ++ len += scnprintf(buf + len, buf_len - len, "q_not_empty_failure = %u\n", ++ htt_stats_buf->q_not_empty_failure); ++ len += scnprintf(buf + len, buf_len - len, "add_msdu_failure = %u\n\n", ++ htt_stats_buf->add_msdu_failure); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2207,33 +2203,33 @@ static inline void htt_print_tx_tqm_cmdq + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_CMDQ_STATUS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__cmdq_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cmdq_id = %u\n", +- (htt_stats_buf->mac_id__cmdq_id__word & 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", +- htt_stats_buf->sync_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", +- htt_stats_buf->write_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", +- htt_stats_buf->gen_mpdu_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", +- htt_stats_buf->mpdu_queue_stats_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", +- htt_stats_buf->mpdu_head_info_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", +- htt_stats_buf->msdu_flow_stats_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", +- htt_stats_buf->remove_mpdu_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", +- htt_stats_buf->remove_msdu_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", +- htt_stats_buf->flush_cache_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", +- htt_stats_buf->update_mpduq_cmd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "update_msduq_cmd = %u\n", +- htt_stats_buf->update_msduq_cmd); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_CMDQ_STATUS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__cmdq_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "cmdq_id = %u\n\n", ++ (htt_stats_buf->mac_id__cmdq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "sync_cmd = %u\n", ++ htt_stats_buf->sync_cmd); ++ len += scnprintf(buf + len, buf_len - len, "write_cmd = %u\n", ++ htt_stats_buf->write_cmd); ++ len += scnprintf(buf + len, buf_len - len, "gen_mpdu_cmd = %u\n", ++ htt_stats_buf->gen_mpdu_cmd); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u\n", ++ htt_stats_buf->mpdu_queue_stats_cmd); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_head_info_cmd = %u\n", ++ htt_stats_buf->mpdu_head_info_cmd); ++ len += scnprintf(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u\n", ++ htt_stats_buf->msdu_flow_stats_cmd); ++ len += scnprintf(buf + len, buf_len - len, "remove_mpdu_cmd = %u\n", ++ htt_stats_buf->remove_mpdu_cmd); ++ len += scnprintf(buf + len, buf_len - len, "remove_msdu_cmd = %u\n", ++ htt_stats_buf->remove_msdu_cmd); ++ len += scnprintf(buf + len, buf_len - len, "flush_cache_cmd = %u\n", ++ htt_stats_buf->flush_cache_cmd); ++ len += scnprintf(buf + len, buf_len - len, "update_mpduq_cmd = %u\n", ++ htt_stats_buf->update_mpduq_cmd); ++ len += scnprintf(buf + len, buf_len - len, "update_msduq_cmd = %u\n\n", ++ htt_stats_buf->update_msduq_cmd); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2252,20 +2248,20 @@ htt_print_tx_de_eapol_packets_stats_tlv( + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_DE_EAPOL_PACKETS_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "m1_packets = %u", +- htt_stats_buf->m1_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "m2_packets = %u", +- htt_stats_buf->m2_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "m3_packets = %u", +- htt_stats_buf->m3_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "m4_packets = %u", +- htt_stats_buf->m4_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "g1_packets = %u", +- htt_stats_buf->g1_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "g2_packets = %u\n", +- htt_stats_buf->g2_packets); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_DE_EAPOL_PACKETS_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "m1_packets = %u\n", ++ htt_stats_buf->m1_packets); ++ len += scnprintf(buf + len, buf_len - len, "m2_packets = %u\n", ++ htt_stats_buf->m2_packets); ++ len += scnprintf(buf + len, buf_len - len, "m3_packets = %u\n", ++ htt_stats_buf->m3_packets); ++ len += scnprintf(buf + len, buf_len - len, "m4_packets = %u\n", ++ htt_stats_buf->m4_packets); ++ len += scnprintf(buf + len, buf_len - len, "g1_packets = %u\n", ++ htt_stats_buf->g1_packets); ++ len += scnprintf(buf + len, buf_len - len, "g2_packets = %u\n\n", ++ htt_stats_buf->g2_packets); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2284,34 +2280,34 @@ htt_print_tx_de_classify_failed_stats_tl + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_DE_CLASSIFY_FAILED_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bss_peer_not_found = %u", +- htt_stats_buf->ap_bss_peer_not_found); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bcast_mcast_no_peer = %u", +- htt_stats_buf->ap_bcast_mcast_no_peer); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sta_delete_in_progress = %u", +- htt_stats_buf->sta_delete_in_progress); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ibss_no_bss_peer = %u", +- htt_stats_buf->ibss_no_bss_peer); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_vdev_type = %u", +- htt_stats_buf->invalid_vdev_type); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_ast_peer_entry = %u", +- htt_stats_buf->invalid_ast_peer_entry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_entry_invalid = %u", +- htt_stats_buf->peer_entry_invalid); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ethertype_not_ip = %u", +- htt_stats_buf->ethertype_not_ip); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "eapol_lookup_failed = %u", +- htt_stats_buf->eapol_lookup_failed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_not_allow_data = %u", +- htt_stats_buf->qpeer_not_allow_data); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_tid_override = %u", +- htt_stats_buf->fse_tid_override); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ipv6_jumbogram_zero_length = %u", +- htt_stats_buf->ipv6_jumbogram_zero_length); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_to_non_qos_in_prog = %u\n", +- htt_stats_buf->qos_to_non_qos_in_prog); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_DE_CLASSIFY_FAILED_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "ap_bss_peer_not_found = %u\n", ++ htt_stats_buf->ap_bss_peer_not_found); ++ len += scnprintf(buf + len, buf_len - len, "ap_bcast_mcast_no_peer = %u\n", ++ htt_stats_buf->ap_bcast_mcast_no_peer); ++ len += scnprintf(buf + len, buf_len - len, "sta_delete_in_progress = %u\n", ++ htt_stats_buf->sta_delete_in_progress); ++ len += scnprintf(buf + len, buf_len - len, "ibss_no_bss_peer = %u\n", ++ htt_stats_buf->ibss_no_bss_peer); ++ len += scnprintf(buf + len, buf_len - len, "invalid_vdev_type = %u\n", ++ htt_stats_buf->invalid_vdev_type); ++ len += scnprintf(buf + len, buf_len - len, "invalid_ast_peer_entry = %u\n", ++ htt_stats_buf->invalid_ast_peer_entry); ++ len += scnprintf(buf + len, buf_len - len, "peer_entry_invalid = %u\n", ++ htt_stats_buf->peer_entry_invalid); ++ len += scnprintf(buf + len, buf_len - len, "ethertype_not_ip = %u\n", ++ htt_stats_buf->ethertype_not_ip); ++ len += scnprintf(buf + len, buf_len - len, "eapol_lookup_failed = %u\n", ++ htt_stats_buf->eapol_lookup_failed); ++ len += scnprintf(buf + len, buf_len - len, "qpeer_not_allow_data = %u\n", ++ htt_stats_buf->qpeer_not_allow_data); ++ len += scnprintf(buf + len, buf_len - len, "fse_tid_override = %u\n", ++ htt_stats_buf->fse_tid_override); ++ len += scnprintf(buf + len, buf_len - len, "ipv6_jumbogram_zero_length = %u\n", ++ htt_stats_buf->ipv6_jumbogram_zero_length); ++ len += scnprintf(buf + len, buf_len - len, "qos_to_non_qos_in_prog = %u\n\n", ++ htt_stats_buf->qos_to_non_qos_in_prog); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2330,73 +2326,73 @@ htt_print_tx_de_classify_stats_tlv(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_CLASSIFY_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "arp_packets = %u", +- htt_stats_buf->arp_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "igmp_packets = %u", +- htt_stats_buf->igmp_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dhcp_packets = %u", +- htt_stats_buf->dhcp_packets); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "host_inspected = %u", +- htt_stats_buf->host_inspected); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_included = %u", +- htt_stats_buf->htt_included); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_mcs = %u", +- htt_stats_buf->htt_valid_mcs); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_nss = %u", +- htt_stats_buf->htt_valid_nss); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_preamble_type = %u", +- htt_stats_buf->htt_valid_preamble_type); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_chainmask = %u", +- htt_stats_buf->htt_valid_chainmask); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_guard_interval = %u", +- htt_stats_buf->htt_valid_guard_interval); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_retries = %u", +- htt_stats_buf->htt_valid_retries); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_bw_info = %u", +- htt_stats_buf->htt_valid_bw_info); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_power = %u", +- htt_stats_buf->htt_valid_power); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_key_flags = 0x%x", +- htt_stats_buf->htt_valid_key_flags); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_no_encryption = %u", +- htt_stats_buf->htt_valid_no_encryption); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_entry_count = %u", +- htt_stats_buf->fse_entry_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_be = %u", +- htt_stats_buf->fse_priority_be); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_high = %u", +- htt_stats_buf->fse_priority_high); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_low = %u", +- htt_stats_buf->fse_priority_low); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_be = %u", +- htt_stats_buf->fse_traffic_ptrn_be); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_over_sub = %u", +- htt_stats_buf->fse_traffic_ptrn_over_sub); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_bursty = %u", +- htt_stats_buf->fse_traffic_ptrn_bursty); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_interactive = %u", +- htt_stats_buf->fse_traffic_ptrn_interactive); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_periodic = %u", +- htt_stats_buf->fse_traffic_ptrn_periodic); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_alloc = %u", +- htt_stats_buf->fse_hwqueue_alloc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_created = %u", +- htt_stats_buf->fse_hwqueue_created); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_send_to_host = %u", +- htt_stats_buf->fse_hwqueue_send_to_host); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mcast_entry = %u", +- htt_stats_buf->mcast_entry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "bcast_entry = %u", +- htt_stats_buf->bcast_entry); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_update_peer_cache = %u", +- htt_stats_buf->htt_update_peer_cache); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_learning_frame = %u", +- htt_stats_buf->htt_learning_frame); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_invalid_peer = %u", +- htt_stats_buf->fse_invalid_peer); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mec_notify = %u\n", +- htt_stats_buf->mec_notify); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_DE_CLASSIFY_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "arp_packets = %u\n", ++ htt_stats_buf->arp_packets); ++ len += scnprintf(buf + len, buf_len - len, "igmp_packets = %u\n", ++ htt_stats_buf->igmp_packets); ++ len += scnprintf(buf + len, buf_len - len, "dhcp_packets = %u\n", ++ htt_stats_buf->dhcp_packets); ++ len += scnprintf(buf + len, buf_len - len, "host_inspected = %u\n", ++ htt_stats_buf->host_inspected); ++ len += scnprintf(buf + len, buf_len - len, "htt_included = %u\n", ++ htt_stats_buf->htt_included); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_mcs = %u\n", ++ htt_stats_buf->htt_valid_mcs); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_nss = %u\n", ++ htt_stats_buf->htt_valid_nss); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_preamble_type = %u\n", ++ htt_stats_buf->htt_valid_preamble_type); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_chainmask = %u\n", ++ htt_stats_buf->htt_valid_chainmask); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_guard_interval = %u\n", ++ htt_stats_buf->htt_valid_guard_interval); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_retries = %u\n", ++ htt_stats_buf->htt_valid_retries); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_bw_info = %u\n", ++ htt_stats_buf->htt_valid_bw_info); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_power = %u\n", ++ htt_stats_buf->htt_valid_power); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_key_flags = 0x%x\n", ++ htt_stats_buf->htt_valid_key_flags); ++ len += scnprintf(buf + len, buf_len - len, "htt_valid_no_encryption = %u\n", ++ htt_stats_buf->htt_valid_no_encryption); ++ len += scnprintf(buf + len, buf_len - len, "fse_entry_count = %u\n", ++ htt_stats_buf->fse_entry_count); ++ len += scnprintf(buf + len, buf_len - len, "fse_priority_be = %u\n", ++ htt_stats_buf->fse_priority_be); ++ len += scnprintf(buf + len, buf_len - len, "fse_priority_high = %u\n", ++ htt_stats_buf->fse_priority_high); ++ len += scnprintf(buf + len, buf_len - len, "fse_priority_low = %u\n", ++ htt_stats_buf->fse_priority_low); ++ len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_be = %u\n", ++ htt_stats_buf->fse_traffic_ptrn_be); ++ len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_over_sub = %u\n", ++ htt_stats_buf->fse_traffic_ptrn_over_sub); ++ len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_bursty = %u\n", ++ htt_stats_buf->fse_traffic_ptrn_bursty); ++ len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_interactive = %u\n", ++ htt_stats_buf->fse_traffic_ptrn_interactive); ++ len += scnprintf(buf + len, buf_len - len, "fse_traffic_ptrn_periodic = %u\n", ++ htt_stats_buf->fse_traffic_ptrn_periodic); ++ len += scnprintf(buf + len, buf_len - len, "fse_hwqueue_alloc = %u\n", ++ htt_stats_buf->fse_hwqueue_alloc); ++ len += scnprintf(buf + len, buf_len - len, "fse_hwqueue_created = %u\n", ++ htt_stats_buf->fse_hwqueue_created); ++ len += scnprintf(buf + len, buf_len - len, "fse_hwqueue_send_to_host = %u\n", ++ htt_stats_buf->fse_hwqueue_send_to_host); ++ len += scnprintf(buf + len, buf_len - len, "mcast_entry = %u\n", ++ htt_stats_buf->mcast_entry); ++ len += scnprintf(buf + len, buf_len - len, "bcast_entry = %u\n", ++ htt_stats_buf->bcast_entry); ++ len += scnprintf(buf + len, buf_len - len, "htt_update_peer_cache = %u\n", ++ htt_stats_buf->htt_update_peer_cache); ++ len += scnprintf(buf + len, buf_len - len, "htt_learning_frame = %u\n", ++ htt_stats_buf->htt_learning_frame); ++ len += scnprintf(buf + len, buf_len - len, "fse_invalid_peer = %u\n", ++ htt_stats_buf->fse_invalid_peer); ++ len += scnprintf(buf + len, buf_len - len, "mec_notify = %u\n\n", ++ htt_stats_buf->mec_notify); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2415,24 +2411,24 @@ htt_print_tx_de_classify_status_stats_tl + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_DE_CLASSIFY_STATUS_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "eok = %u", +- htt_stats_buf->eok); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "classify_done = %u", +- htt_stats_buf->classify_done); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "lookup_failed = %u", +- htt_stats_buf->lookup_failed); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_dhcp = %u", +- htt_stats_buf->send_host_dhcp); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_mcast = %u", +- htt_stats_buf->send_host_mcast); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_unknown_dest = %u", +- htt_stats_buf->send_host_unknown_dest); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host = %u", +- htt_stats_buf->send_host); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "status_invalid = %u\n", +- htt_stats_buf->status_invalid); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_DE_CLASSIFY_STATUS_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "eok = %u\n", ++ htt_stats_buf->eok); ++ len += scnprintf(buf + len, buf_len - len, "classify_done = %u\n", ++ htt_stats_buf->classify_done); ++ len += scnprintf(buf + len, buf_len - len, "lookup_failed = %u\n", ++ htt_stats_buf->lookup_failed); ++ len += scnprintf(buf + len, buf_len - len, "send_host_dhcp = %u\n", ++ htt_stats_buf->send_host_dhcp); ++ len += scnprintf(buf + len, buf_len - len, "send_host_mcast = %u\n", ++ htt_stats_buf->send_host_mcast); ++ len += scnprintf(buf + len, buf_len - len, "send_host_unknown_dest = %u\n", ++ htt_stats_buf->send_host_unknown_dest); ++ len += scnprintf(buf + len, buf_len - len, "send_host = %u\n", ++ htt_stats_buf->send_host); ++ len += scnprintf(buf + len, buf_len - len, "status_invalid = %u\n\n", ++ htt_stats_buf->status_invalid); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2451,14 +2447,14 @@ htt_print_tx_de_enqueue_packets_stats_tl + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_DE_ENQUEUE_PACKETS_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_pkts = %u", +- htt_stats_buf->enqueued_pkts); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm = %u", +- htt_stats_buf->to_tqm); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm_bypass = %u\n", +- htt_stats_buf->to_tqm_bypass); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_DE_ENQUEUE_PACKETS_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "enqueued_pkts = %u\n", ++ htt_stats_buf->enqueued_pkts); ++ len += scnprintf(buf + len, buf_len - len, "to_tqm = %u\n", ++ htt_stats_buf->to_tqm); ++ len += scnprintf(buf + len, buf_len - len, "to_tqm_bypass = %u\n\n", ++ htt_stats_buf->to_tqm_bypass); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2477,14 +2473,14 @@ htt_print_tx_de_enqueue_discard_stats_tl + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_DE_ENQUEUE_DISCARD_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "discarded_pkts = %u", +- htt_stats_buf->discarded_pkts); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "local_frames = %u", +- htt_stats_buf->local_frames); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "is_ext_msdu = %u\n", +- htt_stats_buf->is_ext_msdu); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_DE_ENQUEUE_DISCARD_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "discarded_pkts = %u\n", ++ htt_stats_buf->discarded_pkts); ++ len += scnprintf(buf + len, buf_len - len, "local_frames = %u\n", ++ htt_stats_buf->local_frames); ++ len += scnprintf(buf + len, buf_len - len, "is_ext_msdu = %u\n\n", ++ htt_stats_buf->is_ext_msdu); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2502,17 +2498,17 @@ static inline void htt_print_tx_de_compl + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_COMPL_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_dummy_frame = %u", +- htt_stats_buf->tcl_dummy_frame); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_dummy_frame = %u", +- htt_stats_buf->tqm_dummy_frame); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_notify_frame = %u", +- htt_stats_buf->tqm_notify_frame); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw2wbm_enq = %u", +- htt_stats_buf->fw2wbm_enq); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_bypass_frame = %u\n", +- htt_stats_buf->tqm_bypass_frame); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_DE_COMPL_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "tcl_dummy_frame = %u\n", ++ htt_stats_buf->tcl_dummy_frame); ++ len += scnprintf(buf + len, buf_len - len, "tqm_dummy_frame = %u\n", ++ htt_stats_buf->tqm_dummy_frame); ++ len += scnprintf(buf + len, buf_len - len, "tqm_notify_frame = %u\n", ++ htt_stats_buf->tqm_notify_frame); ++ len += scnprintf(buf + len, buf_len - len, "fw2wbm_enq = %u\n", ++ htt_stats_buf->fw2wbm_enq); ++ len += scnprintf(buf + len, buf_len - len, "tqm_bypass_frame = %u\n\n", ++ htt_stats_buf->tqm_bypass_frame); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2535,19 +2531,19 @@ htt_print_tx_de_fw2wbm_ring_full_hist_tl + u16 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_TX_DE_FW2WBM_RING_FULL_HIST_TLV"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TX_DE_FW2WBM_RING_FULL_HIST_TLV"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + PRINT_ARRAY_TO_BUF(fw2wbm_ring_full_hist, + htt_stats_buf->fw2wbm_ring_full_hist, + num_elements); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "fw2wbm_ring_full_hist = %s\n", +- fw2wbm_ring_full_hist); ++ len += scnprintf(buf + len, buf_len - len, ++ "fw2wbm_ring_full_hist = %s\n\n", ++ fw2wbm_ring_full_hist); + } else { +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER "); ++ len += scnprintf(buf + len, buf_len - len, ++ "INSUFFICIENT PRINT BUFFER\n"); + } + + if (len >= buf_len) +@@ -2566,21 +2562,21 @@ htt_print_tx_de_cmn_stats_tlv(const void + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_CMN_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl2fw_entry_count = %u", +- htt_stats_buf->tcl2fw_entry_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "not_to_fw = %u", +- htt_stats_buf->not_to_fw); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev_vdev_peer = %u", +- htt_stats_buf->invalid_pdev_vdev_peer); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_res_invalid_addrx = %u", +- htt_stats_buf->tcl_res_invalid_addrx); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm2fw_entry_count = %u", +- htt_stats_buf->wbm2fw_entry_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev = %u\n", +- htt_stats_buf->invalid_pdev); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_DE_CMN_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "tcl2fw_entry_count = %u\n", ++ htt_stats_buf->tcl2fw_entry_count); ++ len += scnprintf(buf + len, buf_len - len, "not_to_fw = %u\n", ++ htt_stats_buf->not_to_fw); ++ len += scnprintf(buf + len, buf_len - len, "invalid_pdev_vdev_peer = %u\n", ++ htt_stats_buf->invalid_pdev_vdev_peer); ++ len += scnprintf(buf + len, buf_len - len, "tcl_res_invalid_addrx = %u\n", ++ htt_stats_buf->tcl_res_invalid_addrx); ++ len += scnprintf(buf + len, buf_len - len, "wbm2fw_entry_count = %u\n", ++ htt_stats_buf->wbm2fw_entry_count); ++ len += scnprintf(buf + len, buf_len - len, "invalid_pdev = %u\n\n", ++ htt_stats_buf->invalid_pdev); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2600,49 +2596,49 @@ static inline void htt_print_ring_if_sta + char low_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + char high_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RING_IF_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr = %u", +- htt_stats_buf->base_addr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", +- htt_stats_buf->elem_size); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_elems = %u", +- htt_stats_buf->num_elems__prefetch_tail_idx & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_tail_idx = %u", +- (htt_stats_buf->num_elems__prefetch_tail_idx & +- 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "head_idx = %u", +- htt_stats_buf->head_idx__tail_idx & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_idx = %u", +- (htt_stats_buf->head_idx__tail_idx & 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_head_idx = %u", +- htt_stats_buf->shadow_head_idx__shadow_tail_idx & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_tail_idx = %u", +- (htt_stats_buf->shadow_head_idx__shadow_tail_idx & +- 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tail_incr = %u", +- htt_stats_buf->num_tail_incr); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "lwm_thresh = %u", +- htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwm_thresh = %u", +- (htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "overrun_hit_count = %u", +- htt_stats_buf->overrun_hit_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_hit_count = %u", +- htt_stats_buf->underrun_hit_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "prod_blockwait_count = %u", +- htt_stats_buf->prod_blockwait_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "cons_blockwait_count = %u", +- htt_stats_buf->cons_blockwait_count); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RING_IF_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "base_addr = %u\n", ++ htt_stats_buf->base_addr); ++ len += scnprintf(buf + len, buf_len - len, "elem_size = %u\n", ++ htt_stats_buf->elem_size); ++ len += scnprintf(buf + len, buf_len - len, "num_elems = %u\n", ++ htt_stats_buf->num_elems__prefetch_tail_idx & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "prefetch_tail_idx = %u\n", ++ (htt_stats_buf->num_elems__prefetch_tail_idx & ++ 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "head_idx = %u\n", ++ htt_stats_buf->head_idx__tail_idx & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "tail_idx = %u\n", ++ (htt_stats_buf->head_idx__tail_idx & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "shadow_head_idx = %u\n", ++ htt_stats_buf->shadow_head_idx__shadow_tail_idx & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "shadow_tail_idx = %u\n", ++ (htt_stats_buf->shadow_head_idx__shadow_tail_idx & ++ 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "num_tail_incr = %u\n", ++ htt_stats_buf->num_tail_incr); ++ len += scnprintf(buf + len, buf_len - len, "lwm_thresh = %u\n", ++ htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "hwm_thresh = %u\n", ++ (htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "overrun_hit_count = %u\n", ++ htt_stats_buf->overrun_hit_count); ++ len += scnprintf(buf + len, buf_len - len, "underrun_hit_count = %u\n", ++ htt_stats_buf->underrun_hit_count); ++ len += scnprintf(buf + len, buf_len - len, "prod_blockwait_count = %u\n", ++ htt_stats_buf->prod_blockwait_count); ++ len += scnprintf(buf + len, buf_len - len, "cons_blockwait_count = %u\n", ++ htt_stats_buf->cons_blockwait_count); + + PRINT_ARRAY_TO_BUF(low_wm_hit_count, htt_stats_buf->low_wm_hit_count, + HTT_STATS_LOW_WM_BINS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "low_wm_hit_count = %s ", +- low_wm_hit_count); ++ len += scnprintf(buf + len, buf_len - len, "low_wm_hit_count = %s\n", ++ low_wm_hit_count); + + PRINT_ARRAY_TO_BUF(high_wm_hit_count, htt_stats_buf->high_wm_hit_count, + HTT_STATS_HIGH_WM_BINS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "high_wm_hit_count = %s\n", +- high_wm_hit_count); ++ len += scnprintf(buf + len, buf_len - len, "high_wm_hit_count = %s\n\n", ++ high_wm_hit_count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2660,11 +2656,11 @@ static inline void htt_print_ring_if_cmn + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RING_IF_CMN_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", +- htt_stats_buf->num_records); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RING_IF_CMN_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "num_records = %u\n\n", ++ htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2685,13 +2681,13 @@ static inline void htt_print_sfm_client_ + char dwords_used_by_user_n[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = tag_len >> 2; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(dwords_used_by_user_n, + htt_stats_buf->dwords_used_by_user_n, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dwords_used_by_user_n = %s\n", +- dwords_used_by_user_n); ++ len += scnprintf(buf + len, buf_len - len, "dwords_used_by_user_n = %s\n\n", ++ dwords_used_by_user_n); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2709,21 +2705,21 @@ static inline void htt_print_sfm_client_ + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "client_id = %u", +- htt_stats_buf->client_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_min = %u", +- htt_stats_buf->buf_min); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_max = %u", +- htt_stats_buf->buf_max); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_busy = %u", +- htt_stats_buf->buf_busy); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_alloc = %u", +- htt_stats_buf->buf_alloc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_avail = %u", +- htt_stats_buf->buf_avail); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_users = %u\n", +- htt_stats_buf->num_users); ++ len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CLIENT_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "client_id = %u\n", ++ htt_stats_buf->client_id); ++ len += scnprintf(buf + len, buf_len - len, "buf_min = %u\n", ++ htt_stats_buf->buf_min); ++ len += scnprintf(buf + len, buf_len - len, "buf_max = %u\n", ++ htt_stats_buf->buf_max); ++ len += scnprintf(buf + len, buf_len - len, "buf_busy = %u\n", ++ htt_stats_buf->buf_busy); ++ len += scnprintf(buf + len, buf_len - len, "buf_alloc = %u\n", ++ htt_stats_buf->buf_alloc); ++ len += scnprintf(buf + len, buf_len - len, "buf_avail = %u\n", ++ htt_stats_buf->buf_avail); ++ len += scnprintf(buf + len, buf_len - len, "num_users = %u\n\n", ++ htt_stats_buf->num_users); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2741,17 +2737,17 @@ static inline void htt_print_sfm_cmn_tlv + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CMN_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_total = %u", +- htt_stats_buf->buf_total); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mem_empty = %u", +- htt_stats_buf->mem_empty); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "deallocate_bufs = %u", +- htt_stats_buf->deallocate_bufs); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", +- htt_stats_buf->num_records); ++ len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CMN_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "buf_total = %u\n", ++ htt_stats_buf->buf_total); ++ len += scnprintf(buf + len, buf_len - len, "mem_empty = %u\n", ++ htt_stats_buf->mem_empty); ++ len += scnprintf(buf + len, buf_len - len, "deallocate_bufs = %u\n", ++ htt_stats_buf->deallocate_bufs); ++ len += scnprintf(buf + len, buf_len - len, "num_records = %u\n\n", ++ htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2769,42 +2765,42 @@ static inline void htt_print_sring_stats + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SRING_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_id = %u", +- (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "arena = %u", +- (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ep = %u", +- (htt_stats_buf->mac_id__ring_id__arena__ep & 0x1000000) >> 24); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_lsb = 0x%x", +- htt_stats_buf->base_addr_lsb); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_msb = 0x%x", +- htt_stats_buf->base_addr_msb); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_size = %u", +- htt_stats_buf->ring_size); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", +- htt_stats_buf->elem_size); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_avail_words = %u", +- htt_stats_buf->num_avail_words__num_valid_words & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_valid_words = %u", +- (htt_stats_buf->num_avail_words__num_valid_words & +- 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "head_ptr = %u", +- htt_stats_buf->head_ptr__tail_ptr & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_ptr = %u", +- (htt_stats_buf->head_ptr__tail_ptr & 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "consumer_empty = %u", +- htt_stats_buf->consumer_empty__producer_full & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "producer_full = %u", +- (htt_stats_buf->consumer_empty__producer_full & +- 0xFFFF0000) >> 16); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_count = %u", +- htt_stats_buf->prefetch_count__internal_tail_ptr & 0xFFFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "internal_tail_ptr = %u\n", +- (htt_stats_buf->prefetch_count__internal_tail_ptr & +- 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "HTT_SRING_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "ring_id = %u\n", ++ (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "arena = %u\n", ++ (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "ep = %u\n", ++ (htt_stats_buf->mac_id__ring_id__arena__ep & 0x1000000) >> 24); ++ len += scnprintf(buf + len, buf_len - len, "base_addr_lsb = 0x%x\n", ++ htt_stats_buf->base_addr_lsb); ++ len += scnprintf(buf + len, buf_len - len, "base_addr_msb = 0x%x\n", ++ htt_stats_buf->base_addr_msb); ++ len += scnprintf(buf + len, buf_len - len, "ring_size = %u\n", ++ htt_stats_buf->ring_size); ++ len += scnprintf(buf + len, buf_len - len, "elem_size = %u\n", ++ htt_stats_buf->elem_size); ++ len += scnprintf(buf + len, buf_len - len, "num_avail_words = %u\n", ++ htt_stats_buf->num_avail_words__num_valid_words & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "num_valid_words = %u\n", ++ (htt_stats_buf->num_avail_words__num_valid_words & ++ 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "head_ptr = %u\n", ++ htt_stats_buf->head_ptr__tail_ptr & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "tail_ptr = %u\n", ++ (htt_stats_buf->head_ptr__tail_ptr & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "consumer_empty = %u\n", ++ htt_stats_buf->consumer_empty__producer_full & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "producer_full = %u\n", ++ (htt_stats_buf->consumer_empty__producer_full & ++ 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "prefetch_count = %u\n", ++ htt_stats_buf->prefetch_count__internal_tail_ptr & 0xFFFF); ++ len += scnprintf(buf + len, buf_len - len, "internal_tail_ptr = %u\n\n", ++ (htt_stats_buf->prefetch_count__internal_tail_ptr & ++ 0xFFFF0000) >> 16); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2822,9 +2818,9 @@ static inline void htt_print_sring_cmn_t + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SRING_CMN_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", +- htt_stats_buf->num_records); ++ len += scnprintf(buf + len, buf_len - len, "HTT_SRING_CMN_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "num_records = %u\n\n", ++ htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2851,156 +2847,156 @@ static inline void htt_print_tx_pdev_rat + goto fail; + } + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", +- htt_stats_buf->tx_ldpc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u", +- htt_stats_buf->ac_mu_mimo_tx_ldpc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u", +- htt_stats_buf->ax_mu_mimo_tx_ldpc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_ldpc = %u", +- htt_stats_buf->ofdma_tx_ldpc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", +- htt_stats_buf->rts_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_success = %u", +- htt_stats_buf->rts_success); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", +- htt_stats_buf->ack_rssi); +- +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 11 Mbps: %u", +- htt_stats_buf->tx_legacy_cck_rate[0], +- htt_stats_buf->tx_legacy_cck_rate[1], +- htt_stats_buf->tx_legacy_cck_rate[2], +- htt_stats_buf->tx_legacy_cck_rate[3]); +- +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" +- " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u", +- htt_stats_buf->tx_legacy_ofdm_rate[0], +- htt_stats_buf->tx_legacy_ofdm_rate[1], +- htt_stats_buf->tx_legacy_ofdm_rate[2], +- htt_stats_buf->tx_legacy_ofdm_rate[3], +- htt_stats_buf->tx_legacy_ofdm_rate[4], +- htt_stats_buf->tx_legacy_ofdm_rate[5], +- htt_stats_buf->tx_legacy_ofdm_rate[6], +- htt_stats_buf->tx_legacy_ofdm_rate[7]); ++ len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n", ++ htt_stats_buf->tx_ldpc); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u\n", ++ htt_stats_buf->ac_mu_mimo_tx_ldpc); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u\n", ++ htt_stats_buf->ax_mu_mimo_tx_ldpc); ++ len += scnprintf(buf + len, buf_len - len, "ofdma_tx_ldpc = %u\n", ++ htt_stats_buf->ofdma_tx_ldpc); ++ len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", ++ htt_stats_buf->rts_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rts_success = %u\n", ++ htt_stats_buf->rts_success); ++ len += scnprintf(buf + len, buf_len - len, "ack_rssi = %u\n", ++ htt_stats_buf->ack_rssi); ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 11 Mbps: %u\n", ++ htt_stats_buf->tx_legacy_cck_rate[0], ++ htt_stats_buf->tx_legacy_cck_rate[1], ++ htt_stats_buf->tx_legacy_cck_rate[2], ++ htt_stats_buf->tx_legacy_cck_rate[3]); ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" ++ " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u\n", ++ htt_stats_buf->tx_legacy_ofdm_rate[0], ++ htt_stats_buf->tx_legacy_ofdm_rate[1], ++ htt_stats_buf->tx_legacy_ofdm_rate[2], ++ htt_stats_buf->tx_legacy_ofdm_rate[3], ++ htt_stats_buf->tx_legacy_ofdm_rate[4], ++ htt_stats_buf->tx_legacy_ofdm_rate[5], ++ htt_stats_buf->tx_legacy_ofdm_rate[6], ++ htt_stats_buf->tx_legacy_ofdm_rate[7]); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ofdma_tx_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ofdma_tx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_bw = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_bw = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_bw = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_bw = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_bw = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_bw = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ofdma_tx_bw = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_stbc = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_pream = %s\n", str_buf); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u", +- htt_stats_buf->tx_he_ltf[1], +- htt_stats_buf->tx_he_ltf[2], +- htt_stats_buf->tx_he_ltf[3]); ++ len += scnprintf(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u\n", ++ htt_stats_buf->tx_he_ltf[1], ++ htt_stats_buf->tx_he_ltf[2], ++ htt_stats_buf->tx_he_ltf[3]); + + /* SU GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, "tx_gi[%u] = %s\n", ++ j, tx_gi[j]); + } + + /* AC MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ac_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ac_mu_mimo_tx_gi[%u] = %s ", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_tx_gi[%u] = %s\n", ++ j, tx_gi[j]); + } + + /* AX MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ax_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ax_mu_mimo_tx_gi[%u] = %s ", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_tx_gi[%u] = %s\n", ++ j, tx_gi[j]); + } + + /* DL OFDMA GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ofdma_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_gi[%u] = %s ", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, "ofdma_tx_gi[%u] = %s\n", ++ j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "tx_dcm = %s\n\n", str_buf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3045,202 +3041,202 @@ static inline void htt_print_rx_pdev_rat + goto fail; + } + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", +- htt_stats_buf->nsts); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", +- htt_stats_buf->rx_ldpc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", +- htt_stats_buf->rts_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", +- htt_stats_buf->rssi_mgmt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", +- htt_stats_buf->rssi_data); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", +- htt_stats_buf->rssi_comb); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_in_dbm = %d", +- htt_stats_buf->rssi_in_dbm); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "nsts = %u\n", ++ htt_stats_buf->nsts); ++ len += scnprintf(buf + len, buf_len - len, "rx_ldpc = %u\n", ++ htt_stats_buf->rx_ldpc); ++ len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", ++ htt_stats_buf->rts_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rssi_mgmt = %u\n", ++ htt_stats_buf->rssi_mgmt); ++ len += scnprintf(buf + len, buf_len - len, "rssi_data = %u\n", ++ htt_stats_buf->rssi_data); ++ len += scnprintf(buf + len, buf_len - len, "rssi_comb = %u\n", ++ htt_stats_buf->rssi_comb); ++ len += scnprintf(buf + len, buf_len - len, "rssi_in_dbm = %d\n", ++ htt_stats_buf->rssi_in_dbm); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_mcs = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_dcm = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_stbc = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_evm_nss_count = %u", +- htt_stats_buf->nss_count); ++ len += scnprintf(buf + len, buf_len - len, "rx_bw = %s\n", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_evm_nss_count = %u\n", ++ htt_stats_buf->nss_count); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_evm_pilot_count = %u", +- htt_stats_buf->pilot_count); ++ len += scnprintf(buf + len, buf_len - len, "rx_evm_pilot_count = %u\n", ++ htt_stats_buf->pilot_count); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + index = 0; + + for (i = 0; i < HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS; i++) + index += scnprintf(&rx_pilot_evm_db[j][index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", +- i, +- htt_stats_buf->rx_pilot_evm_db[j][i]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pilot_evm_dB[%u] = %s ", +- j, rx_pilot_evm_db[j]); ++ HTT_MAX_STRING_LEN - index, ++ " %u:%d,", ++ i, ++ htt_stats_buf->rx_pilot_evm_db[j][i]); ++ len += scnprintf(buf + len, buf_len - len, "pilot_evm_dB[%u] = %s\n", ++ j, rx_pilot_evm_db[j]); + } + + index = 0; + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + for (i = 0; i < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) + index += scnprintf(&str_buf[index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", i, htt_stats_buf->rx_pilot_evm_db_mean[i]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pilot_evm_dB_mean = %s ", str_buf); ++ HTT_MAX_STRING_LEN - index, ++ " %u:%d,", i, htt_stats_buf->rx_pilot_evm_db_mean[i]); ++ len += scnprintf(buf + len, buf_len - len, "pilot_evm_dB_mean = %s\n", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + PRINT_ARRAY_TO_BUF(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", +- j, rssi_chain[j]); ++ len += scnprintf(buf + len, buf_len - len, "rssi_chain[%u] = %s\n", ++ j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", +- j, rx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, "rx_gi[%u] = %s\n", ++ j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_pream = %s\n", str_buf); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_su_ext = %u", +- htt_stats_buf->rx_11ax_su_ext); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ac_mumimo = %u", +- htt_stats_buf->rx_11ac_mumimo); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_mumimo = %u", +- htt_stats_buf->rx_11ax_mumimo); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_ofdma = %u", +- htt_stats_buf->rx_11ax_ofdma); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "txbf = %u", +- htt_stats_buf->txbf); ++ len += scnprintf(buf + len, buf_len - len, "rx_11ax_su_ext = %u\n", ++ htt_stats_buf->rx_11ax_su_ext); ++ len += scnprintf(buf + len, buf_len - len, "rx_11ac_mumimo = %u\n", ++ htt_stats_buf->rx_11ac_mumimo); ++ len += scnprintf(buf + len, buf_len - len, "rx_11ax_mumimo = %u\n", ++ htt_stats_buf->rx_11ax_mumimo); ++ len += scnprintf(buf + len, buf_len - len, "rx_11ax_ofdma = %u\n", ++ htt_stats_buf->rx_11ax_ofdma); ++ len += scnprintf(buf + len, buf_len - len, "txbf = %u\n", ++ htt_stats_buf->txbf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_legacy_cck_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_cck_rate = %s ", +- str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_legacy_cck_rate = %s\n", ++ str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_legacy_ofdm_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_ofdm_rate = %s ", +- str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_legacy_ofdm_rate = %s\n", ++ str_buf); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_low = %u", +- htt_stats_buf->rx_active_dur_us_low); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_high = %u", +- htt_stats_buf->rx_active_dur_us_high); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u", +- htt_stats_buf->rx_11ax_ul_ofdma); ++ len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_low = %u\n", ++ htt_stats_buf->rx_active_dur_us_low); ++ len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_high = %u\n", ++ htt_stats_buf->rx_active_dur_us_high); ++ len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", ++ htt_stats_buf->rx_11ax_ul_ofdma); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_mcs = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_mcs = %s\n", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->ul_ofdma_rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u] = %s ", +- j, rx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u] = %s\n", ++ j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_nss = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_nss = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_bw = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_bw = %s\n", str_buf); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u", ++ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + htt_stats_buf->ul_ofdma_rx_stbc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u", ++ len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + htt_stats_buf->ul_ofdma_rx_ldpc); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_non_data_ppdu, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu = %s ", +- str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu = %s\n", ++ str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_data_ppdu, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_data_ppdu = %s ", ++ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ppdu = %s\n", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_mpdu_ok, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok = %s ", str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok = %s\n", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_mpdu_fail, + HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail = %s", +- str_buf); ++ len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail = %s\n", ++ str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + index = 0; + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + for (i = 0; i < HTT_RX_PDEV_MAX_OFDMA_NUM_USER; i++) + index += scnprintf(&str_buf[index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", +- i, htt_stats_buf->rx_ul_fd_rssi[j][i]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "rx_ul_fd_rssi: nss[%u] = %s", j, str_buf); ++ HTT_MAX_STRING_LEN - index, ++ " %u:%d,", ++ i, htt_stats_buf->rx_ul_fd_rssi[j][i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "rx_ul_fd_rssi: nss[%u] = %s\n", j, str_buf); + } + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x", +- htt_stats_buf->per_chain_rssi_pkt_type); ++ len += scnprintf(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x\n", ++ htt_stats_buf->per_chain_rssi_pkt_type); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + index = 0; + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + for (i = 0; i < HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) + index += scnprintf(&str_buf[index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", +- i, +- htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "rx_per_chain_rssi_in_dbm[%u] = %s ", j, str_buf); ++ HTT_MAX_STRING_LEN - index, ++ " %u:%d,", ++ i, ++ htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "rx_per_chain_rssi_in_dbm[%u] = %s\n", j, str_buf); + } +- len += HTT_DBG_OUT(buf + len, buf_len - len, "\n"); ++ len += scnprintf(buf + len, buf_len - len, "\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3268,34 +3264,34 @@ static inline void htt_print_rx_soc_fw_s + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_SOC_FW_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_reo_ring_data_msdu = %u", +- htt_stats_buf->fw_reo_ring_data_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_bcmc = %u", +- htt_stats_buf->fw_to_host_data_msdu_bcmc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_uc = %u", +- htt_stats_buf->fw_to_host_data_msdu_uc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ofld_remote_data_buf_recycle_cnt = %u", +- htt_stats_buf->ofld_remote_data_buf_recycle_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ofld_remote_free_buf_indication_cnt = %u", +- htt_stats_buf->ofld_remote_free_buf_indication_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ofld_buf_to_host_data_msdu_uc = %u", +- htt_stats_buf->ofld_buf_to_host_data_msdu_uc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "reo_fw_ring_to_host_data_msdu_uc = %u", +- htt_stats_buf->reo_fw_ring_to_host_data_msdu_uc); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_sw_ring_reap = %u", +- htt_stats_buf->wbm_sw_ring_reap); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_forward_to_host_cnt = %u", +- htt_stats_buf->wbm_forward_to_host_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_target_recycle_cnt = %u", +- htt_stats_buf->wbm_target_recycle_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "target_refill_ring_recycle_cnt = %u", +- htt_stats_buf->target_refill_ring_recycle_cnt); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_SOC_FW_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "fw_reo_ring_data_msdu = %u\n", ++ htt_stats_buf->fw_reo_ring_data_msdu); ++ len += scnprintf(buf + len, buf_len - len, "fw_to_host_data_msdu_bcmc = %u\n", ++ htt_stats_buf->fw_to_host_data_msdu_bcmc); ++ len += scnprintf(buf + len, buf_len - len, "fw_to_host_data_msdu_uc = %u\n", ++ htt_stats_buf->fw_to_host_data_msdu_uc); ++ len += scnprintf(buf + len, buf_len - len, ++ "ofld_remote_data_buf_recycle_cnt = %u\n", ++ htt_stats_buf->ofld_remote_data_buf_recycle_cnt); ++ len += scnprintf(buf + len, buf_len - len, ++ "ofld_remote_free_buf_indication_cnt = %u\n", ++ htt_stats_buf->ofld_remote_free_buf_indication_cnt); ++ len += scnprintf(buf + len, buf_len - len, ++ "ofld_buf_to_host_data_msdu_uc = %u\n", ++ htt_stats_buf->ofld_buf_to_host_data_msdu_uc); ++ len += scnprintf(buf + len, buf_len - len, ++ "reo_fw_ring_to_host_data_msdu_uc = %u\n", ++ htt_stats_buf->reo_fw_ring_to_host_data_msdu_uc); ++ len += scnprintf(buf + len, buf_len - len, "wbm_sw_ring_reap = %u\n", ++ htt_stats_buf->wbm_sw_ring_reap); ++ len += scnprintf(buf + len, buf_len - len, "wbm_forward_to_host_cnt = %u\n", ++ htt_stats_buf->wbm_forward_to_host_cnt); ++ len += scnprintf(buf + len, buf_len - len, "wbm_target_recycle_cnt = %u\n", ++ htt_stats_buf->wbm_target_recycle_cnt); ++ len += scnprintf(buf + len, buf_len - len, ++ "target_refill_ring_recycle_cnt = %u\n", ++ htt_stats_buf->target_refill_ring_recycle_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3317,14 +3313,14 @@ htt_print_rx_soc_fw_refill_ring_empty_tl + char refill_ring_empty_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_RX_SOC_FW_REFILL_RING_EMPTY_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_RX_SOC_FW_REFILL_RING_EMPTY_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(refill_ring_empty_cnt, + htt_stats_buf->refill_ring_empty_cnt, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_empty_cnt = %s\n", +- refill_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, "refill_ring_empty_cnt = %s\n\n", ++ refill_ring_empty_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3347,14 +3343,14 @@ htt_print_rx_soc_fw_refill_ring_num_rxdm + char rxdma_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_RXDMA_MAX_ERR_CODE); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_RX_SOC_FW_REFILL_RING_NUM_RXDMA_ERR_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_RX_SOC_FW_REFILL_RING_NUM_RXDMA_ERR_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(rxdma_err_cnt, + htt_stats_buf->rxdma_err, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdma_err = %s\n", +- rxdma_err_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rxdma_err = %s\n\n", ++ rxdma_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3376,14 +3372,14 @@ htt_print_rx_soc_fw_refill_ring_num_reo_ + char reo_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_REO_MAX_ERR_CODE); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_RX_SOC_FW_REFILL_RING_NUM_REO_ERR_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_RX_SOC_FW_REFILL_RING_NUM_REO_ERR_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(reo_err_cnt, + htt_stats_buf->reo_err, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "reo_err = %s\n", +- reo_err_cnt); ++ len += scnprintf(buf + len, buf_len - len, "reo_err = %s\n\n", ++ reo_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3402,27 +3398,27 @@ htt_print_rx_reo_debug_stats_tlv_v(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_REO_RESOURCE_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sample_id = %u", +- htt_stats_buf->sample_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "total_max = %u", +- htt_stats_buf->total_max); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "total_avg = %u", +- htt_stats_buf->total_avg); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "total_sample = %u", +- htt_stats_buf->total_sample); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_avg = %u", +- htt_stats_buf->non_zeros_avg); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_sample = %u", +- htt_stats_buf->non_zeros_sample); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_max = %u", +- htt_stats_buf->last_non_zeros_max); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_min %u", +- htt_stats_buf->last_non_zeros_min); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_avg %u", +- htt_stats_buf->last_non_zeros_avg); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_sample %u\n", +- htt_stats_buf->last_non_zeros_sample); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_REO_RESOURCE_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "sample_id = %u\n", ++ htt_stats_buf->sample_id); ++ len += scnprintf(buf + len, buf_len - len, "total_max = %u\n", ++ htt_stats_buf->total_max); ++ len += scnprintf(buf + len, buf_len - len, "total_avg = %u\n", ++ htt_stats_buf->total_avg); ++ len += scnprintf(buf + len, buf_len - len, "total_sample = %u\n", ++ htt_stats_buf->total_sample); ++ len += scnprintf(buf + len, buf_len - len, "non_zeros_avg = %u\n", ++ htt_stats_buf->non_zeros_avg); ++ len += scnprintf(buf + len, buf_len - len, "non_zeros_sample = %u\n", ++ htt_stats_buf->non_zeros_sample); ++ len += scnprintf(buf + len, buf_len - len, "last_non_zeros_max = %u\n", ++ htt_stats_buf->last_non_zeros_max); ++ len += scnprintf(buf + len, buf_len - len, "last_non_zeros_min %u\n", ++ htt_stats_buf->last_non_zeros_min); ++ len += scnprintf(buf + len, buf_len - len, "last_non_zeros_avg %u\n", ++ htt_stats_buf->last_non_zeros_avg); ++ len += scnprintf(buf + len, buf_len - len, "last_non_zeros_sample %u\n\n", ++ htt_stats_buf->last_non_zeros_sample); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3444,14 +3440,14 @@ htt_print_rx_soc_fw_refill_ring_num_refi + char refill_ring_num_refill[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_RX_SOC_FW_REFILL_RING_NUM_REFILL_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_RX_SOC_FW_REFILL_RING_NUM_REFILL_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(refill_ring_num_refill, + htt_stats_buf->refill_ring_num_refill, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_num_refill = %s\n", +- refill_ring_num_refill); ++ len += scnprintf(buf + len, buf_len - len, "refill_ring_num_refill = %s\n\n", ++ refill_ring_num_refill); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3471,110 +3467,110 @@ static inline void htt_print_rx_pdev_fw_ + char fw_ring_mgmt_subtype[HTT_MAX_STRING_LEN] = {0}; + char fw_ring_ctrl_subtype[HTT_MAX_STRING_LEN] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_recvd = %u", +- htt_stats_buf->ppdu_recvd); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_ok = %u", +- htt_stats_buf->mpdu_cnt_fcs_ok); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_err = %u", +- htt_stats_buf->mpdu_cnt_fcs_err); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_msdu_cnt = %u", +- htt_stats_buf->tcp_msdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_ack_msdu_cnt = %u", +- htt_stats_buf->tcp_ack_msdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "udp_msdu_cnt = %u", +- htt_stats_buf->udp_msdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "other_msdu_cnt = %u", +- htt_stats_buf->other_msdu_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_ind = %u", +- htt_stats_buf->fw_ring_mpdu_ind); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "ppdu_recvd = %u\n", ++ htt_stats_buf->ppdu_recvd); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_cnt_fcs_ok = %u\n", ++ htt_stats_buf->mpdu_cnt_fcs_ok); ++ len += scnprintf(buf + len, buf_len - len, "mpdu_cnt_fcs_err = %u\n", ++ htt_stats_buf->mpdu_cnt_fcs_err); ++ len += scnprintf(buf + len, buf_len - len, "tcp_msdu_cnt = %u\n", ++ htt_stats_buf->tcp_msdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "tcp_ack_msdu_cnt = %u\n", ++ htt_stats_buf->tcp_ack_msdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "udp_msdu_cnt = %u\n", ++ htt_stats_buf->udp_msdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "other_msdu_cnt = %u\n", ++ htt_stats_buf->other_msdu_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_mpdu_ind = %u\n", ++ htt_stats_buf->fw_ring_mpdu_ind); + + PRINT_ARRAY_TO_BUF(fw_ring_mgmt_subtype, + htt_stats_buf->fw_ring_mgmt_subtype, + HTT_STATS_SUBTYPE_MAX); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mgmt_subtype = %s ", +- fw_ring_mgmt_subtype); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_mgmt_subtype = %s\n", ++ fw_ring_mgmt_subtype); + + PRINT_ARRAY_TO_BUF(fw_ring_ctrl_subtype, + htt_stats_buf->fw_ring_ctrl_subtype, + HTT_STATS_SUBTYPE_MAX); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ctrl_subtype = %s ", +- fw_ring_ctrl_subtype); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mcast_data_msdu = %u", +- htt_stats_buf->fw_ring_mcast_data_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_bcast_data_msdu = %u", +- htt_stats_buf->fw_ring_bcast_data_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ucast_data_msdu = %u", +- htt_stats_buf->fw_ring_ucast_data_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_null_data_msdu = %u", +- htt_stats_buf->fw_ring_null_data_msdu); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_drop = %u", +- htt_stats_buf->fw_ring_mpdu_drop); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "ofld_local_data_ind_cnt = %u", +- htt_stats_buf->ofld_local_data_ind_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "ofld_local_data_buf_recycle_cnt = %u", +- htt_stats_buf->ofld_local_data_buf_recycle_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "drx_local_data_ind_cnt = %u", +- htt_stats_buf->drx_local_data_ind_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "drx_local_data_buf_recycle_cnt = %u", +- htt_stats_buf->drx_local_data_buf_recycle_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_ind_cnt = %u", +- htt_stats_buf->local_nondata_ind_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_buf_recycle_cnt = %u", +- htt_stats_buf->local_nondata_buf_recycle_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_refill_cnt = %u", +- htt_stats_buf->fw_status_buf_ring_refill_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_empty_cnt = %u", +- htt_stats_buf->fw_status_buf_ring_empty_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_refill_cnt = %u", +- htt_stats_buf->fw_pkt_buf_ring_refill_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_empty_cnt = %u", +- htt_stats_buf->fw_pkt_buf_ring_empty_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_refill_cnt = %u", +- htt_stats_buf->fw_link_buf_ring_refill_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_empty_cnt = %u", +- htt_stats_buf->fw_link_buf_ring_empty_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_refill_cnt = %u", +- htt_stats_buf->host_pkt_buf_ring_refill_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_empty_cnt = %u", +- htt_stats_buf->host_pkt_buf_ring_empty_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_refill_cnt = %u", +- htt_stats_buf->mon_pkt_buf_ring_refill_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_empty_cnt = %u", +- htt_stats_buf->mon_pkt_buf_ring_empty_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "mon_status_buf_ring_refill_cnt = %u", +- htt_stats_buf->mon_status_buf_ring_refill_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_status_buf_ring_empty_cnt = %u", +- htt_stats_buf->mon_status_buf_ring_empty_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_refill_cnt = %u", +- htt_stats_buf->mon_desc_buf_ring_refill_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_empty_cnt = %u", +- htt_stats_buf->mon_desc_buf_ring_empty_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_update_cnt = %u", +- htt_stats_buf->mon_dest_ring_update_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_full_cnt = %u", +- htt_stats_buf->mon_dest_ring_full_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_cnt = %u", +- htt_stats_buf->rx_suspend_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_fail_cnt = %u", +- htt_stats_buf->rx_suspend_fail_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_cnt = %u", +- htt_stats_buf->rx_resume_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_fail_cnt = %u", +- htt_stats_buf->rx_resume_fail_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_switch_cnt = %u", +- htt_stats_buf->rx_ring_switch_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_restore_cnt = %u", +- htt_stats_buf->rx_ring_restore_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_flush_cnt = %u", +- htt_stats_buf->rx_flush_cnt); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_recovery_reset_cnt = %u\n", +- htt_stats_buf->rx_recovery_reset_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_ctrl_subtype = %s\n", ++ fw_ring_ctrl_subtype); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_mcast_data_msdu = %u\n", ++ htt_stats_buf->fw_ring_mcast_data_msdu); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_bcast_data_msdu = %u\n", ++ htt_stats_buf->fw_ring_bcast_data_msdu); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_ucast_data_msdu = %u\n", ++ htt_stats_buf->fw_ring_ucast_data_msdu); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_null_data_msdu = %u\n", ++ htt_stats_buf->fw_ring_null_data_msdu); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_mpdu_drop = %u\n", ++ htt_stats_buf->fw_ring_mpdu_drop); ++ len += scnprintf(buf + len, buf_len - len, "ofld_local_data_ind_cnt = %u\n", ++ htt_stats_buf->ofld_local_data_ind_cnt); ++ len += scnprintf(buf + len, buf_len - len, ++ "ofld_local_data_buf_recycle_cnt = %u\n", ++ htt_stats_buf->ofld_local_data_buf_recycle_cnt); ++ len += scnprintf(buf + len, buf_len - len, "drx_local_data_ind_cnt = %u\n", ++ htt_stats_buf->drx_local_data_ind_cnt); ++ len += scnprintf(buf + len, buf_len - len, ++ "drx_local_data_buf_recycle_cnt = %u\n", ++ htt_stats_buf->drx_local_data_buf_recycle_cnt); ++ len += scnprintf(buf + len, buf_len - len, "local_nondata_ind_cnt = %u\n", ++ htt_stats_buf->local_nondata_ind_cnt); ++ len += scnprintf(buf + len, buf_len - len, "local_nondata_buf_recycle_cnt = %u\n", ++ htt_stats_buf->local_nondata_buf_recycle_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_status_buf_ring_refill_cnt = %u\n", ++ htt_stats_buf->fw_status_buf_ring_refill_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_status_buf_ring_empty_cnt = %u\n", ++ htt_stats_buf->fw_status_buf_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_pkt_buf_ring_refill_cnt = %u\n", ++ htt_stats_buf->fw_pkt_buf_ring_refill_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_pkt_buf_ring_empty_cnt = %u\n", ++ htt_stats_buf->fw_pkt_buf_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_link_buf_ring_refill_cnt = %u\n", ++ htt_stats_buf->fw_link_buf_ring_refill_cnt); ++ len += scnprintf(buf + len, buf_len - len, "fw_link_buf_ring_empty_cnt = %u\n", ++ htt_stats_buf->fw_link_buf_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, "host_pkt_buf_ring_refill_cnt = %u\n", ++ htt_stats_buf->host_pkt_buf_ring_refill_cnt); ++ len += scnprintf(buf + len, buf_len - len, "host_pkt_buf_ring_empty_cnt = %u\n", ++ htt_stats_buf->host_pkt_buf_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mon_pkt_buf_ring_refill_cnt = %u\n", ++ htt_stats_buf->mon_pkt_buf_ring_refill_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mon_pkt_buf_ring_empty_cnt = %u\n", ++ htt_stats_buf->mon_pkt_buf_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, ++ "mon_status_buf_ring_refill_cnt = %u\n", ++ htt_stats_buf->mon_status_buf_ring_refill_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mon_status_buf_ring_empty_cnt = %u\n", ++ htt_stats_buf->mon_status_buf_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mon_desc_buf_ring_refill_cnt = %u\n", ++ htt_stats_buf->mon_desc_buf_ring_refill_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mon_desc_buf_ring_empty_cnt = %u\n", ++ htt_stats_buf->mon_desc_buf_ring_empty_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mon_dest_ring_update_cnt = %u\n", ++ htt_stats_buf->mon_dest_ring_update_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mon_dest_ring_full_cnt = %u\n", ++ htt_stats_buf->mon_dest_ring_full_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_suspend_cnt = %u\n", ++ htt_stats_buf->rx_suspend_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_suspend_fail_cnt = %u\n", ++ htt_stats_buf->rx_suspend_fail_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_resume_cnt = %u\n", ++ htt_stats_buf->rx_resume_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_resume_fail_cnt = %u\n", ++ htt_stats_buf->rx_resume_fail_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_ring_switch_cnt = %u\n", ++ htt_stats_buf->rx_ring_switch_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_ring_restore_cnt = %u\n", ++ htt_stats_buf->rx_ring_restore_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_flush_cnt = %u\n", ++ htt_stats_buf->rx_flush_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_recovery_reset_cnt = %u\n\n", ++ htt_stats_buf->rx_recovery_reset_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3594,14 +3590,14 @@ htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_ring_mpdu_err[HTT_MAX_STRING_LEN] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_RX_PDEV_FW_RING_MPDU_ERR_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_RX_PDEV_FW_RING_MPDU_ERR_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(fw_ring_mpdu_err, + htt_stats_buf->fw_ring_mpdu_err, + HTT_RX_STATS_RXDMA_MAX_ERR); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_err = %s\n", +- fw_ring_mpdu_err); ++ len += scnprintf(buf + len, buf_len - len, "fw_ring_mpdu_err = %s\n\n", ++ fw_ring_mpdu_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3623,12 +3619,12 @@ htt_print_rx_pdev_fw_mpdu_drop_tlv_v(con + char fw_mpdu_drop[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_FW_DROP_REASON_MAX); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_MPDU_DROP_TLV_V:"); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_FW_MPDU_DROP_TLV_V:\n"); + + PRINT_ARRAY_TO_BUF(fw_mpdu_drop, + htt_stats_buf->fw_mpdu_drop, + num_elems); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_mpdu_drop = %s\n", fw_mpdu_drop); ++ len += scnprintf(buf + len, buf_len - len, "fw_mpdu_drop = %s\n\n", fw_mpdu_drop); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3648,16 +3644,16 @@ htt_print_rx_pdev_fw_stats_phy_err_tlv(c + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_PHY_ERR_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id__word = %u", +- htt_stats_buf->mac_id__word); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "total_phy_err_nct = %u", +- htt_stats_buf->total_phy_err_cnt); ++ len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_PHY_ERR_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id__word = %u\n", ++ htt_stats_buf->mac_id__word); ++ len += scnprintf(buf + len, buf_len - len, "total_phy_err_nct = %u\n", ++ htt_stats_buf->total_phy_err_cnt); + + PRINT_ARRAY_TO_BUF(phy_errs, + htt_stats_buf->phy_err, + HTT_STATS_PHY_ERR_MAX); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); ++ len += scnprintf(buf + len, buf_len - len, "phy_errs = %s\n\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3676,20 +3672,20 @@ htt_print_pdev_cca_stats_hist_tlv(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "\nHTT_PDEV_CCA_STATS_HIST_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "chan_num = %u", +- htt_stats_buf->chan_num); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u", +- htt_stats_buf->num_records); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "valid_cca_counters_bitmap = 0x%x", +- htt_stats_buf->valid_cca_counters_bitmap); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "collection_interval = %u\n", +- htt_stats_buf->collection_interval); +- +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "HTT_PDEV_STATS_CCA_COUNTERS_TLV:(in usec)"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "| tx_frame| rx_frame| rx_clear| my_rx_frame| cnt| med_rx_idle| med_tx_idle_global| cca_obss|"); ++ len += scnprintf(buf + len, buf_len - len, "\nHTT_PDEV_CCA_STATS_HIST_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "chan_num = %u\n", ++ htt_stats_buf->chan_num); ++ len += scnprintf(buf + len, buf_len - len, "num_records = %u\n", ++ htt_stats_buf->num_records); ++ len += scnprintf(buf + len, buf_len - len, "valid_cca_counters_bitmap = 0x%x\n", ++ htt_stats_buf->valid_cca_counters_bitmap); ++ len += scnprintf(buf + len, buf_len - len, "collection_interval = %u\n\n", ++ htt_stats_buf->collection_interval); ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_PDEV_STATS_CCA_COUNTERS_TLV:(in usec)\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "| tx_frame| rx_frame| rx_clear| my_rx_frame| cnt| med_rx_idle| med_tx_idle_global| cca_obss|\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3708,16 +3704,16 @@ htt_print_pdev_stats_cca_counters_tlv(co + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "|%10u| %10u| %10u| %11u| %10u| %11u| %18u| %10u|", +- htt_stats_buf->tx_frame_usec, +- htt_stats_buf->rx_frame_usec, +- htt_stats_buf->rx_clear_usec, +- htt_stats_buf->my_rx_frame_usec, +- htt_stats_buf->usec_cnt, +- htt_stats_buf->med_rx_idle_usec, +- htt_stats_buf->med_tx_idle_global_usec, +- htt_stats_buf->cca_obss_usec); ++ len += scnprintf(buf + len, buf_len - len, ++ "|%10u| %10u| %10u| %11u| %10u| %11u| %18u| %10u|\n", ++ htt_stats_buf->tx_frame_usec, ++ htt_stats_buf->rx_frame_usec, ++ htt_stats_buf->rx_clear_usec, ++ htt_stats_buf->my_rx_frame_usec, ++ htt_stats_buf->usec_cnt, ++ htt_stats_buf->med_rx_idle_usec, ++ htt_stats_buf->med_tx_idle_global_usec, ++ htt_stats_buf->cca_obss_usec); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3735,32 +3731,32 @@ static inline void htt_print_hw_stats_wh + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_WHAL_TX_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", +- htt_stats_buf->mac_id__word & 0xFF); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "last_unpause_ppdu_id = %u", +- htt_stats_buf->last_unpause_ppdu_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u", +- htt_stats_buf->hwsch_unpause_wait_tqm_write); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dummy_tlv_skipped = %u", +- htt_stats_buf->hwsch_dummy_tlv_skipped); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "hwsch_misaligned_offset_received = %u", +- htt_stats_buf->hwsch_misaligned_offset_received); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_reset_count = %u", +- htt_stats_buf->hwsch_reset_count); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dev_reset_war = %u", +- htt_stats_buf->hwsch_dev_reset_war); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_delayed_pause = %u", +- htt_stats_buf->hwsch_delayed_pause); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_long_delayed_pause = %u", +- htt_stats_buf->hwsch_long_delayed_pause); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_ppdu_no_response = %u", +- htt_stats_buf->sch_rx_ppdu_no_response); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_selfgen_response = %u", +- htt_stats_buf->sch_selfgen_response); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_sifs_resp_trigger= %u\n", +- htt_stats_buf->sch_rx_sifs_resp_trigger); ++ len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_WHAL_TX_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", ++ htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "last_unpause_ppdu_id = %u\n", ++ htt_stats_buf->last_unpause_ppdu_id); ++ len += scnprintf(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u\n", ++ htt_stats_buf->hwsch_unpause_wait_tqm_write); ++ len += scnprintf(buf + len, buf_len - len, "hwsch_dummy_tlv_skipped = %u\n", ++ htt_stats_buf->hwsch_dummy_tlv_skipped); ++ len += scnprintf(buf + len, buf_len - len, ++ "hwsch_misaligned_offset_received = %u\n", ++ htt_stats_buf->hwsch_misaligned_offset_received); ++ len += scnprintf(buf + len, buf_len - len, "hwsch_reset_count = %u\n", ++ htt_stats_buf->hwsch_reset_count); ++ len += scnprintf(buf + len, buf_len - len, "hwsch_dev_reset_war = %u\n", ++ htt_stats_buf->hwsch_dev_reset_war); ++ len += scnprintf(buf + len, buf_len - len, "hwsch_delayed_pause = %u\n", ++ htt_stats_buf->hwsch_delayed_pause); ++ len += scnprintf(buf + len, buf_len - len, "hwsch_long_delayed_pause = %u\n", ++ htt_stats_buf->hwsch_long_delayed_pause); ++ len += scnprintf(buf + len, buf_len - len, "sch_rx_ppdu_no_response = %u\n", ++ htt_stats_buf->sch_rx_ppdu_no_response); ++ len += scnprintf(buf + len, buf_len - len, "sch_selfgen_response = %u\n", ++ htt_stats_buf->sch_selfgen_response); ++ len += scnprintf(buf + len, buf_len - len, "sch_rx_sifs_resp_trigger= %u\n\n", ++ htt_stats_buf->sch_rx_sifs_resp_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3779,11 +3775,11 @@ htt_print_pdev_stats_twt_sessions_tlv(co + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSIONS_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", +- htt_stats_buf->pdev_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sessions = %u\n", +- htt_stats_buf->num_sessions); ++ len += scnprintf(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSIONS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", ++ htt_stats_buf->pdev_id); ++ len += scnprintf(buf + len, buf_len - len, "num_sessions = %u\n\n", ++ htt_stats_buf->num_sessions); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3802,27 +3798,27 @@ htt_print_pdev_stats_twt_session_tlv(con + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSION_TLV:"); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", +- htt_stats_buf->vdev_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "peer_mac = %02x:%02x:%02x:%02x:%02x:%02x", +- htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF, +- (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF00) >> 8, +- (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF0000) >> 16, +- (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF000000) >> 24, +- (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF), +- (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF00) >> 8); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "flow_id_flags = %u", +- htt_stats_buf->flow_id_flags); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "dialog_id = %u", +- htt_stats_buf->dialog_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_dura_us = %u", +- htt_stats_buf->wake_dura_us); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_intvl_us = %u", +- htt_stats_buf->wake_intvl_us); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "sp_offset_us = %u\n", +- htt_stats_buf->sp_offset_us); ++ len += scnprintf(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSION_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "vdev_id = %u\n", ++ htt_stats_buf->vdev_id); ++ len += scnprintf(buf + len, buf_len - len, ++ "peer_mac = %02x:%02x:%02x:%02x:%02x:%02x\n", ++ htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF, ++ (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF00) >> 8, ++ (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF0000) >> 16, ++ (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF000000) >> 24, ++ (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF), ++ (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "flow_id_flags = %u\n", ++ htt_stats_buf->flow_id_flags); ++ len += scnprintf(buf + len, buf_len - len, "dialog_id = %u\n", ++ htt_stats_buf->dialog_id); ++ len += scnprintf(buf + len, buf_len - len, "wake_dura_us = %u\n", ++ htt_stats_buf->wake_dura_us); ++ len += scnprintf(buf + len, buf_len - len, "wake_intvl_us = %u\n", ++ htt_stats_buf->wake_intvl_us); ++ len += scnprintf(buf + len, buf_len - len, "sp_offset_us = %u\n\n", ++ htt_stats_buf->sp_offset_us); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3841,21 +3837,21 @@ htt_print_pdev_obss_pd_stats_tlv_v(const + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "OBSS Tx success PPDU = %u", ++ len += scnprintf(buf + len, buf_len - len, "OBSS Tx success PPDU = %u\n", + htt_stats_buf->num_obss_tx_ppdu_success); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "OBSS Tx failures PPDU = %u\n", ++ len += scnprintf(buf + len, buf_len - len, "OBSS Tx failures PPDU = %u\n", + htt_stats_buf->num_obss_tx_ppdu_failure); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "Non-SRG Opportunities = %u\n", ++ len += scnprintf(buf + len, buf_len - len, "Non-SRG Opportunities = %u\n", + htt_stats_buf->num_non_srg_opportunities); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "Non-SRG tried PPDU = %u\n", ++ len += scnprintf(buf + len, buf_len - len, "Non-SRG tried PPDU = %u\n", + htt_stats_buf->num_non_srg_ppdu_tried); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "Non-SRG success PPDU = %u\n", ++ len += scnprintf(buf + len, buf_len - len, "Non-SRG success PPDU = %u\n", + htt_stats_buf->num_non_srg_ppdu_success); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "SRG Opportunities = %u\n", ++ len += scnprintf(buf + len, buf_len - len, "SRG Opportunities = %u\n", + htt_stats_buf->num_srg_opportunities); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "SRG tried PPDU = %u\n", ++ len += scnprintf(buf + len, buf_len - len, "SRG tried PPDU = %u\n", + htt_stats_buf->num_srg_ppdu_tried); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "SRG success PPDU = %u\n", ++ len += scnprintf(buf + len, buf_len - len, "SRG success PPDU = %u\n\n", + htt_stats_buf->num_srg_ppdu_success); + + if (len >= buf_len) +@@ -3878,25 +3874,25 @@ static inline void htt_print_backpressur + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + +- len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", +- htt_stats_buf->pdev_id); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "current_head_idx = %u", +- htt_stats_buf->current_head_idx); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "current_tail_idx = %u", +- htt_stats_buf->current_tail_idx); +- len += HTT_DBG_OUT(buf + len, buf_len - len, "num_htt_msgs_sent = %u", +- htt_stats_buf->num_htt_msgs_sent); +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "backpressure_time_ms = %u", +- htt_stats_buf->backpressure_time_ms); ++ len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", ++ htt_stats_buf->pdev_id); ++ len += scnprintf(buf + len, buf_len - len, "current_head_idx = %u\n", ++ htt_stats_buf->current_head_idx); ++ len += scnprintf(buf + len, buf_len - len, "current_tail_idx = %u\n", ++ htt_stats_buf->current_tail_idx); ++ len += scnprintf(buf + len, buf_len - len, "num_htt_msgs_sent = %u\n", ++ htt_stats_buf->num_htt_msgs_sent); ++ len += scnprintf(buf + len, buf_len - len, ++ "backpressure_time_ms = %u\n", ++ htt_stats_buf->backpressure_time_ms); + + for (i = 0; i < 5; i++) +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "backpressure_hist_%u = %u", +- i + 1, htt_stats_buf->backpressure_hist[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "backpressure_hist_%u = %u\n", ++ i + 1, htt_stats_buf->backpressure_hist[i]); + +- len += HTT_DBG_OUT(buf + len, buf_len - len, +- "============================"); ++ len += scnprintf(buf + len, buf_len - len, ++ "============================\n"); + + if (len >= buf_len) { + buf[buf_len - 1] = 0; diff --git a/package/kernel/mac80211/patches/ath11k/0044-ath11k-Remove-htt-stats-fixed-size-array-usage.patch b/package/kernel/mac80211/patches/ath11k/0044-ath11k-Remove-htt-stats-fixed-size-array-usage.patch new file mode 100644 index 00000000000000..70e5ea6ae12113 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0044-ath11k-Remove-htt-stats-fixed-size-array-usage.patch @@ -0,0 +1,1451 @@ +From 74327bab6781a34d96ff4c0a7c59bb032fab1650 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 28 Sep 2021 14:00:45 +0300 +Subject: [PATCH] ath11k: Remove htt stats fixed size array usage + +To support the HTT Stats DebugFS interface a single large buffer that +contains the stats must be provided to the DebugFS infrastructure. +In the current code, for each class of stats, the stats are first +formatted in a local on-stack buffer, and then the local buffer is +copied to the large DebugFS buffer. + +This logic has a problem when, for a given class, the formatted +stats exceed the size of the on-stack buffer. When this occurs the +stats for this class is truncated. In addition, this logic is +inefficient since it introduces an unnecessary memory copy. + +To address these issues, update the logic to no longer use a local +on-stack buffer, and instead write the formatted data directly into +the large DebugFS buffer. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01105-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913223148.208026-4-jouni@codeaurora.org +--- + .../wireless/ath/ath11k/debugfs_htt_stats.c | 838 ++++++------------ + 1 file changed, 264 insertions(+), 574 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +@@ -10,20 +10,28 @@ + #include "debug.h" + #include "debugfs_htt_stats.h" + +-#define HTT_MAX_STRING_LEN 256 + #define HTT_MAX_PRINT_CHAR_PER_ELEM 15 + + #define HTT_TLV_HDR_LEN 4 + +-#define PRINT_ARRAY_TO_BUF(out, arr, len) \ ++#define PRINT_ARRAY_TO_BUF(out, buflen, arr, str, len, newline) \ + do { \ +- int index = 0; u8 i; \ ++ int index = 0; u8 i; const char *str_val = str; \ ++ const char *new_line = newline; \ ++ if (str_val) { \ ++ index += scnprintf((out + buflen), \ ++ (ATH11K_HTT_STATS_BUF_SIZE - buflen), \ ++ "%s = ", str_val); \ ++ } \ + for (i = 0; i < len; i++) { \ +- index += scnprintf(out + index, HTT_MAX_STRING_LEN - index, \ +- " %u:%u,", i, arr[i]); \ +- if (index < 0 || index >= HTT_MAX_STRING_LEN) \ +- break; \ ++ index += scnprintf((out + buflen) + index, \ ++ (ATH11K_HTT_STATS_BUF_SIZE - buflen) - index, \ ++ " %u:%u,", i, arr[i]); \ + } \ ++ index += scnprintf((out + buflen) + index, \ ++ (ATH11K_HTT_STATS_BUF_SIZE - buflen) - index, \ ++ "%s", new_line); \ ++ buflen += index; \ + } while (0) + + static inline void htt_print_stats_string_tlv(const void *tag_buf, +@@ -35,22 +43,20 @@ static inline void htt_print_stats_strin + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; +- u16 index = 0; +- char data[HTT_MAX_STRING_LEN] = {0}; + + tag_len = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:\n"); + ++ len += scnprintf(buf + len, buf_len - len, ++ "data = "); + for (i = 0; i < tag_len; i++) { +- index += scnprintf(&data[index], +- HTT_MAX_STRING_LEN - index, +- "%.*s", 4, (char *)&(htt_stats_buf->data[i])); +- if (index >= HTT_MAX_STRING_LEN) +- break; ++ len += scnprintf(buf + len, ++ buf_len - len, ++ "%.*s", 4, (char *)&(htt_stats_buf->data[i])); + } +- +- len += scnprintf(buf + len, buf_len - len, "data = %s\n\n", data); ++ /* New lines are added for better display */ ++ len += scnprintf(buf + len, buf_len - len, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -187,13 +193,12 @@ htt_print_tx_pdev_stats_urrn_tlv_v(const + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char urrn_stats[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_URRN_STATS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_URRN_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(urrn_stats, htt_stats_buf->urrn_stats, num_elems); +- len += scnprintf(buf + len, buf_len - len, "urrn_stats = %s\n\n", urrn_stats); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->urrn_stats, "urrn_stats", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -212,13 +217,12 @@ htt_print_tx_pdev_stats_flush_tlv_v(cons + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char flush_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_FLUSH_REASON_STATS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_FLUSH_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(flush_errs, htt_stats_buf->flush_errs, num_elems); +- len += scnprintf(buf + len, buf_len - len, "flush_errs = %s\n\n", flush_errs); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->flush_errs, "flush_errs", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -237,14 +241,12 @@ htt_print_tx_pdev_stats_sifs_tlv_v(const + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char sifs_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_STATS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_SIFS_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(sifs_status, htt_stats_buf->sifs_status, num_elems); +- len += scnprintf(buf + len, buf_len - len, "sifs_status = %s\n\n", +- sifs_status); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->sifs_status, "sifs_status", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -263,13 +265,12 @@ htt_print_tx_pdev_stats_phy_err_tlv_v(co + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char phy_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_PHY_ERR_STATS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_PHY_ERR_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(phy_errs, htt_stats_buf->phy_errs, num_elems); +- len += scnprintf(buf + len, buf_len - len, "phy_errs = %s\n\n", phy_errs); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->phy_errs, "phy_errs", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -288,15 +289,13 @@ htt_print_tx_pdev_stats_sifs_hist_tlv_v( + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char sifs_hist_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_HIST_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(sifs_hist_status, htt_stats_buf->sifs_hist_status, num_elems); +- len += scnprintf(buf + len, buf_len - len, "sifs_hist_status = %s\n\n", +- sifs_hist_status); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->sifs_hist_status, ++ "sifs_hist_status", num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -350,25 +349,15 @@ htt_print_tx_pdev_stats_tried_mpdu_cnt_h + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - sizeof(htt_stats_buf->hist_bin_size)) >> 2); +- u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_TRIED_MPDU_CNT_HIST_TLV_V:\n"); + len += scnprintf(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u\n", + htt_stats_buf->hist_bin_size); + +- if (required_buffer_size < HTT_MAX_STRING_LEN) { +- PRINT_ARRAY_TO_BUF(tried_mpdu_cnt_hist, +- htt_stats_buf->tried_mpdu_cnt_hist, +- num_elements); +- len += scnprintf(buf + len, buf_len - len, "tried_mpdu_cnt_hist = %s\n\n", +- tried_mpdu_cnt_hist); +- } else { +- len += scnprintf(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER\n\n"); +- } ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tried_mpdu_cnt_hist, ++ "tried_mpdu_cnt_hist", num_elements, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -659,14 +648,12 @@ static inline void htt_print_counter_tlv + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char counter_name[HTT_MAX_STRING_LEN] = {0}; + + len += scnprintf(buf + len, buf_len - len, "HTT_COUNTER_TLV:\n"); + +- PRINT_ARRAY_TO_BUF(counter_name, +- htt_stats_buf->counter_name, +- HTT_MAX_COUNTER_NAME); +- len += scnprintf(buf + len, buf_len - len, "counter_name = %s\n", counter_name); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->counter_name, ++ "counter_name", ++ HTT_MAX_COUNTER_NAME, "\n"); + len += scnprintf(buf + len, buf_len - len, "count = %u\n\n", + htt_stats_buf->count); + +@@ -771,16 +758,8 @@ static inline void htt_print_tx_peer_rat + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char str_buf[HTT_MAX_STRING_LEN] = {0}; +- char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS] = {NULL}; + u8 j; + +- for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { +- tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); +- if (!tx_gi[j]) +- goto fail; +- } +- + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PEER_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n", + htt_stats_buf->tx_ldpc); +@@ -789,56 +768,30 @@ static inline void htt_print_tx_peer_rat + len += scnprintf(buf + len, buf_len - len, "ack_rssi = %u\n", + htt_stats_buf->ack_rssi); + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_su_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_su_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mu_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_mu_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, +- htt_stats_buf->tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "tx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, +- htt_stats_buf->tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_bw = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_stbc, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_stbc = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_pream, +- HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += scnprintf(buf + len, buf_len - len, "tx_pream = %s\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_mcs, "tx_mcs", ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_su_mcs, "tx_su_mcs", ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_mu_mcs, "tx_mu_mcs", ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_nss, "tx_nss", ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_bw, "tx_bw", ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_stbc, "tx_stbc", ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_pream, "tx_pream", ++ HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(tx_gi[j], +- htt_stats_buf->tx_gi[j], +- HTT_TX_PEER_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_gi[%u] = %s\n", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, ++ "tx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_gi[j], NULL, ++ HTT_TX_PEER_STATS_NUM_MCS_COUNTERS, "\n"); + } + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, +- htt_stats_buf->tx_dcm, +- HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_dcm = %s\n\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_dcm, "tx_dcm", ++ HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -847,9 +800,6 @@ static inline void htt_print_tx_peer_rat + + stats_req->buf_len = len; + +-fail: +- for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) +- kfree(tx_gi[j]); + } + + static inline void htt_print_rx_peer_rate_stats_tlv(const void *tag_buf, +@@ -860,21 +810,6 @@ static inline void htt_print_rx_peer_rat + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; +- char *rssi_chain[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS] = {NULL}; +- char *rx_gi[HTT_RX_PEER_STATS_NUM_GI_COUNTERS] = {NULL}; +- char str_buf[HTT_MAX_STRING_LEN] = {0}; +- +- for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { +- rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); +- if (!rssi_chain[j]) +- goto fail; +- } +- +- for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { +- rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); +- if (!rx_gi[j]) +- goto fail; +- } + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PEER_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "nsts = %u\n", +@@ -890,49 +825,33 @@ static inline void htt_print_rx_peer_rat + len += scnprintf(buf + len, buf_len - len, "rssi_comb = %u\n", + htt_stats_buf->rssi_comb); + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_mcs, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_nss, +- HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "rx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_dcm, +- HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_dcm = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_stbc, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_stbc = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_bw, +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_bw = %s\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_mcs, "rx_mcs", ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_nss, "rx_nss", ++ HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_dcm, "rx_dcm", ++ HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_stbc, "rx_stbc", ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_bw, "rx_bw", ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { +- PRINT_ARRAY_TO_BUF(rssi_chain[j], htt_stats_buf->rssi_chain[j], +- HTT_RX_PEER_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rssi_chain[%u] = %s\n", +- j, rssi_chain[j]); ++ len += scnprintf(buf + len, (buf_len - len), ++ "rssi_chain[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rssi_chain[j], NULL, ++ HTT_RX_PEER_STATS_NUM_BW_COUNTERS, "\n"); + } + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->rx_gi[j], +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_gi[%u] = %s\n", +- j, rx_gi[j]); ++ len += scnprintf(buf + len, (buf_len - len), ++ "rx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_gi[j], NULL, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + } + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_pream, +- HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += scnprintf(buf + len, buf_len - len, "rx_pream = %s\n\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_pream, "rx_pream", ++ HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -940,13 +859,6 @@ static inline void htt_print_rx_peer_rat + buf[len] = 0; + + stats_req->buf_len = len; +- +-fail: +- for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) +- kfree(rssi_chain[j]); +- +- for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) +- kfree(rx_gi[j]); + } + + static inline void +@@ -1104,17 +1016,14 @@ htt_print_tx_hwq_difs_latency_stats_tlv_ + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS); +- char difs_latency_hist[HTT_MAX_STRING_LEN] = {0}; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_HWQ_DIFS_LATENCY_STATS_TLV_V:\n"); + len += scnprintf(buf + len, buf_len - len, "hist_intvl = %u\n", + htt_stats_buf->hist_intvl); + +- PRINT_ARRAY_TO_BUF(difs_latency_hist, htt_stats_buf->difs_latency_hist, +- data_len); +- len += scnprintf(buf + len, buf_len - len, "difs_latency_hist = %s\n\n", +- difs_latency_hist); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->difs_latency_hist, ++ "difs_latency_hist", data_len, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1134,16 +1043,14 @@ htt_print_tx_hwq_cmd_result_stats_tlv_v( + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len; +- char cmd_result[HTT_MAX_STRING_LEN] = {0}; + + data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_RESULT_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_HWQ_CMD_RESULT_STATS_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(cmd_result, htt_stats_buf->cmd_result, data_len); +- +- len += scnprintf(buf + len, buf_len - len, "cmd_result = %s\n\n", cmd_result); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->cmd_result, "cmd_result", ++ data_len, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1163,15 +1070,13 @@ htt_print_tx_hwq_cmd_stall_stats_tlv_v(c + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; +- char cmd_stall_status[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_STALL_STATS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_HWQ_CMD_STALL_STATS_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(cmd_stall_status, htt_stats_buf->cmd_stall_status, num_elems); +- len += scnprintf(buf + len, buf_len - len, "cmd_stall_status = %s\n\n", +- cmd_stall_status); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->cmd_stall_status, ++ "cmd_stall_status", num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1191,15 +1096,14 @@ htt_print_tx_hwq_fes_result_stats_tlv_v( + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; +- char fes_result[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_FES_RESULT_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_HWQ_FES_RESULT_STATS_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(fes_result, htt_stats_buf->fes_result, num_elems); +- len += scnprintf(buf + len, buf_len - len, "fes_result = %s\n\n", fes_result); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->fes_result, "fes_result", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1218,27 +1122,16 @@ htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - + sizeof(htt_stats_buf->hist_bin_size)) >> 2); +- u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_HWQ_TRIED_MPDU_CNT_HIST_TLV_V:\n"); + len += scnprintf(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u\n", + htt_stats_buf->hist_bin_size); + +- if (required_buffer_size < HTT_MAX_STRING_LEN) { +- PRINT_ARRAY_TO_BUF(tried_mpdu_cnt_hist, +- htt_stats_buf->tried_mpdu_cnt_hist, +- num_elements); +- len += scnprintf(buf + len, buf_len - len, +- "tried_mpdu_cnt_hist = %s\n\n", +- tried_mpdu_cnt_hist); +- } else { +- len += scnprintf(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER\n"); +- } ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tried_mpdu_cnt_hist, ++ "tried_mpdu_cnt_hist", num_elements, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1257,23 +1150,14 @@ htt_print_tx_hwq_txop_used_cnt_hist_tlv_ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char txop_used_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = tag_len >> 2; +- u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_HWQ_TXOP_USED_CNT_HIST_TLV_V:\n"); + +- if (required_buffer_size < HTT_MAX_STRING_LEN) { +- PRINT_ARRAY_TO_BUF(txop_used_cnt_hist, +- htt_stats_buf->txop_used_cnt_hist, +- num_elements); +- len += scnprintf(buf + len, buf_len - len, "txop_used_cnt_hist = %s\n\n", +- txop_used_cnt_hist); +- } else { +- len += scnprintf(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER\n"); +- } ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->txop_used_cnt_hist, ++ "txop_used_cnt_hist", num_elements, "\n\n"); ++ + if (len >= buf_len) + buf[buf_len - 1] = 0; + else +@@ -1781,15 +1665,12 @@ htt_print_sched_txq_cmd_posted_tlv_v(con + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char sched_cmd_posted[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(sched_cmd_posted, htt_stats_buf->sched_cmd_posted, +- num_elements); +- len += scnprintf(buf + len, buf_len - len, "sched_cmd_posted = %s\n\n", +- sched_cmd_posted); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->sched_cmd_posted, ++ "sched_cmd_posted", num_elements, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1808,15 +1689,12 @@ htt_print_sched_txq_cmd_reaped_tlv_v(con + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char sched_cmd_reaped[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(sched_cmd_reaped, htt_stats_buf->sched_cmd_reaped, +- num_elements); +- len += scnprintf(buf + len, buf_len - len, "sched_cmd_reaped = %s\n\n", +- sched_cmd_reaped); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->sched_cmd_reaped, ++ "sched_cmd_reaped", num_elements, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1835,7 +1713,6 @@ htt_print_sched_txq_sched_order_su_tlv_v + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char sched_order_su[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_order_su_num_entries = + min_t(u32, (tag_len >> 2), HTT_TX_PDEV_NUM_SCHED_ORDER_LOG); +@@ -1843,10 +1720,8 @@ htt_print_sched_txq_sched_order_su_tlv_v + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(sched_order_su, htt_stats_buf->sched_order_su, +- sched_order_su_num_entries); +- len += scnprintf(buf + len, buf_len - len, "sched_order_su = %s\n\n", +- sched_order_su); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->sched_order_su, "sched_order_su", ++ sched_order_su_num_entries, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1865,17 +1740,15 @@ htt_print_sched_txq_sched_ineligibility_ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char sched_ineligibility[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_ineligibility_num_entries = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_INELIGIBILITY_V:\n"); + +- PRINT_ARRAY_TO_BUF(sched_ineligibility, htt_stats_buf->sched_ineligibility, +- sched_ineligibility_num_entries); +- len += scnprintf(buf + len, buf_len - len, "sched_ineligibility = %s\n\n", +- sched_ineligibility); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->sched_ineligibility, ++ "sched_ineligibility", sched_ineligibility_num_entries, ++ "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -1982,16 +1855,13 @@ htt_print_tx_tqm_gen_mpdu_stats_tlv_v(co + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char gen_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(gen_mpdu_end_reason, htt_stats_buf->gen_mpdu_end_reason, +- num_elements); +- len += scnprintf(buf + len, buf_len - len, "gen_mpdu_end_reason = %s\n\n", +- gen_mpdu_end_reason); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->gen_mpdu_end_reason, ++ "gen_mpdu_end_reason", num_elements, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2010,16 +1880,14 @@ htt_print_tx_tqm_list_mpdu_stats_tlv_v(c + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char list_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_TQM_LIST_MPDU_STATS_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(list_mpdu_end_reason, htt_stats_buf->list_mpdu_end_reason, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "list_mpdu_end_reason = %s\n\n", +- list_mpdu_end_reason); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->list_mpdu_end_reason, ++ "list_mpdu_end_reason", num_elems, "\n\n"); ++ + if (len >= buf_len) + buf[buf_len - 1] = 0; + else +@@ -2037,16 +1905,13 @@ htt_print_tx_tqm_list_mpdu_cnt_tlv_v(con + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char list_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(list_mpdu_cnt_hist, htt_stats_buf->list_mpdu_cnt_hist, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "list_mpdu_cnt_hist = %s\n\n", +- list_mpdu_cnt_hist); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->list_mpdu_cnt_hist, ++ "list_mpdu_cnt_hist", num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2527,24 +2392,13 @@ htt_print_tx_de_fw2wbm_ring_full_hist_tl + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char fw2wbm_ring_full_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = tag_len >> 2; +- u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_DE_FW2WBM_RING_FULL_HIST_TLV"); + +- if (required_buffer_size < HTT_MAX_STRING_LEN) { +- PRINT_ARRAY_TO_BUF(fw2wbm_ring_full_hist, +- htt_stats_buf->fw2wbm_ring_full_hist, +- num_elements); +- len += scnprintf(buf + len, buf_len - len, +- "fw2wbm_ring_full_hist = %s\n\n", +- fw2wbm_ring_full_hist); +- } else { +- len += scnprintf(buf + len, buf_len - len, +- "INSUFFICIENT PRINT BUFFER\n"); +- } ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->fw2wbm_ring_full_hist, ++ "fw2wbm_ring_full_hist", num_elements, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2593,8 +2447,6 @@ static inline void htt_print_ring_if_sta + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char low_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; +- char high_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + + len += scnprintf(buf + len, buf_len - len, "HTT_RING_IF_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "base_addr = %u\n", +@@ -2630,15 +2482,10 @@ static inline void htt_print_ring_if_sta + len += scnprintf(buf + len, buf_len - len, "cons_blockwait_count = %u\n", + htt_stats_buf->cons_blockwait_count); + +- PRINT_ARRAY_TO_BUF(low_wm_hit_count, htt_stats_buf->low_wm_hit_count, +- HTT_STATS_LOW_WM_BINS); +- len += scnprintf(buf + len, buf_len - len, "low_wm_hit_count = %s\n", +- low_wm_hit_count); +- +- PRINT_ARRAY_TO_BUF(high_wm_hit_count, htt_stats_buf->high_wm_hit_count, +- HTT_STATS_HIGH_WM_BINS); +- len += scnprintf(buf + len, buf_len - len, "high_wm_hit_count = %s\n\n", +- high_wm_hit_count); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->low_wm_hit_count, ++ "low_wm_hit_count", HTT_STATS_LOW_WM_BINS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->high_wm_hit_count, ++ "high_wm_hit_count", HTT_STATS_HIGH_WM_BINS, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2678,16 +2525,12 @@ static inline void htt_print_sfm_client_ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char dwords_used_by_user_n[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(dwords_used_by_user_n, +- htt_stats_buf->dwords_used_by_user_n, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "dwords_used_by_user_n = %s\n\n", +- dwords_used_by_user_n); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->dwords_used_by_user_n, ++ "dwords_used_by_user_n", num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2838,14 +2681,6 @@ static inline void htt_print_tx_pdev_rat + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; +- char str_buf[HTT_MAX_STRING_LEN] = {0}; +- char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS] = {NULL}; +- +- for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { +- tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); +- if (!tx_gi[j]) +- goto fail; +- } + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +@@ -2884,75 +2719,37 @@ static inline void htt_print_tx_pdev_rat + htt_stats_buf->tx_legacy_ofdm_rate[6], + htt_stats_buf->tx_legacy_ofdm_rate[7]); + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_mcs, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ofdma_tx_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "tx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_nss, +- HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "ofdma_tx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_bw = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ac_mu_mimo_tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_bw = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ax_mu_mimo_tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_bw = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ofdma_tx_bw, +- HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ofdma_tx_bw = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_stbc, +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_stbc = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_pream, +- HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += scnprintf(buf + len, buf_len - len, "tx_pream = %s\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_mcs, "tx_mcs", ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ac_mu_mimo_tx_mcs, ++ "ac_mu_mimo_tx_mcs", HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ax_mu_mimo_tx_mcs, ++ "ax_mu_mimo_tx_mcs", HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ofdma_tx_mcs, "ofdma_tx_mcs", ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_nss, "tx_nss", ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ac_mu_mimo_tx_nss, ++ "ac_mu_mimo_tx_nss", ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ax_mu_mimo_tx_nss, ++ "ax_mu_mimo_tx_nss", ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ofdma_tx_nss, "ofdma_tx_nss", ++ HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_bw, "tx_bw", ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ac_mu_mimo_tx_bw, ++ "ac_mu_mimo_tx_bw", HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ax_mu_mimo_tx_bw, ++ "ax_mu_mimo_tx_bw", ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ofdma_tx_bw, "ofdma_tx_bw", ++ HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_stbc, "tx_stbc", ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_pream, "tx_pream", ++ HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + + len += scnprintf(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u\n", + htt_stats_buf->tx_he_ltf[1], +@@ -2961,42 +2758,38 @@ static inline void htt_print_tx_pdev_rat + + /* SU GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_gi[%u] = %s\n", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, (buf_len - len), ++ "tx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_gi[j], NULL, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + } + + /* AC MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ac_mu_mimo_tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, +- "ac_mu_mimo_tx_gi[%u] = %s\n", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, (buf_len - len), ++ "ac_mu_mimo_tx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ac_mu_mimo_tx_gi[j], ++ NULL, HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + } + + /* AX MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ax_mu_mimo_tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, +- "ax_mu_mimo_tx_gi[%u] = %s\n", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, (buf_len - len), ++ "ax_mu_mimo_tx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ax_mu_mimo_tx_gi[j], ++ NULL, HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + } + + /* DL OFDMA GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(tx_gi[j], htt_stats_buf->ofdma_tx_gi[j], +- HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ofdma_tx_gi[%u] = %s\n", +- j, tx_gi[j]); ++ len += scnprintf(buf + len, (buf_len - len), ++ "ofdma_tx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ofdma_tx_gi[j], NULL, ++ HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + } + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->tx_dcm, +- HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "tx_dcm = %s\n\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->tx_dcm, "tx_dcm", ++ HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3004,9 +2797,6 @@ static inline void htt_print_tx_pdev_rat + buf[len] = 0; + + stats_req->buf_len = len; +-fail: +- for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) +- kfree(tx_gi[j]); + } + + static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, +@@ -3017,29 +2807,6 @@ static inline void htt_print_rx_pdev_rat + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i, j; +- u16 index = 0; +- char *rssi_chain[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] = {NULL}; +- char *rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] = {NULL}; +- char str_buf[HTT_MAX_STRING_LEN] = {0}; +- char *rx_pilot_evm_db[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] = {NULL}; +- +- for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); +- if (!rssi_chain[j]) +- goto fail; +- } +- +- for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); +- if (!rx_gi[j]) +- goto fail; +- } +- +- for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- rx_pilot_evm_db[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); +- if (!rx_pilot_evm_db[j]) +- goto fail; +- } + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +@@ -3059,30 +2826,17 @@ static inline void htt_print_rx_pdev_rat + len += scnprintf(buf + len, buf_len - len, "rssi_in_dbm = %d\n", + htt_stats_buf->rssi_in_dbm); + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_mcs, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_mcs = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_nss, +- HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "rx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_dcm, +- HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_dcm = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_stbc, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_stbc = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_bw, +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_bw = %s\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_mcs, "rx_mcs", ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_nss, "rx_nss", ++ HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_dcm, "rx_dcm", ++ HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_stbc, "rx_stbc", ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_bw, "rx_bw", ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); ++ + len += scnprintf(buf + len, buf_len - len, "rx_evm_nss_count = %u\n", + htt_stats_buf->nss_count); + +@@ -3090,44 +2844,43 @@ static inline void htt_print_rx_pdev_rat + htt_stats_buf->pilot_count); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- index = 0; + ++ len += scnprintf(buf + len, buf_len - len, ++ "pilot_evm_db[%u] = ", j); + for (i = 0; i < HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS; i++) +- index += scnprintf(&rx_pilot_evm_db[j][index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", +- i, +- htt_stats_buf->rx_pilot_evm_db[j][i]); +- len += scnprintf(buf + len, buf_len - len, "pilot_evm_dB[%u] = %s\n", +- j, rx_pilot_evm_db[j]); ++ len += scnprintf(buf + len, ++ buf_len - len, ++ " %u:%d,", ++ i, ++ htt_stats_buf->rx_pilot_evm_db[j][i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); + } + +- index = 0; +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); ++ len += scnprintf(buf + len, buf_len - len, ++ "pilot_evm_db_mean = "); + for (i = 0; i < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) +- index += scnprintf(&str_buf[index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", i, htt_stats_buf->rx_pilot_evm_db_mean[i]); +- len += scnprintf(buf + len, buf_len - len, "pilot_evm_dB_mean = %s\n", str_buf); ++ len += scnprintf(buf + len, ++ buf_len - len, ++ " %u:%d,", i, ++ htt_stats_buf->rx_pilot_evm_db_mean[i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- PRINT_ARRAY_TO_BUF(rssi_chain[j], htt_stats_buf->rssi_chain[j], +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rssi_chain[%u] = %s\n", +- j, rssi_chain[j]); ++ len += scnprintf(buf + len, buf_len - len, ++ "rssi_chain[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rssi_chain[j], NULL, ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->rx_gi[j], +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "rx_gi[%u] = %s\n", +- j, rx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, ++ "rx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_gi[j], NULL, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + } + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_pream, +- HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); +- len += scnprintf(buf + len, buf_len - len, "rx_pream = %s\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_pream, "rx_pream", ++ HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + + len += scnprintf(buf + len, buf_len - len, "rx_11ax_su_ext = %u\n", + htt_stats_buf->rx_11ax_su_ext); +@@ -3140,17 +2893,13 @@ static inline void htt_print_rx_pdev_rat + len += scnprintf(buf + len, buf_len - len, "txbf = %u\n", + htt_stats_buf->txbf); + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_legacy_cck_rate, +- HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS); +- len += scnprintf(buf + len, buf_len - len, "rx_legacy_cck_rate = %s\n", +- str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_legacy_ofdm_rate, +- HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS); +- len += scnprintf(buf + len, buf_len - len, "rx_legacy_ofdm_rate = %s\n", +- str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_legacy_cck_rate, ++ "rx_legacy_cck_rate", ++ HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS, "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_legacy_ofdm_rate, ++ "rx_legacy_ofdm_rate", ++ HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS, "\n"); + + len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_low = %u\n", + htt_stats_buf->rx_active_dur_us_low); +@@ -3159,82 +2908,66 @@ static inline void htt_print_rx_pdev_rat + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", + htt_stats_buf->rx_11ax_ul_ofdma); + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_mcs, +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_mcs = %s\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ul_ofdma_rx_mcs, ++ "ul_ofdma_rx_mcs", ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { +- PRINT_ARRAY_TO_BUF(rx_gi[j], htt_stats_buf->ul_ofdma_rx_gi[j], +- HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u] = %s\n", +- j, rx_gi[j]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ul_ofdma_rx_gi[%u] = ", j); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ul_ofdma_rx_gi[j], NULL, ++ HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + } + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_nss, +- HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); +- len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_nss = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->ul_ofdma_rx_bw, +- HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); +- len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_bw = %s\n", str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ul_ofdma_rx_nss, ++ "ul_ofdma_rx_nss", ++ HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->ul_ofdma_rx_bw, "ul_ofdma_rx_bw", ++ HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + htt_stats_buf->ul_ofdma_rx_stbc); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + htt_stats_buf->ul_ofdma_rx_ldpc); + +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_non_data_ppdu, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu = %s\n", +- str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_data_ppdu, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ppdu = %s\n", +- str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_mpdu_ok, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok = %s\n", str_buf); +- +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- PRINT_ARRAY_TO_BUF(str_buf, htt_stats_buf->rx_ulofdma_mpdu_fail, +- HTT_RX_PDEV_MAX_OFDMA_NUM_USER); +- len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail = %s\n", +- str_buf); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulofdma_non_data_ppdu, ++ "rx_ulofdma_non_data_ppdu", ++ HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulofdma_data_ppdu, ++ "rx_ulofdma_data_ppdu", HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulofdma_mpdu_ok, ++ "rx_ulofdma_mpdu_ok", HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulofdma_mpdu_fail, ++ "rx_ulofdma_mpdu_fail", HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- index = 0; +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- for (i = 0; i < HTT_RX_PDEV_MAX_OFDMA_NUM_USER; i++) +- index += scnprintf(&str_buf[index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", +- i, htt_stats_buf->rx_ul_fd_rssi[j][i]); + len += scnprintf(buf + len, buf_len - len, +- "rx_ul_fd_rssi: nss[%u] = %s\n", j, str_buf); ++ "rx_ul_fd_rssi: nss[%u] = ", j); ++ for (i = 0; i < HTT_RX_PDEV_MAX_OFDMA_NUM_USER; i++) ++ len += scnprintf(buf + len, ++ buf_len - len, ++ " %u:%d,", ++ i, htt_stats_buf->rx_ul_fd_rssi[j][i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); + } + + len += scnprintf(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x\n", + htt_stats_buf->per_chain_rssi_pkt_type); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- index = 0; +- memset(str_buf, 0x0, HTT_MAX_STRING_LEN); +- for (i = 0; i < HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) +- index += scnprintf(&str_buf[index], +- HTT_MAX_STRING_LEN - index, +- " %u:%d,", +- i, +- htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); + len += scnprintf(buf + len, buf_len - len, +- "rx_per_chain_rssi_in_dbm[%u] = %s\n", j, str_buf); ++ "rx_per_chain_rssi_in_dbm[%u] = ", j); ++ for (i = 0; i < HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) ++ len += scnprintf(buf + len, ++ buf_len - len, ++ " %u:%d,", ++ i, ++ htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); + } + len += scnprintf(buf + len, buf_len - len, "\n"); + +@@ -3244,16 +2977,6 @@ static inline void htt_print_rx_pdev_rat + buf[len] = 0; + + stats_req->buf_len = len; +- +-fail: +- for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) +- kfree(rssi_chain[j]); +- +- for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) +- kfree(rx_pilot_evm_db[j]); +- +- for (i = 0; i < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; i++) +- kfree(rx_gi[i]); + } + + static inline void htt_print_rx_soc_fw_stats_tlv(const void *tag_buf, +@@ -3310,17 +3033,13 @@ htt_print_rx_soc_fw_refill_ring_empty_tl + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char refill_ring_empty_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_EMPTY_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(refill_ring_empty_cnt, +- htt_stats_buf->refill_ring_empty_cnt, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "refill_ring_empty_cnt = %s\n\n", +- refill_ring_empty_cnt); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->refill_ring_empty_cnt, ++ "refill_ring_empty_cnt", num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3340,17 +3059,13 @@ htt_print_rx_soc_fw_refill_ring_num_rxdm + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char rxdma_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_RXDMA_MAX_ERR_CODE); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_RXDMA_ERR_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(rxdma_err_cnt, +- htt_stats_buf->rxdma_err, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "rxdma_err = %s\n\n", +- rxdma_err_cnt); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rxdma_err, "rxdma_err", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3369,17 +3084,13 @@ htt_print_rx_soc_fw_refill_ring_num_reo_ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char reo_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_REO_MAX_ERR_CODE); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REO_ERR_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(reo_err_cnt, +- htt_stats_buf->reo_err, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "reo_err = %s\n\n", +- reo_err_cnt); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->reo_err, "reo_err", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3437,17 +3148,13 @@ htt_print_rx_soc_fw_refill_ring_num_refi + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char refill_ring_num_refill[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REFILL_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(refill_ring_num_refill, +- htt_stats_buf->refill_ring_num_refill, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "refill_ring_num_refill = %s\n\n", +- refill_ring_num_refill); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->refill_ring_num_refill, ++ "refill_ring_num_refill", num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3464,8 +3171,6 @@ static inline void htt_print_rx_pdev_fw_ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char fw_ring_mgmt_subtype[HTT_MAX_STRING_LEN] = {0}; +- char fw_ring_ctrl_subtype[HTT_MAX_STRING_LEN] = {0}; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +@@ -3487,17 +3192,12 @@ static inline void htt_print_rx_pdev_fw_ + len += scnprintf(buf + len, buf_len - len, "fw_ring_mpdu_ind = %u\n", + htt_stats_buf->fw_ring_mpdu_ind); + +- PRINT_ARRAY_TO_BUF(fw_ring_mgmt_subtype, +- htt_stats_buf->fw_ring_mgmt_subtype, +- HTT_STATS_SUBTYPE_MAX); +- len += scnprintf(buf + len, buf_len - len, "fw_ring_mgmt_subtype = %s\n", +- fw_ring_mgmt_subtype); +- +- PRINT_ARRAY_TO_BUF(fw_ring_ctrl_subtype, +- htt_stats_buf->fw_ring_ctrl_subtype, +- HTT_STATS_SUBTYPE_MAX); +- len += scnprintf(buf + len, buf_len - len, "fw_ring_ctrl_subtype = %s\n", +- fw_ring_ctrl_subtype); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->fw_ring_mgmt_subtype, ++ "fw_ring_mgmt_subtype", HTT_STATS_SUBTYPE_MAX, "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->fw_ring_ctrl_subtype, ++ "fw_ring_ctrl_subtype", HTT_STATS_SUBTYPE_MAX, "\n"); ++ + len += scnprintf(buf + len, buf_len - len, "fw_ring_mcast_data_msdu = %u\n", + htt_stats_buf->fw_ring_mcast_data_msdu); + len += scnprintf(buf + len, buf_len - len, "fw_ring_bcast_data_msdu = %u\n", +@@ -3588,16 +3288,12 @@ htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char fw_ring_mpdu_err[HTT_MAX_STRING_LEN] = {0}; + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_FW_RING_MPDU_ERR_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(fw_ring_mpdu_err, +- htt_stats_buf->fw_ring_mpdu_err, +- HTT_RX_STATS_RXDMA_MAX_ERR); +- len += scnprintf(buf + len, buf_len - len, "fw_ring_mpdu_err = %s\n\n", +- fw_ring_mpdu_err); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->fw_ring_mpdu_err, ++ "fw_ring_mpdu_err", HTT_RX_STATS_RXDMA_MAX_ERR, "\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3616,15 +3312,12 @@ htt_print_rx_pdev_fw_mpdu_drop_tlv_v(con + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char fw_mpdu_drop[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_FW_DROP_REASON_MAX); + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_FW_MPDU_DROP_TLV_V:\n"); + +- PRINT_ARRAY_TO_BUF(fw_mpdu_drop, +- htt_stats_buf->fw_mpdu_drop, +- num_elems); +- len += scnprintf(buf + len, buf_len - len, "fw_mpdu_drop = %s\n\n", fw_mpdu_drop); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->fw_mpdu_drop, "fw_mpdu_drop", ++ num_elems, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -3642,7 +3335,6 @@ htt_print_rx_pdev_fw_stats_phy_err_tlv(c + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; +- char phy_errs[HTT_MAX_STRING_LEN] = {0}; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_PHY_ERR_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id__word = %u\n", +@@ -3650,10 +3342,8 @@ htt_print_rx_pdev_fw_stats_phy_err_tlv(c + len += scnprintf(buf + len, buf_len - len, "total_phy_err_nct = %u\n", + htt_stats_buf->total_phy_err_cnt); + +- PRINT_ARRAY_TO_BUF(phy_errs, +- htt_stats_buf->phy_err, +- HTT_STATS_PHY_ERR_MAX); +- len += scnprintf(buf + len, buf_len - len, "phy_errs = %s\n\n", phy_errs); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->phy_err, "phy_errs", ++ HTT_STATS_PHY_ERR_MAX, "\n\n"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; diff --git a/package/kernel/mac80211/patches/ath11k/0045-ath11k-Change-masking-and-shifting-in-htt-stats.patch b/package/kernel/mac80211/patches/ath11k/0045-ath11k-Change-masking-and-shifting-in-htt-stats.patch new file mode 100644 index 00000000000000..6cd198849e7746 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0045-ath11k-Change-masking-and-shifting-in-htt-stats.patch @@ -0,0 +1,683 @@ +From 6ed731829cf862dc1f73bbd063662d8a6c78a5b7 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Tue, 28 Sep 2021 14:00:45 +0300 +Subject: [PATCH] ath11k: Change masking and shifting in htt stats + +In debugfs_htt_stats.c, masking and shifting is done to get +stats values. Instead use GENMASK and FIELD_GET to improve +code readability and maintenance. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01105-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913223148.208026-5-jouni@codeaurora.org +--- + .../wireless/ath/ath11k/debugfs_htt_stats.c | 318 ++++++++++-------- + .../wireless/ath/ath11k/debugfs_htt_stats.h | 54 +++ + drivers/net/wireless/ath/ath11k/dp.h | 7 + + 3 files changed, 243 insertions(+), 136 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +@@ -75,8 +75,8 @@ static inline void htt_print_tx_pdev_sta + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_CMN_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "hw_queued = %u\n", + htt_stats_buf->hw_queued); + len += scnprintf(buf + len, buf_len - len, "hw_reaped = %u\n", +@@ -428,8 +428,8 @@ static inline void htt_print_hw_stats_pd + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_PDEV_ERRS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "tx_abort = %u\n", + htt_stats_buf->tx_abort); + len += scnprintf(buf + len, buf_len - len, "tx_abort_fail_count = %u\n", +@@ -480,13 +480,15 @@ static inline void htt_print_msdu_flow_s + htt_stats_buf->cur_msdu_count_in_flowq); + len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", + htt_stats_buf->sw_peer_id); +- len += scnprintf(buf + len, buf_len - len, "tx_flow_no = %u\n", +- htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", +- (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "drop_rule = %u\n", +- (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0x100000) >> +- 20); ++ len += scnprintf(buf + len, buf_len - len, "tx_flow_no = %lu\n", ++ FIELD_GET(HTT_MSDU_FLOW_STATS_TX_FLOW_NO, ++ htt_stats_buf->tx_flow_no__tid_num__drop_rule)); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %lu\n", ++ FIELD_GET(HTT_MSDU_FLOW_STATS_TID_NUM, ++ htt_stats_buf->tx_flow_no__tid_num__drop_rule)); ++ len += scnprintf(buf + len, buf_len - len, "drop_rule = %lu\n", ++ FIELD_GET(HTT_MSDU_FLOW_STATS_DROP_RULE, ++ htt_stats_buf->tx_flow_no__tid_num__drop_rule)); + len += scnprintf(buf + len, buf_len - len, "last_cycle_enqueue_count = %u\n", + htt_stats_buf->last_cycle_enqueue_count); + len += scnprintf(buf + len, buf_len - len, "last_cycle_dequeue_count = %u\n", +@@ -516,15 +518,18 @@ static inline void htt_print_tx_tid_stat + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TID_STATS_TLV:\n"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += scnprintf(buf + len, buf_len - len, "tid_name = %s\n", tid_name); +- len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", +- htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", +- (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "num_sched_pending = %u\n", +- htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "num_ppdu_in_hwq = %u\n", +- (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & +- 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_SW_PEER_ID, ++ htt_stats_buf->sw_peer_id__tid_num)); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_TID_NUM, ++ htt_stats_buf->sw_peer_id__tid_num)); ++ len += scnprintf(buf + len, buf_len - len, "num_sched_pending = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_NUM_SCHED_PENDING, ++ htt_stats_buf->num_sched_pending__num_ppdu_in_hwq)); ++ len += scnprintf(buf + len, buf_len - len, "num_ppdu_in_hwq = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_NUM_PPDU_IN_HWQ, ++ htt_stats_buf->num_sched_pending__num_ppdu_in_hwq)); + len += scnprintf(buf + len, buf_len - len, "tid_flags = 0x%x\n", + htt_stats_buf->tid_flags); + len += scnprintf(buf + len, buf_len - len, "hw_queued = %u\n", +@@ -566,15 +571,18 @@ static inline void htt_print_tx_tid_stat + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TID_STATS_V1_TLV:\n"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += scnprintf(buf + len, buf_len - len, "tid_name = %s\n", tid_name); +- len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", +- htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", +- (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "num_sched_pending = %u\n", +- htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "num_ppdu_in_hwq = %u\n", +- (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & +- 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_V1_SW_PEER_ID, ++ htt_stats_buf->sw_peer_id__tid_num)); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_V1_TID_NUM, ++ htt_stats_buf->sw_peer_id__tid_num)); ++ len += scnprintf(buf + len, buf_len - len, "num_sched_pending = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_V1_NUM_SCHED_PENDING, ++ htt_stats_buf->num_sched_pending__num_ppdu_in_hwq)); ++ len += scnprintf(buf + len, buf_len - len, "num_ppdu_in_hwq = %lu\n", ++ FIELD_GET(HTT_TX_TID_STATS_V1_NUM_PPDU_IN_HWQ, ++ htt_stats_buf->num_sched_pending__num_ppdu_in_hwq)); + len += scnprintf(buf + len, buf_len - len, "tid_flags = 0x%x\n", + htt_stats_buf->tid_flags); + len += scnprintf(buf + len, buf_len - len, "max_qdepth_bytes = %u\n", +@@ -618,10 +626,12 @@ static inline void htt_print_rx_tid_stat + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_TID_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", +- htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "tid_num = %u\n", +- (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %lu\n", ++ FIELD_GET(HTT_RX_TID_STATS_SW_PEER_ID, ++ htt_stats_buf->sw_peer_id__tid_num)); ++ len += scnprintf(buf + len, buf_len - len, "tid_num = %lu\n", ++ FIELD_GET(HTT_RX_TID_STATS_TID_NUM, ++ htt_stats_buf->sw_peer_id__tid_num)); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += scnprintf(buf + len, buf_len - len, "tid_name = %s\n", tid_name); + len += scnprintf(buf + len, buf_len - len, "dup_in_reorder = %u\n", +@@ -724,20 +734,29 @@ static inline void htt_print_peer_detail + htt_stats_buf->peer_type); + len += scnprintf(buf + len, buf_len - len, "sw_peer_id = %u\n", + htt_stats_buf->sw_peer_id); +- len += scnprintf(buf + len, buf_len - len, "vdev_id = %u\n", +- htt_stats_buf->vdev_pdev_ast_idx & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "pdev_id = %u\n", +- (htt_stats_buf->vdev_pdev_ast_idx & 0xFF00) >> 8); +- len += scnprintf(buf + len, buf_len - len, "ast_idx = %u\n", +- (htt_stats_buf->vdev_pdev_ast_idx & 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, +- "mac_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", +- htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF, +- (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF00) >> 8, +- (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF0000) >> 16, +- (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF000000) >> 24, +- (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF), +- (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "vdev_id = %lu\n", ++ FIELD_GET(HTT_PEER_DETAILS_VDEV_ID, ++ htt_stats_buf->vdev_pdev_ast_idx)); ++ len += scnprintf(buf + len, buf_len - len, "pdev_id = %lu\n", ++ FIELD_GET(HTT_PEER_DETAILS_PDEV_ID, ++ htt_stats_buf->vdev_pdev_ast_idx)); ++ len += scnprintf(buf + len, buf_len - len, "ast_idx = %lu\n", ++ FIELD_GET(HTT_PEER_DETAILS_AST_IDX, ++ htt_stats_buf->vdev_pdev_ast_idx)); ++ len += scnprintf(buf + len, buf_len - len, ++ "mac_addr = %02lx:%02lx:%02lx:%02lx:%02lx:%02lx\n", ++ FIELD_GET(HTT_MAC_ADDR_L32_0, ++ htt_stats_buf->mac_addr.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_L32_1, ++ htt_stats_buf->mac_addr.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_L32_2, ++ htt_stats_buf->mac_addr.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_L32_3, ++ htt_stats_buf->mac_addr.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_H16_0, ++ htt_stats_buf->mac_addr.mac_addr_h16), ++ FIELD_GET(HTT_MAC_ADDR_H16_1, ++ htt_stats_buf->mac_addr.mac_addr_h16)); + len += scnprintf(buf + len, buf_len - len, "peer_flags = 0x%x\n", + htt_stats_buf->peer_flags); + len += scnprintf(buf + len, buf_len - len, "qpeer_flags = 0x%x\n\n", +@@ -799,7 +818,6 @@ static inline void htt_print_tx_peer_rat + buf[len] = 0; + + stats_req->buf_len = len; +- + } + + static inline void htt_print_rx_peer_rate_stats_tlv(const void *tag_buf, +@@ -930,10 +948,12 @@ htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(c + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_CMN_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__hwq_id__word & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "hwq_id = %u\n\n", +- (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_TX_HWQ_STATS_MAC_ID, ++ htt_stats_buf->mac_id__hwq_id__word)); ++ len += scnprintf(buf + len, buf_len - len, "hwq_id = %lu\n\n", ++ FIELD_GET(HTT_TX_HWQ_STATS_HWQ_ID, ++ htt_stats_buf->mac_id__hwq_id__word)); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -953,10 +973,12 @@ htt_print_tx_hwq_stats_cmn_tlv(const voi + + /* TODO: HKDBG */ + len += scnprintf(buf + len, buf_len - len, "HTT_TX_HWQ_STATS_CMN_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__hwq_id__word & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "hwq_id = %u\n", +- (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_TX_HWQ_STATS_MAC_ID, ++ htt_stats_buf->mac_id__hwq_id__word)); ++ len += scnprintf(buf + len, buf_len - len, "hwq_id = %lu\n", ++ FIELD_GET(HTT_TX_HWQ_STATS_HWQ_ID, ++ htt_stats_buf->mac_id__hwq_id__word)); + len += scnprintf(buf + len, buf_len - len, "xretry = %u\n", + htt_stats_buf->xretry); + len += scnprintf(buf + len, buf_len - len, "underrun_cnt = %u\n", +@@ -1281,8 +1303,8 @@ htt_print_tx_selfgen_cmn_stats_tlv(const + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_SELFGEN_CMN_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "su_bar = %u\n", + htt_stats_buf->su_bar); + len += scnprintf(buf + len, buf_len - len, "rts = %u\n", +@@ -1769,10 +1791,12 @@ htt_print_tx_pdev_stats_sched_per_txq_tl + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__txq_id__word & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "txq_id = %u\n", +- (htt_stats_buf->mac_id__txq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID, ++ htt_stats_buf->mac_id__txq_id__word)); ++ len += scnprintf(buf + len, buf_len - len, "txq_id = %lu\n", ++ FIELD_GET(HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID, ++ htt_stats_buf->mac_id__txq_id__word)); + len += scnprintf(buf + len, buf_len - len, "sched_policy = %u\n", + htt_stats_buf->sched_policy); + len += scnprintf(buf + len, buf_len - len, +@@ -1833,8 +1857,8 @@ static inline void htt_print_stats_tx_sc + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_TX_SCHED_CMN_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "current_timestamp = %u\n\n", + htt_stats_buf->current_timestamp); + +@@ -2011,8 +2035,8 @@ static inline void htt_print_tx_tqm_cmn_ + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_CMN_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "max_cmdq_id = %u\n", + htt_stats_buf->max_cmdq_id); + len += scnprintf(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u\n", +@@ -2069,10 +2093,12 @@ static inline void htt_print_tx_tqm_cmdq + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_CMDQ_STATUS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__cmdq_id__word & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "cmdq_id = %u\n\n", +- (htt_stats_buf->mac_id__cmdq_id__word & 0xFF00) >> 8); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_TX_TQM_CMDQ_STATUS_MAC_ID, ++ htt_stats_buf->mac_id__cmdq_id__word)); ++ len += scnprintf(buf + len, buf_len - len, "cmdq_id = %lu\n\n", ++ FIELD_GET(HTT_TX_TQM_CMDQ_STATUS_CMDQ_ID, ++ htt_stats_buf->mac_id__cmdq_id__word)); + len += scnprintf(buf + len, buf_len - len, "sync_cmd = %u\n", + htt_stats_buf->sync_cmd); + len += scnprintf(buf + len, buf_len - len, "write_cmd = %u\n", +@@ -2417,8 +2443,8 @@ htt_print_tx_de_cmn_stats_tlv(const void + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_DE_CMN_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "tcl2fw_entry_count = %u\n", + htt_stats_buf->tcl2fw_entry_count); + len += scnprintf(buf + len, buf_len - len, "not_to_fw = %u\n", +@@ -2453,26 +2479,32 @@ static inline void htt_print_ring_if_sta + htt_stats_buf->base_addr); + len += scnprintf(buf + len, buf_len - len, "elem_size = %u\n", + htt_stats_buf->elem_size); +- len += scnprintf(buf + len, buf_len - len, "num_elems = %u\n", +- htt_stats_buf->num_elems__prefetch_tail_idx & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "prefetch_tail_idx = %u\n", +- (htt_stats_buf->num_elems__prefetch_tail_idx & +- 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "head_idx = %u\n", +- htt_stats_buf->head_idx__tail_idx & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "tail_idx = %u\n", +- (htt_stats_buf->head_idx__tail_idx & 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "shadow_head_idx = %u\n", +- htt_stats_buf->shadow_head_idx__shadow_tail_idx & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "shadow_tail_idx = %u\n", +- (htt_stats_buf->shadow_head_idx__shadow_tail_idx & +- 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "num_elems = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_NUM_ELEMS, ++ htt_stats_buf->num_elems__prefetch_tail_idx)); ++ len += scnprintf(buf + len, buf_len - len, "prefetch_tail_idx = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_PREFETCH_TAIL_INDEX, ++ htt_stats_buf->num_elems__prefetch_tail_idx)); ++ len += scnprintf(buf + len, buf_len - len, "head_idx = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_HEAD_IDX, ++ htt_stats_buf->head_idx__tail_idx)); ++ len += scnprintf(buf + len, buf_len - len, "tail_idx = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_TAIL_IDX, ++ htt_stats_buf->head_idx__tail_idx)); ++ len += scnprintf(buf + len, buf_len - len, "shadow_head_idx = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_SHADOW_HEAD_IDX, ++ htt_stats_buf->shadow_head_idx__shadow_tail_idx)); ++ len += scnprintf(buf + len, buf_len - len, "shadow_tail_idx = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_SHADOW_TAIL_IDX, ++ htt_stats_buf->shadow_head_idx__shadow_tail_idx)); + len += scnprintf(buf + len, buf_len - len, "num_tail_incr = %u\n", + htt_stats_buf->num_tail_incr); +- len += scnprintf(buf + len, buf_len - len, "lwm_thresh = %u\n", +- htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "hwm_thresh = %u\n", +- (htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "lwm_thresh = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_LWM_THRESH, ++ htt_stats_buf->lwm_thresh__hwm_thresh)); ++ len += scnprintf(buf + len, buf_len - len, "hwm_thresh = %lu\n", ++ FIELD_GET(HTT_RING_IF_STATS_HWM_THRESH, ++ htt_stats_buf->lwm_thresh__hwm_thresh)); + len += scnprintf(buf + len, buf_len - len, "overrun_hit_count = %u\n", + htt_stats_buf->overrun_hit_count); + len += scnprintf(buf + len, buf_len - len, "underrun_hit_count = %u\n", +@@ -2504,8 +2536,8 @@ static inline void htt_print_ring_if_cmn + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_RING_IF_CMN_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "num_records = %u\n\n", + htt_stats_buf->num_records); + +@@ -2581,8 +2613,8 @@ static inline void htt_print_sfm_cmn_tlv + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_SFM_CMN_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "buf_total = %u\n", + htt_stats_buf->buf_total); + len += scnprintf(buf + len, buf_len - len, "mem_empty = %u\n", +@@ -2609,14 +2641,18 @@ static inline void htt_print_sring_stats + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_SRING_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF); +- len += scnprintf(buf + len, buf_len - len, "ring_id = %u\n", +- (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF00) >> 8); +- len += scnprintf(buf + len, buf_len - len, "arena = %u\n", +- (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "ep = %u\n", +- (htt_stats_buf->mac_id__ring_id__arena__ep & 0x1000000) >> 24); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_MAC_ID, ++ htt_stats_buf->mac_id__ring_id__arena__ep)); ++ len += scnprintf(buf + len, buf_len - len, "ring_id = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_RING_ID, ++ htt_stats_buf->mac_id__ring_id__arena__ep)); ++ len += scnprintf(buf + len, buf_len - len, "arena = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_ARENA, ++ htt_stats_buf->mac_id__ring_id__arena__ep)); ++ len += scnprintf(buf + len, buf_len - len, "ep = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_EP, ++ htt_stats_buf->mac_id__ring_id__arena__ep)); + len += scnprintf(buf + len, buf_len - len, "base_addr_lsb = 0x%x\n", + htt_stats_buf->base_addr_lsb); + len += scnprintf(buf + len, buf_len - len, "base_addr_msb = 0x%x\n", +@@ -2625,25 +2661,30 @@ static inline void htt_print_sring_stats + htt_stats_buf->ring_size); + len += scnprintf(buf + len, buf_len - len, "elem_size = %u\n", + htt_stats_buf->elem_size); +- len += scnprintf(buf + len, buf_len - len, "num_avail_words = %u\n", +- htt_stats_buf->num_avail_words__num_valid_words & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "num_valid_words = %u\n", +- (htt_stats_buf->num_avail_words__num_valid_words & +- 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "head_ptr = %u\n", +- htt_stats_buf->head_ptr__tail_ptr & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "tail_ptr = %u\n", +- (htt_stats_buf->head_ptr__tail_ptr & 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "consumer_empty = %u\n", +- htt_stats_buf->consumer_empty__producer_full & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "producer_full = %u\n", +- (htt_stats_buf->consumer_empty__producer_full & +- 0xFFFF0000) >> 16); +- len += scnprintf(buf + len, buf_len - len, "prefetch_count = %u\n", +- htt_stats_buf->prefetch_count__internal_tail_ptr & 0xFFFF); +- len += scnprintf(buf + len, buf_len - len, "internal_tail_ptr = %u\n\n", +- (htt_stats_buf->prefetch_count__internal_tail_ptr & +- 0xFFFF0000) >> 16); ++ len += scnprintf(buf + len, buf_len - len, "num_avail_words = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_NUM_AVAIL_WORDS, ++ htt_stats_buf->num_avail_words__num_valid_words)); ++ len += scnprintf(buf + len, buf_len - len, "num_valid_words = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_NUM_VALID_WORDS, ++ htt_stats_buf->num_avail_words__num_valid_words)); ++ len += scnprintf(buf + len, buf_len - len, "head_ptr = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_HEAD_PTR, ++ htt_stats_buf->head_ptr__tail_ptr)); ++ len += scnprintf(buf + len, buf_len - len, "tail_ptr = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_TAIL_PTR, ++ htt_stats_buf->head_ptr__tail_ptr)); ++ len += scnprintf(buf + len, buf_len - len, "consumer_empty = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_CONSUMER_EMPTY, ++ htt_stats_buf->consumer_empty__producer_full)); ++ len += scnprintf(buf + len, buf_len - len, "producer_full = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_PRODUCER_FULL, ++ htt_stats_buf->consumer_empty__producer_full)); ++ len += scnprintf(buf + len, buf_len - len, "prefetch_count = %lu\n", ++ FIELD_GET(HTT_SRING_STATS_PREFETCH_COUNT, ++ htt_stats_buf->prefetch_count__internal_tail_ptr)); ++ len += scnprintf(buf + len, buf_len - len, "internal_tail_ptr = %lu\n\n", ++ FIELD_GET(HTT_SRING_STATS_INTERNAL_TAIL_PTR, ++ htt_stats_buf->prefetch_count__internal_tail_ptr)); + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2683,8 +2724,8 @@ static inline void htt_print_tx_pdev_rat + u8 j; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n", + htt_stats_buf->tx_ldpc); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u\n", +@@ -2809,8 +2850,8 @@ static inline void htt_print_rx_pdev_rat + u8 i, j; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "nsts = %u\n", + htt_stats_buf->nsts); + len += scnprintf(buf + len, buf_len - len, "rx_ldpc = %u\n", +@@ -2844,7 +2885,6 @@ static inline void htt_print_rx_pdev_rat + htt_stats_buf->pilot_count); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { +- + len += scnprintf(buf + len, buf_len - len, + "pilot_evm_db[%u] = ", j); + for (i = 0; i < HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS; i++) +@@ -3173,8 +3213,8 @@ static inline void htt_print_rx_pdev_fw_ + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "ppdu_recvd = %u\n", + htt_stats_buf->ppdu_recvd); + len += scnprintf(buf + len, buf_len - len, "mpdu_cnt_fcs_ok = %u\n", +@@ -3422,8 +3462,8 @@ static inline void htt_print_hw_stats_wh + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_WHAL_TX_TLV:\n"); +- len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", +- htt_stats_buf->mac_id__word & 0xFF); ++ len += scnprintf(buf + len, buf_len - len, "mac_id = %lu\n", ++ FIELD_GET(HTT_STATS_MAC_ID, htt_stats_buf->mac_id__word)); + len += scnprintf(buf + len, buf_len - len, "last_unpause_ppdu_id = %u\n", + htt_stats_buf->last_unpause_ppdu_id); + len += scnprintf(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u\n", +@@ -3492,13 +3532,19 @@ htt_print_pdev_stats_twt_session_tlv(con + len += scnprintf(buf + len, buf_len - len, "vdev_id = %u\n", + htt_stats_buf->vdev_id); + len += scnprintf(buf + len, buf_len - len, +- "peer_mac = %02x:%02x:%02x:%02x:%02x:%02x\n", +- htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF, +- (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF00) >> 8, +- (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF0000) >> 16, +- (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF000000) >> 24, +- (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF), +- (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF00) >> 8); ++ "peer_mac = %02lx:%02lx:%02lx:%02lx:%02lx:%02lx\n", ++ FIELD_GET(HTT_MAC_ADDR_L32_0, ++ htt_stats_buf->peer_mac.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_L32_1, ++ htt_stats_buf->peer_mac.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_L32_2, ++ htt_stats_buf->peer_mac.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_L32_3, ++ htt_stats_buf->peer_mac.mac_addr_l32), ++ FIELD_GET(HTT_MAC_ADDR_H16_0, ++ htt_stats_buf->peer_mac.mac_addr_h16), ++ FIELD_GET(HTT_MAC_ADDR_H16_1, ++ htt_stats_buf->peer_mac.mac_addr_h16)); + len += scnprintf(buf + len, buf_len - len, "flow_id_flags = %u\n", + htt_stats_buf->flow_id_flags); + len += scnprintf(buf + len, buf_len - len, "dialog_id = %u\n", +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h +@@ -137,6 +137,8 @@ struct htt_stats_string_tlv { + u32 data[0]; /* Can be variable length */ + } __packed; + ++#define HTT_STATS_MAC_ID GENMASK(7, 0) ++ + /* == TX PDEV STATS == */ + struct htt_tx_pdev_stats_cmn_tlv { + u32 mac_id__word; +@@ -290,6 +292,10 @@ struct htt_hw_stats_whal_tx_tlv { + }; + + /* ============ PEER STATS ============ */ ++#define HTT_MSDU_FLOW_STATS_TX_FLOW_NO GENMASK(15, 0) ++#define HTT_MSDU_FLOW_STATS_TID_NUM GENMASK(19, 16) ++#define HTT_MSDU_FLOW_STATS_DROP_RULE BIT(20) ++ + struct htt_msdu_flow_stats_tlv { + u32 last_update_timestamp; + u32 last_add_timestamp; +@@ -306,6 +312,11 @@ struct htt_msdu_flow_stats_tlv { + + #define MAX_HTT_TID_NAME 8 + ++#define HTT_TX_TID_STATS_SW_PEER_ID GENMASK(15, 0) ++#define HTT_TX_TID_STATS_TID_NUM GENMASK(31, 16) ++#define HTT_TX_TID_STATS_NUM_SCHED_PENDING GENMASK(7, 0) ++#define HTT_TX_TID_STATS_NUM_PPDU_IN_HWQ GENMASK(15, 8) ++ + /* Tidq stats */ + struct htt_tx_tid_stats_tlv { + /* Stored as little endian */ +@@ -326,6 +337,11 @@ struct htt_tx_tid_stats_tlv { + u32 tid_tx_airtime; + }; + ++#define HTT_TX_TID_STATS_V1_SW_PEER_ID GENMASK(15, 0) ++#define HTT_TX_TID_STATS_V1_TID_NUM GENMASK(31, 16) ++#define HTT_TX_TID_STATS_V1_NUM_SCHED_PENDING GENMASK(7, 0) ++#define HTT_TX_TID_STATS_V1_NUM_PPDU_IN_HWQ GENMASK(15, 8) ++ + /* Tidq stats */ + struct htt_tx_tid_stats_v1_tlv { + /* Stored as little endian */ +@@ -348,6 +364,9 @@ struct htt_tx_tid_stats_v1_tlv { + u32 sendn_frms_allowed; + }; + ++#define HTT_RX_TID_STATS_SW_PEER_ID GENMASK(15, 0) ++#define HTT_RX_TID_STATS_TID_NUM GENMASK(31, 16) ++ + struct htt_rx_tid_stats_tlv { + u32 sw_peer_id__tid_num; + u8 tid_name[MAX_HTT_TID_NAME]; +@@ -386,6 +405,10 @@ struct htt_peer_stats_cmn_tlv { + u32 inactive_time; + }; + ++#define HTT_PEER_DETAILS_VDEV_ID GENMASK(7, 0) ++#define HTT_PEER_DETAILS_PDEV_ID GENMASK(15, 8) ++#define HTT_PEER_DETAILS_AST_IDX GENMASK(31, 16) ++ + struct htt_peer_details_tlv { + u32 peer_type; + u32 sw_peer_id; +@@ -510,6 +533,9 @@ struct htt_tx_hwq_mu_mimo_mpdu_stats_tlv + u32 mu_mimo_ampdu_underrun_usr; + }; + ++#define HTT_TX_HWQ_STATS_MAC_ID GENMASK(7, 0) ++#define HTT_TX_HWQ_STATS_HWQ_ID GENMASK(15, 8) ++ + struct htt_tx_hwq_mu_mimo_cmn_stats_tlv { + u32 mac_id__hwq_id__word; + }; +@@ -789,6 +815,9 @@ struct htt_sched_txq_sched_ineligibility + u32 sched_ineligibility[0]; + }; + ++#define HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) ++#define HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8) ++ + struct htt_tx_pdev_stats_sched_per_txq_tlv { + u32 mac_id__txq_id__word; + u32 sched_policy; +@@ -910,6 +939,9 @@ struct htt_tx_tqm_error_stats_tlv { + }; + + /* == TQM CMDQ stats == */ ++#define HTT_TX_TQM_CMDQ_STATUS_MAC_ID GENMASK(7, 0) ++#define HTT_TX_TQM_CMDQ_STATUS_CMDQ_ID GENMASK(15, 8) ++ + struct htt_tx_tqm_cmdq_status_tlv { + u32 mac_id__cmdq_id__word; + u32 sync_cmd; +@@ -1055,6 +1087,15 @@ struct htt_tx_de_cmn_stats_tlv { + #define HTT_STATS_LOW_WM_BINS 5 + #define HTT_STATS_HIGH_WM_BINS 5 + ++#define HTT_RING_IF_STATS_NUM_ELEMS GENMASK(15, 0) ++#define HTT_RING_IF_STATS_PREFETCH_TAIL_INDEX GENMASK(31, 16) ++#define HTT_RING_IF_STATS_HEAD_IDX GENMASK(15, 0) ++#define HTT_RING_IF_STATS_TAIL_IDX GENMASK(31, 16) ++#define HTT_RING_IF_STATS_SHADOW_HEAD_IDX GENMASK(15, 0) ++#define HTT_RING_IF_STATS_SHADOW_TAIL_IDX GENMASK(31, 16) ++#define HTT_RING_IF_STATS_LWM_THRESH GENMASK(15, 0) ++#define HTT_RING_IF_STATS_HWM_THRESH GENMASK(31, 16) ++ + struct htt_ring_if_stats_tlv { + u32 base_addr; /* DWORD aligned base memory address of the ring */ + u32 elem_size; +@@ -1117,6 +1158,19 @@ struct htt_sfm_cmn_tlv { + }; + + /* == SRNG STATS == */ ++#define HTT_SRING_STATS_MAC_ID GENMASK(7, 0) ++#define HTT_SRING_STATS_RING_ID GENMASK(15, 8) ++#define HTT_SRING_STATS_ARENA GENMASK(23, 16) ++#define HTT_SRING_STATS_EP BIT(24) ++#define HTT_SRING_STATS_NUM_AVAIL_WORDS GENMASK(15, 0) ++#define HTT_SRING_STATS_NUM_VALID_WORDS GENMASK(31, 16) ++#define HTT_SRING_STATS_HEAD_PTR GENMASK(15, 0) ++#define HTT_SRING_STATS_TAIL_PTR GENMASK(31, 16) ++#define HTT_SRING_STATS_CONSUMER_EMPTY GENMASK(15, 0) ++#define HTT_SRING_STATS_PRODUCER_FULL GENMASK(31, 16) ++#define HTT_SRING_STATS_PREFETCH_COUNT GENMASK(15, 0) ++#define HTT_SRING_STATS_INTERNAL_TAIL_PTR GENMASK(31, 16) ++ + struct htt_sring_stats_tlv { + u32 mac_id__ring_id__arena__ep; + u32 base_addr_lsb; /* DWORD aligned base memory address of the ring */ +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -1594,6 +1594,13 @@ struct ath11k_htt_extd_stats_msg { + u8 data[0]; + } __packed; + ++#define HTT_MAC_ADDR_L32_0 GENMASK(7, 0) ++#define HTT_MAC_ADDR_L32_1 GENMASK(15, 8) ++#define HTT_MAC_ADDR_L32_2 GENMASK(23, 16) ++#define HTT_MAC_ADDR_L32_3 GENMASK(31, 24) ++#define HTT_MAC_ADDR_H16_0 GENMASK(7, 0) ++#define HTT_MAC_ADDR_H16_1 GENMASK(15, 8) ++ + struct htt_mac_addr { + u32 mac_addr_l32; + u32 mac_addr_h16; diff --git a/package/kernel/mac80211/patches/ath11k/0046-ath11k-add-HTT-stats-support-for-new-stats.patch b/package/kernel/mac80211/patches/ath11k/0046-ath11k-add-HTT-stats-support-for-new-stats.patch new file mode 100644 index 00000000000000..b5d2d829f9a0ca --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0046-ath11k-add-HTT-stats-support-for-new-stats.patch @@ -0,0 +1,677 @@ +From ac83b6034cfa3bec010c1e01d6e6b44673135afe Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Tue, 28 Sep 2021 14:00:45 +0300 +Subject: [PATCH] ath11k: add HTT stats support for new stats + +Add HTT stats support for, + +29-ATH11K_DBG_HTT_EXT_STATS_PEER_CTRL_PATH_TXRX_STATS: +Used to dump the control path txrx stats for each connected peer. +Usage: +echo 29 > /sys/kernel/debug/ieee80211/phyx/ath11k/htt_stats_type +cat /sys/kernel/debug/ieee80211/phyx/netdev\:wlan0/stations/ +/htt_peer_stats. + +31-ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF_STATS: +Used to dump the per pdev tx rate txbf stats. +Usage: +echo 31 > /sys/kernel/debug/ieee80211/phyx/ath11k/htt_stats_type +cat /sys/kernel/debug/ieee80211/phyx/ath11k/htt_stats + +32-ATH11k_DBG_HTT_EXT_STATS_TXBF_OFDMA: +Used to dump the TXBF ofdma stats for all ofdma users. +Usage: +echo 32 > /sys/kernel/debug/ieee80211/phyx/ath11k/htt_stats_type +cat /sys/kernel/debug/ieee80211/phyx/ath11k/htt_stats + +37-ATH11K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS: +Used to dump the mac and phy txrx counts and phy stats like per chain rssi +and ANI level. +Usage: +echo 37 > /sys/kernel/debug/ieee80211/phyx/ath11k/htt_stats_type +cat /sys/kernel/debug/ieee80211/phyx/ath11k/htt_stats + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00486-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913223148.208026-6-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/debugfs.h | 4 + + .../wireless/ath/ath11k/debugfs_htt_stats.c | 368 +++++++++++++++++- + .../wireless/ath/ath11k/debugfs_htt_stats.h | 172 ++++++++ + drivers/net/wireless/ath/ath11k/debugfs_sta.c | 8 +- + 4 files changed, 548 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs.h +@@ -38,6 +38,10 @@ enum ath11k_dbg_htt_ext_stats_type { + ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, + ATH11K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, + ATH11K_DBG_HTT_EXT_STATS_RING_BACKPRESSURE_STATS = 24, ++ ATH11K_DBG_HTT_EXT_STATS_PEER_CTRL_PATH_TXRX_STATS = 29, ++ ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF_STATS = 31, ++ ATH11K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, ++ ATH11K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37, + + /* keep this last */ + ATH11K_DBG_HTT_NUM_EXT_STATS, +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +@@ -3639,6 +3639,334 @@ static inline void htt_print_backpressur + } + } + ++static inline ++void htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_pdev_txrate_txbf_stats_tlv *htt_stats_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_STATS_PDEV_TX_RATE_TXBF_STATS:\n"); ++ ++ len += scnprintf(buf + len, buf_len - len, "tx_ol_mcs = "); ++ for (i = 0; i < HTT_TX_TXBF_RATE_STATS_NUM_MCS_COUNTERS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_ol_mcs[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_ibf_mcs = "); ++ for (i = 0; i < HTT_TX_TXBF_RATE_STATS_NUM_MCS_COUNTERS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_ibf_mcs[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_txbf_mcs ="); ++ for (i = 0; i < HTT_TX_TXBF_RATE_STATS_NUM_MCS_COUNTERS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_txbf_mcs[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_ol_nss = "); ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_ol_nss[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_ibf_nss = "); ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_ibf_nss[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_txbf_nss = "); ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_txbf_nss[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_ol_bw = "); ++ for (i = 0; i < HTT_TX_TXBF_RATE_STATS_NUM_BW_COUNTERS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_ol_bw[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_ibf_bw = "); ++ for (i = 0; i < HTT_TX_TXBF_RATE_STATS_NUM_BW_COUNTERS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_ibf_bw[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\ntx_txbf_bw = "); ++ for (i = 0; i < HTT_TX_TXBF_RATE_STATS_NUM_BW_COUNTERS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "%d:%u,", i, htt_stats_buf->tx_su_txbf_bw[i]); ++ len--; ++ ++ len += scnprintf(buf + len, buf_len - len, "\n"); ++ ++ stats_req->buf_len = len; ++} ++ ++static inline ++void htt_print_txbf_ofdma_ndpa_stats_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_txbf_ofdma_ndpa_stats_tlv *htt_stats_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TXBF_OFDMA_NDPA_STATS_TLV:\n"); ++ ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) { ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndpa_queued_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndpa_queued[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndpa_tried_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndpa_tried[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndpa_flushed_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndpa_flushed[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndpa_err_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndpa_err[i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); ++ } ++ ++ stats_req->buf_len = len; ++} ++ ++static inline ++void htt_print_txbf_ofdma_ndp_stats_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_txbf_ofdma_ndp_stats_tlv *htt_stats_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TXBF_OFDMA_NDP_STATS_TLV:\n"); ++ ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) { ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndp_queued_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndp_queued[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndp_tried_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndp_tried[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndp_flushed_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndp_flushed[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_ndp_err_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_ndp_err[i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); ++ } ++ ++ stats_req->buf_len = len; ++} ++ ++static inline ++void htt_print_txbf_ofdma_brp_stats_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_txbf_ofdma_brp_stats_tlv *htt_stats_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TXBF_OFDMA_BRP_STATS_TLV:\n"); ++ ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) { ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_brpoll_queued_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_brpoll_queued[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_brpoll_tried_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_brpoll_tried[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_brpoll_flushed_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_brpoll_flushed[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_brp_err_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_brp_err[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_brp_err_num_cbf_rcvd_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_brp_err_num_cbf_rcvd[i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); ++ } ++ ++ stats_req->buf_len = len; ++} ++ ++static inline ++void htt_print_txbf_ofdma_steer_stats_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_txbf_ofdma_steer_stats_tlv *htt_stats_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_TXBF_OFDMA_STEER_STATS_TLV:\n"); ++ ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) { ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_num_ppdu_steer_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_num_ppdu_steer[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_num_ppdu_ol_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_num_ppdu_ol[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_num_usrs_prefetch_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_num_usrs_prefetch[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_num_usrs_sound_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_num_usrs_sound[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ofdma_num_usrs_force_sound_user%d = %u\n", ++ i, htt_stats_buf->ax_ofdma_num_usrs_force_sound[i]); ++ len += scnprintf(buf + len, buf_len - len, "\n"); ++ } ++ ++ stats_req->buf_len = len; ++} ++ ++static inline ++void htt_print_phy_counters_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_phy_counters_tlv *htt_stats_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ ++ len += scnprintf(buf + len, buf_len - len, "HTT_PHY_COUNTERS_TLV:\n"); ++ len += scnprintf(buf + len, buf_len - len, "rx_ofdma_timing_err_cnt = %u\n", ++ htt_stats_buf->rx_ofdma_timing_err_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_cck_fail_cnt = %u\n", ++ htt_stats_buf->rx_cck_fail_cnt); ++ len += scnprintf(buf + len, buf_len - len, "mactx_abort_cnt = %u\n", ++ htt_stats_buf->mactx_abort_cnt); ++ len += scnprintf(buf + len, buf_len - len, "macrx_abort_cnt = %u\n", ++ htt_stats_buf->macrx_abort_cnt); ++ len += scnprintf(buf + len, buf_len - len, "phytx_abort_cnt = %u\n", ++ htt_stats_buf->phytx_abort_cnt); ++ len += scnprintf(buf + len, buf_len - len, "phyrx_abort_cnt = %u\n", ++ htt_stats_buf->phyrx_abort_cnt); ++ len += scnprintf(buf + len, buf_len - len, "phyrx_defer_abort_cnt = %u\n", ++ htt_stats_buf->phyrx_defer_abort_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_gain_adj_lstf_event_cnt = %u\n", ++ htt_stats_buf->rx_gain_adj_lstf_event_cnt); ++ len += scnprintf(buf + len, buf_len - len, "rx_gain_adj_non_legacy_cnt = %u\n", ++ htt_stats_buf->rx_gain_adj_non_legacy_cnt); ++ ++ for (i = 0; i < HTT_MAX_RX_PKT_CNT; i++) ++ len += scnprintf(buf + len, buf_len - len, "rx_pkt_cnt[%d] = %u\n", ++ i, htt_stats_buf->rx_pkt_cnt[i]); ++ ++ for (i = 0; i < HTT_MAX_RX_PKT_CRC_PASS_CNT; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "rx_pkt_crc_pass_cnt[%d] = %u\n", ++ i, htt_stats_buf->rx_pkt_crc_pass_cnt[i]); ++ ++ for (i = 0; i < HTT_MAX_PER_BLK_ERR_CNT; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "per_blk_err_cnt[%d] = %u\n", ++ i, htt_stats_buf->per_blk_err_cnt[i]); ++ ++ for (i = 0; i < HTT_MAX_RX_OTA_ERR_CNT; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "rx_ota_err_cnt[%d] = %u\n", ++ i, htt_stats_buf->rx_ota_err_cnt[i]); ++ ++ stats_req->buf_len = len; ++} ++ ++static inline ++void htt_print_phy_stats_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_phy_stats_tlv *htt_stats_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ ++ len += scnprintf(buf + len, buf_len - len, "HTT_PHY_STATS_TLV:\n"); ++ ++ for (i = 0; i < HTT_STATS_MAX_CHAINS; i++) ++ len += scnprintf(buf + len, buf_len - len, "nf_chain[%d] = %d\n", ++ i, htt_stats_buf->nf_chain[i]); ++ ++ len += scnprintf(buf + len, buf_len - len, "false_radar_cnt = %u\n", ++ htt_stats_buf->false_radar_cnt); ++ len += scnprintf(buf + len, buf_len - len, "radar_cs_cnt = %u\n", ++ htt_stats_buf->radar_cs_cnt); ++ len += scnprintf(buf + len, buf_len - len, "ani_level = %d\n", ++ htt_stats_buf->ani_level); ++ len += scnprintf(buf + len, buf_len - len, "fw_run_time = %u\n", ++ htt_stats_buf->fw_run_time); ++ ++ stats_req->buf_len = len; ++} ++ ++static inline ++void htt_print_peer_ctrl_path_txrx_stats_tlv(const void *tag_buf, ++ struct debug_htt_stats_req *stats_req) ++{ ++ const struct htt_peer_ctrl_path_txrx_stats_tlv *htt_stat_buf = tag_buf; ++ u8 *buf = stats_req->buf; ++ u32 len = stats_req->buf_len; ++ u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; ++ int i; ++ const char *mgmt_frm_type[ATH11K_STATS_MGMT_FRM_TYPE_MAX - 1] = { ++ "assoc_req", "assoc_resp", ++ "reassoc_req", "reassoc_resp", ++ "probe_req", "probe_resp", ++ "timing_advertisement", "reserved", ++ "beacon", "atim", "disassoc", ++ "auth", "deauth", "action", "action_no_ack"}; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "HTT_STATS_PEER_CTRL_PATH_TXRX_STATS_TAG:\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "peer_mac_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", ++ htt_stat_buf->peer_mac_addr[0], htt_stat_buf->peer_mac_addr[1], ++ htt_stat_buf->peer_mac_addr[2], htt_stat_buf->peer_mac_addr[3], ++ htt_stat_buf->peer_mac_addr[4], htt_stat_buf->peer_mac_addr[5]); ++ ++ len += scnprintf(buf + len, buf_len - len, "peer_tx_mgmt_subtype:\n"); ++ for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX - 1; i++) ++ len += scnprintf(buf + len, buf_len - len, "%s:%u\n", ++ mgmt_frm_type[i], ++ htt_stat_buf->peer_rx_mgmt_subtype[i]); ++ ++ len += scnprintf(buf + len, buf_len - len, "peer_rx_mgmt_subtype:\n"); ++ for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX - 1; i++) ++ len += scnprintf(buf + len, buf_len - len, "%s:%u\n", ++ mgmt_frm_type[i], ++ htt_stat_buf->peer_rx_mgmt_subtype[i]); ++ ++ len += scnprintf(buf + len, buf_len - len, "\n"); ++ ++ stats_req->buf_len = len; ++} ++ + static int ath11k_dbg_htt_ext_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, const void *tag_buf, + void *user_data) +@@ -3990,6 +4318,30 @@ static int ath11k_dbg_htt_ext_stats_pars + case HTT_STATS_RING_BACKPRESSURE_STATS_TAG: + htt_print_backpressure_stats_tlv_v(tag_buf, user_data); + break; ++ case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG: ++ htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, stats_req); ++ break; ++ case HTT_STATS_TXBF_OFDMA_NDPA_STATS_TAG: ++ htt_print_txbf_ofdma_ndpa_stats_tlv(tag_buf, stats_req); ++ break; ++ case HTT_STATS_TXBF_OFDMA_NDP_STATS_TAG: ++ htt_print_txbf_ofdma_ndp_stats_tlv(tag_buf, stats_req); ++ break; ++ case HTT_STATS_TXBF_OFDMA_BRP_STATS_TAG: ++ htt_print_txbf_ofdma_brp_stats_tlv(tag_buf, stats_req); ++ break; ++ case HTT_STATS_TXBF_OFDMA_STEER_STATS_TAG: ++ htt_print_txbf_ofdma_steer_stats_tlv(tag_buf, stats_req); ++ break; ++ case HTT_STATS_PHY_COUNTERS_TAG: ++ htt_print_phy_counters_tlv(tag_buf, stats_req); ++ break; ++ case HTT_STATS_PHY_STATS_TAG: ++ htt_print_phy_stats_tlv(tag_buf, stats_req); ++ break; ++ case HTT_STATS_PEER_CTRL_PATH_TXRX_STATS_TAG: ++ htt_print_peer_ctrl_path_txrx_stats_tlv(tag_buf, stats_req); ++ break; + default: + break; + } +@@ -4077,8 +4429,7 @@ static ssize_t ath11k_write_htt_stats_ty + if (type >= ATH11K_DBG_HTT_NUM_EXT_STATS) + return -E2BIG; + +- if (type == ATH11K_DBG_HTT_EXT_STATS_RESET || +- type == ATH11K_DBG_HTT_EXT_STATS_PEER_INFO) ++ if (type == ATH11K_DBG_HTT_EXT_STATS_RESET) + return -EPERM; + + ar->debug.htt_stats.type = type; +@@ -4139,6 +4490,15 @@ static int ath11k_prep_htt_stats_cfg_par + case ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS; + break; ++ case ATH11K_DBG_HTT_EXT_STATS_PEER_CTRL_PATH_TXRX_STATS: ++ cfg_params->cfg0 = HTT_STAT_PEER_INFO_MAC_ADDR; ++ cfg_params->cfg1 |= FIELD_PREP(GENMASK(7, 0), mac_addr[0]); ++ cfg_params->cfg1 |= FIELD_PREP(GENMASK(15, 8), mac_addr[1]); ++ cfg_params->cfg1 |= FIELD_PREP(GENMASK(23, 16), mac_addr[2]); ++ cfg_params->cfg1 |= FIELD_PREP(GENMASK(31, 24), mac_addr[3]); ++ cfg_params->cfg2 |= FIELD_PREP(GENMASK(7, 0), mac_addr[4]); ++ cfg_params->cfg2 |= FIELD_PREP(GENMASK(15, 8), mac_addr[5]); ++ break; + default: + break; + } +@@ -4196,7 +4556,9 @@ static int ath11k_open_htt_stats(struct + u8 type = ar->debug.htt_stats.type; + int ret; + +- if (type == ATH11K_DBG_HTT_EXT_STATS_RESET) ++ if (type == ATH11K_DBG_HTT_EXT_STATS_RESET || ++ type == ATH11K_DBG_HTT_EXT_STATS_PEER_INFO || ++ type == ATH11K_DBG_HTT_EXT_STATS_PEER_CTRL_PATH_TXRX_STATS) + return -EPERM; + + mutex_lock(&ar->conf_mutex); +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h +@@ -102,6 +102,14 @@ enum htt_tlv_tag_t { + HTT_STATS_PDEV_OBSS_PD_TAG = 88, + HTT_STATS_HW_WAR_TAG = 89, + HTT_STATS_RING_BACKPRESSURE_STATS_TAG = 90, ++ HTT_STATS_PEER_CTRL_PATH_TXRX_STATS_TAG = 101, ++ HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG = 108, ++ HTT_STATS_TXBF_OFDMA_NDPA_STATS_TAG = 113, ++ HTT_STATS_TXBF_OFDMA_NDP_STATS_TAG = 114, ++ HTT_STATS_TXBF_OFDMA_BRP_STATS_TAG = 115, ++ HTT_STATS_TXBF_OFDMA_STEER_STATS_TAG = 116, ++ HTT_STATS_PHY_COUNTERS_TAG = 121, ++ HTT_STATS_PHY_STATS_TAG = 122, + + HTT_STATS_MAX_TAG, + }; +@@ -1750,6 +1758,170 @@ struct htt_ring_backpressure_stats_tlv { + u32 backpressure_hist[5]; + }; + ++#define HTT_TX_TXBF_RATE_STATS_NUM_MCS_COUNTERS 14 ++#define HTT_TX_TXBF_RATE_STATS_NUM_BW_COUNTERS 5 ++#define HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 ++ ++struct htt_pdev_txrate_txbf_stats_tlv { ++ /* SU TxBF TX MCS stats */ ++ u32 tx_su_txbf_mcs[HTT_TX_TXBF_RATE_STATS_NUM_MCS_COUNTERS]; ++ /* Implicit BF TX MCS stats */ ++ u32 tx_su_ibf_mcs[HTT_TX_TXBF_RATE_STATS_NUM_MCS_COUNTERS]; ++ /* Open loop TX MCS stats */ ++ u32 tx_su_ol_mcs[HTT_TX_TXBF_RATE_STATS_NUM_MCS_COUNTERS]; ++ /* SU TxBF TX NSS stats */ ++ u32 tx_su_txbf_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; ++ /* Implicit BF TX NSS stats */ ++ u32 tx_su_ibf_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; ++ /* Open loop TX NSS stats */ ++ u32 tx_su_ol_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; ++ /* SU TxBF TX BW stats */ ++ u32 tx_su_txbf_bw[HTT_TX_TXBF_RATE_STATS_NUM_BW_COUNTERS]; ++ /* Implicit BF TX BW stats */ ++ u32 tx_su_ibf_bw[HTT_TX_TXBF_RATE_STATS_NUM_BW_COUNTERS]; ++ /* Open loop TX BW stats */ ++ u32 tx_su_ol_bw[HTT_TX_TXBF_RATE_STATS_NUM_BW_COUNTERS]; ++}; ++ ++struct htt_txbf_ofdma_ndpa_stats_tlv { ++ /* 11AX HE OFDMA NDPA frame queued to the HW */ ++ u32 ax_ofdma_ndpa_queued[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA NDPA frame sent over the air */ ++ u32 ax_ofdma_ndpa_tried[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA NDPA frame flushed by HW */ ++ u32 ax_ofdma_ndpa_flushed[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA NDPA frame completed with error(s) */ ++ u32 ax_ofdma_ndpa_err[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++}; ++ ++struct htt_txbf_ofdma_ndp_stats_tlv { ++ /* 11AX HE OFDMA NDP frame queued to the HW */ ++ u32 ax_ofdma_ndp_queued[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA NDPA frame sent over the air */ ++ u32 ax_ofdma_ndp_tried[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA NDPA frame flushed by HW */ ++ u32 ax_ofdma_ndp_flushed[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA NDPA frame completed with error(s) */ ++ u32 ax_ofdma_ndp_err[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++}; ++ ++struct htt_txbf_ofdma_brp_stats_tlv { ++ /* 11AX HE OFDMA MU BRPOLL frame queued to the HW */ ++ u32 ax_ofdma_brpoll_queued[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA MU BRPOLL frame sent over the air */ ++ u32 ax_ofdma_brpoll_tried[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA MU BRPOLL frame flushed by HW */ ++ u32 ax_ofdma_brpoll_flushed[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA MU BRPOLL frame completed with error(s) */ ++ u32 ax_ofdma_brp_err[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* Number of CBF(s) received when 11AX HE OFDMA MU BRPOLL frame ++ * completed with error(s). ++ */ ++ u32 ax_ofdma_brp_err_num_cbf_rcvd[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS + 1]; ++}; ++ ++struct htt_txbf_ofdma_steer_stats_tlv { ++ /* 11AX HE OFDMA PPDUs that were sent over the air with steering (TXBF + OFDMA) */ ++ u32 ax_ofdma_num_ppdu_steer[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA PPDUs that were sent over the air in open loop */ ++ u32 ax_ofdma_num_ppdu_ol[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA number of users for which CBF prefetch was ++ * initiated to PHY HW during TX. ++ */ ++ u32 ax_ofdma_num_usrs_prefetch[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA number of users for which sounding was initiated during TX */ ++ u32 ax_ofdma_num_usrs_sound[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ /* 11AX HE OFDMA number of users for which sounding was forced during TX */ ++ u32 ax_ofdma_num_usrs_force_sound[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++}; ++ ++#define HTT_MAX_RX_PKT_CNT 8 ++#define HTT_MAX_RX_PKT_CRC_PASS_CNT 8 ++#define HTT_MAX_PER_BLK_ERR_CNT 20 ++#define HTT_MAX_RX_OTA_ERR_CNT 14 ++#define HTT_STATS_MAX_CHAINS 8 ++#define ATH11K_STATS_MGMT_FRM_TYPE_MAX 16 ++ ++struct htt_phy_counters_tlv { ++ /* number of RXTD OFDMA OTA error counts except power surge and drop */ ++ u32 rx_ofdma_timing_err_cnt; ++ /* rx_cck_fail_cnt: ++ * number of cck error counts due to rx reception failure because of ++ * timing error in cck ++ */ ++ u32 rx_cck_fail_cnt; ++ /* number of times tx abort initiated by mac */ ++ u32 mactx_abort_cnt; ++ /* number of times rx abort initiated by mac */ ++ u32 macrx_abort_cnt; ++ /* number of times tx abort initiated by phy */ ++ u32 phytx_abort_cnt; ++ /* number of times rx abort initiated by phy */ ++ u32 phyrx_abort_cnt; ++ /* number of rx defered count initiated by phy */ ++ u32 phyrx_defer_abort_cnt; ++ /* number of sizing events generated at LSTF */ ++ u32 rx_gain_adj_lstf_event_cnt; ++ /* number of sizing events generated at non-legacy LTF */ ++ u32 rx_gain_adj_non_legacy_cnt; ++ /* rx_pkt_cnt - ++ * Received EOP (end-of-packet) count per packet type; ++ * [0] = 11a; [1] = 11b; [2] = 11n; [3] = 11ac; [4] = 11ax; [5] = GF ++ * [6-7]=RSVD ++ */ ++ u32 rx_pkt_cnt[HTT_MAX_RX_PKT_CNT]; ++ /* rx_pkt_crc_pass_cnt - ++ * Received EOP (end-of-packet) count per packet type; ++ * [0] = 11a; [1] = 11b; [2] = 11n; [3] = 11ac; [4] = 11ax; [5] = GF ++ * [6-7]=RSVD ++ */ ++ u32 rx_pkt_crc_pass_cnt[HTT_MAX_RX_PKT_CRC_PASS_CNT]; ++ /* per_blk_err_cnt - ++ * Error count per error source; ++ * [0] = unknown; [1] = LSIG; [2] = HTSIG; [3] = VHTSIG; [4] = HESIG; ++ * [5] = RXTD_OTA; [6] = RXTD_FATAL; [7] = DEMF; [8] = ROBE; ++ * [9] = PMI; [10] = TXFD; [11] = TXTD; [12] = PHYRF ++ * [13-19]=RSVD ++ */ ++ u32 per_blk_err_cnt[HTT_MAX_PER_BLK_ERR_CNT]; ++ /* rx_ota_err_cnt - ++ * RXTD OTA (over-the-air) error count per error reason; ++ * [0] = voting fail; [1] = weak det fail; [2] = strong sig fail; ++ * [3] = cck fail; [4] = power surge; [5] = power drop; ++ * [6] = btcf timing timeout error; [7] = btcf packet detect error; ++ * [8] = coarse timing timeout error ++ * [9-13]=RSVD ++ */ ++ u32 rx_ota_err_cnt[HTT_MAX_RX_OTA_ERR_CNT]; ++}; ++ ++struct htt_phy_stats_tlv { ++ /* per chain hw noise floor values in dBm */ ++ s32 nf_chain[HTT_STATS_MAX_CHAINS]; ++ /* number of false radars detected */ ++ u32 false_radar_cnt; ++ /* number of channel switches happened due to radar detection */ ++ u32 radar_cs_cnt; ++ /* ani_level - ++ * ANI level (noise interference) corresponds to the channel ++ * the desense levels range from -5 to 15 in dB units, ++ * higher values indicating more noise interference. ++ */ ++ s32 ani_level; ++ /* running time in minutes since FW boot */ ++ u32 fw_run_time; ++}; ++ ++struct htt_peer_ctrl_path_txrx_stats_tlv { ++ /* peer mac address */ ++ u8 peer_mac_addr[ETH_ALEN]; ++ u8 rsvd[2]; ++ /* Num of tx mgmt frames with subtype on peer level */ ++ u32 peer_tx_mgmt_subtype[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; ++ /* Num of rx mgmt frames with subtype on peer level */ ++ u32 peer_rx_mgmt_subtype[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; ++}; ++ + #ifdef CPTCFG_ATH11K_DEBUGFS + + void ath11k_debugfs_htt_stats_init(struct ath11k *ar); +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -419,15 +419,21 @@ ath11k_dbg_sta_open_htt_peer_stats(struc + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct debug_htt_stats_req *stats_req; ++ int type = ar->debug.htt_stats.type; + int ret; + ++ if ((type != ATH11K_DBG_HTT_EXT_STATS_PEER_INFO && ++ type != ATH11K_DBG_HTT_EXT_STATS_PEER_CTRL_PATH_TXRX_STATS) || ++ type == ATH11K_DBG_HTT_EXT_STATS_RESET) ++ return -EPERM; ++ + stats_req = vzalloc(sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); + if (!stats_req) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + ar->debug.htt_stats.stats_req = stats_req; +- stats_req->type = ATH11K_DBG_HTT_EXT_STATS_PEER_INFO; ++ stats_req->type = type; + memcpy(stats_req->peer_addr, sta->addr, ETH_ALEN); + ret = ath11k_debugfs_htt_stats_req(ar); + mutex_unlock(&ar->conf_mutex); diff --git a/package/kernel/mac80211/patches/ath11k/0048-ath11k-indicate-scan-complete-for-scan-canceled-when.patch b/package/kernel/mac80211/patches/ath11k/0048-ath11k-indicate-scan-complete-for-scan-canceled-when.patch new file mode 100644 index 00000000000000..b68a200bc5e919 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0048-ath11k-indicate-scan-complete-for-scan-canceled-when.patch @@ -0,0 +1,76 @@ +From c677d4b1bcc4f7330043d8f039f494557d720ed4 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 28 Sep 2021 14:00:45 +0300 +Subject: [PATCH] ath11k: indicate scan complete for scan canceled when scan + running + +ath11k prints "Received scan event for unknown vdev" when doing the +following test: +1. trigger scan +2. wait 0.2 second +3. iw reg set or 11d scan complete from firmware + +Reason: When iw reg set or 11d scan complete, the new country code will +be set to the firmware, and the new regdomain info indicated to ath11k, +then the new channel list will be sent to the firmware. The firmware +will cancel the current scan after receiving WMI_SCAN_CHAN_LIST_CMDID +which is used for the new channel list, and the state of ath11k is +ATH11K_SCAN_RUNNING, then ath11k_get_ar_on_scan_abort() returns NULL and +ath11k_scan_event() returns at this point and does not indicate scan +completion to mac80211. + +Indicate scan completion to mac80211 and get rid of the "Received scan +event for unknown vdev" print for the above case. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210914164226.38843-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/wmi.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -6290,8 +6290,9 @@ exit: + rcu_read_unlock(); + } + +-static struct ath11k *ath11k_get_ar_on_scan_abort(struct ath11k_base *ab, +- u32 vdev_id) ++static struct ath11k *ath11k_get_ar_on_scan_state(struct ath11k_base *ab, ++ u32 vdev_id, ++ enum ath11k_scan_state state) + { + int i; + struct ath11k_pdev *pdev; +@@ -6303,7 +6304,7 @@ static struct ath11k *ath11k_get_ar_on_s + ar = pdev->ar; + + spin_lock_bh(&ar->data_lock); +- if (ar->scan.state == ATH11K_SCAN_ABORTING && ++ if (ar->scan.state == state && + ar->scan.vdev_id == vdev_id) { + spin_unlock_bh(&ar->data_lock); + return ar; +@@ -6333,10 +6334,15 @@ static void ath11k_scan_event(struct ath + * aborting scan's vdev id matches this event info. + */ + if (scan_ev.event_type == WMI_SCAN_EVENT_COMPLETED && +- scan_ev.reason == WMI_SCAN_REASON_CANCELLED) +- ar = ath11k_get_ar_on_scan_abort(ab, scan_ev.vdev_id); +- else ++ scan_ev.reason == WMI_SCAN_REASON_CANCELLED) { ++ ar = ath11k_get_ar_on_scan_state(ab, scan_ev.vdev_id, ++ ATH11K_SCAN_ABORTING); ++ if (!ar) ++ ar = ath11k_get_ar_on_scan_state(ab, scan_ev.vdev_id, ++ ATH11K_SCAN_RUNNING); ++ } else { + ar = ath11k_mac_get_ar_by_vdev_id(ab, scan_ev.vdev_id); ++ } + + if (!ar) { + ath11k_warn(ab, "Received scan event for unknown vdev"); diff --git a/package/kernel/mac80211/patches/ath11k/0049-ath11k-indicate-to-mac80211-scan-complete-with-abort.patch b/package/kernel/mac80211/patches/ath11k/0049-ath11k-indicate-to-mac80211-scan-complete-with-abort.patch new file mode 100644 index 00000000000000..6b0ad11e94686b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0049-ath11k-indicate-to-mac80211-scan-complete-with-abort.patch @@ -0,0 +1,71 @@ +From 62db14ea95b1017c53ebb8f724119ea4d90ecc07 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 28 Sep 2021 14:00:45 +0300 +Subject: [PATCH] ath11k: indicate to mac80211 scan complete with aborted flag + for ATH11K_SCAN_STARTING state + +Scan failure can not be recovered from when running a loop of the +following steps: +1. run scan: "iw wlan scan". +2. run command: echo assert > /sys/kernel/debug/ath11k/qca6490\ hw2.0/simulate_fw_crash + immediately after step 1. + +result: +scan failed and can not recover even when wlan recovery succeeds: +command failed: Device or resource busy (-16) + +reason: +When scan arrives, WMI_START_SCAN_CMDID is sent to the firmware and +function ath11k_mac_op_hw_scan() returns, then simulate_fw_crash arrives +and the scan started event does not arrive, and then it starts to do +recovery of wlan. __ath11k_mac_scan_finish() which is called from +ath11k_core_halt() is one step of recovery, it will not call +ieee80211_scan_completed() by logic currently because the scan state is +ATH11K_SCAN_STARTING. Thus it leads the scan not being completed in +mac80211, and leads all consecutive scans failing with -EBUSY in +nl80211_trigger_scan even after wlan recovery success. + +Indicate scan complete with aborted flag to mac80211 for +ATH11K_SCAN_STARTING to allow recovery from scan failed with "Device or +resource busy (-16)" after wlan recovery. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210914164226.38843-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2987,18 +2987,21 @@ void __ath11k_mac_scan_finish(struct ath + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: ++ if (ar->scan.is_roc && ar->scan.roc_notify) ++ ieee80211_remain_on_channel_expired(ar->hw); ++ fallthrough; ++ case ATH11K_SCAN_STARTING: + if (!ar->scan.is_roc) { + struct cfg80211_scan_info info = { +- .aborted = (ar->scan.state == +- ATH11K_SCAN_ABORTING), ++ .aborted = ((ar->scan.state == ++ ATH11K_SCAN_ABORTING) || ++ (ar->scan.state == ++ ATH11K_SCAN_STARTING)), + }; + + ieee80211_scan_completed(ar->hw, &info); +- } else if (ar->scan.roc_notify) { +- ieee80211_remain_on_channel_expired(ar->hw); + } +- fallthrough; +- case ATH11K_SCAN_STARTING: ++ + ar->scan.state = ATH11K_SCAN_IDLE; + ar->scan_channel = NULL; + ar->scan.roc_freq = 0; diff --git a/package/kernel/mac80211/patches/ath11k/0050-ath11k-add-6-GHz-params-in-peer-assoc-command.patch b/package/kernel/mac80211/patches/ath11k/0050-ath11k-add-6-GHz-params-in-peer-assoc-command.patch new file mode 100644 index 00000000000000..4339ffb7098ed5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0050-ath11k-add-6-GHz-params-in-peer-assoc-command.patch @@ -0,0 +1,94 @@ +From c3a7d7eb4c9853bb457b792cef42ddd4a029a914 Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Tue, 28 Sep 2021 14:00:46 +0300 +Subject: [PATCH] ath11k: add 6 GHz params in peer assoc command + +Currently A-MPDU aggregation parameters are not being configured +during peer association for 6 GHz band. Hence, extract these +parameters from station's capabilities received in association +request and send to firmware. Without this, A-MPDU aggregation +is not happening in 6 GHz band. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01386-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913175510.193005-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 50 ++++++++++++++++++++++++++- + 1 file changed, 49 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2021,6 +2021,53 @@ static void ath11k_peer_assoc_h_he(struc + arg->peer_bw_rxnss_override); + } + ++static void ath11k_peer_assoc_h_he_6ghz(struct ath11k *ar, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ struct peer_assoc_params *arg) ++{ ++ const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; ++ struct cfg80211_chan_def def; ++ enum nl80211_band band; ++ u8 ampdu_factor; ++ ++ if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) ++ return; ++ ++ band = def.chan->band; ++ ++ if (!arg->he_flag || band != NL80211_BAND_6GHZ || !sta->he_6ghz_capa.capa) ++ return; ++ ++ if (sta->bandwidth == IEEE80211_STA_RX_BW_80) ++ arg->bw_80 = true; ++ ++ if (sta->bandwidth == IEEE80211_STA_RX_BW_160) ++ arg->bw_160 = true; ++ ++ arg->peer_he_caps_6ghz = le16_to_cpu(sta->he_6ghz_capa.capa); ++ arg->peer_mpdu_density = ++ ath11k_parse_mpdudensity(FIELD_GET(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START, ++ arg->peer_he_caps_6ghz)); ++ ++ /* From IEEE Std 802.11ax-2021 - Section 10.12.2: An HE STA shall be capable of ++ * receiving A-MPDU where the A-MPDU pre-EOF padding length is up to the value ++ * indicated by the Maximum A-MPDU Length Exponent Extension field in the HE ++ * Capabilities element and the Maximum A-MPDU Length Exponent field in HE 6 GHz ++ * Band Capabilities element in the 6 GHz band. ++ * ++ * Here, we are extracting the Max A-MPDU Exponent Extension from HE caps and ++ * factor is the Maximum A-MPDU Length Exponent from HE 6 GHZ Band capability. ++ */ ++ ampdu_factor = FIELD_GET(IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK, ++ he_cap->he_cap_elem.mac_cap_info[3]) + ++ FIELD_GET(IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP, ++ arg->peer_he_caps_6ghz); ++ ++ arg->peer_max_mpdu = (1u << (IEEE80211_HE_6GHZ_MAX_AMPDU_FACTOR + ++ ampdu_factor)) - 1; ++} ++ + static void ath11k_peer_assoc_h_smps(struct ieee80211_sta *sta, + struct peer_assoc_params *arg) + { +@@ -2310,6 +2357,7 @@ static void ath11k_peer_assoc_prepare(st + ath11k_peer_assoc_h_ht(ar, vif, sta, arg); + ath11k_peer_assoc_h_vht(ar, vif, sta, arg); + ath11k_peer_assoc_h_he(ar, vif, sta, arg); ++ ath11k_peer_assoc_h_he_6ghz(ar, vif, sta, arg); + ath11k_peer_assoc_h_qos(ar, vif, sta, arg); + ath11k_peer_assoc_h_smps(sta, arg); + +@@ -7608,7 +7656,7 @@ static int __ath11k_mac_register(struct + if (cap->nss_ratio_enabled) + ieee80211_hw_set(ar->hw, SUPPORTS_VHT_EXT_NSS_BW); + +- if (ht_cap & WMI_HT_CAP_ENABLED) { ++ if ((ht_cap & WMI_HT_CAP_ENABLED) || ar->supports_6ghz) { + ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION); + ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(ar->hw, SUPPORTS_REORDERING_BUFFER); diff --git a/package/kernel/mac80211/patches/ath11k/0051-ath11k-support-SMPS-configuration-for-6-GHz.patch b/package/kernel/mac80211/patches/ath11k/0051-ath11k-support-SMPS-configuration-for-6-GHz.patch new file mode 100644 index 00000000000000..51827787664994 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0051-ath11k-support-SMPS-configuration-for-6-GHz.patch @@ -0,0 +1,93 @@ +From 6f4d70308e5eb63c99702e93f7c0d8e55f360da2 Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Tue, 28 Sep 2021 14:00:46 +0300 +Subject: [PATCH] ath11k: support SMPS configuration for 6 GHz + +Parse SMPS configuration from IEs and configure. Without this, +SMPS is not enabled for 6 GHz band. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01386-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913175510.193005-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 31 ++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 10 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2074,11 +2074,16 @@ static void ath11k_peer_assoc_h_smps(str + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int smps; + +- if (!ht_cap->ht_supported) ++ if (!ht_cap->ht_supported && !sta->he_6ghz_capa.capa) + return; + +- smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; +- smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; ++ if (ht_cap->ht_supported) { ++ smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; ++ smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; ++ } else { ++ smps = FIELD_GET(IEEE80211_HE_6GHZ_CAP_SM_PS, ++ le16_to_cpu(sta->he_6ghz_capa.capa)); ++ } + + switch (smps) { + case WLAN_HT_CAP_SM_PS_STATIC: +@@ -2366,15 +2371,20 @@ static void ath11k_peer_assoc_prepare(st + + static int ath11k_setup_peer_smps(struct ath11k *ar, struct ath11k_vif *arvif, + const u8 *addr, +- const struct ieee80211_sta_ht_cap *ht_cap) ++ const struct ieee80211_sta_ht_cap *ht_cap, ++ u16 he_6ghz_capa) + { + int smps; + +- if (!ht_cap->ht_supported) ++ if (!ht_cap->ht_supported && !he_6ghz_capa) + return 0; + +- smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; +- smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; ++ if (ht_cap->ht_supported) { ++ smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; ++ smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; ++ } else { ++ smps = FIELD_GET(IEEE80211_HE_6GHZ_CAP_SM_PS, he_6ghz_capa); ++ } + + if (smps >= ARRAY_SIZE(ath11k_smps_map)) + return -EINVAL; +@@ -2427,7 +2437,8 @@ static void ath11k_bss_assoc(struct ieee + } + + ret = ath11k_setup_peer_smps(ar, arvif, bss_conf->bssid, +- &ap_sta->ht_cap); ++ &ap_sta->ht_cap, ++ le16_to_cpu(ap_sta->he_6ghz_capa.capa)); + if (ret) { + ath11k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); +@@ -3720,7 +3731,7 @@ static int ath11k_station_assoc(struct a + return 0; + + ret = ath11k_setup_peer_smps(ar, arvif, sta->addr, +- &sta->ht_cap); ++ &sta->ht_cap, le16_to_cpu(sta->he_6ghz_capa.capa)); + if (ret) { + ath11k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); +@@ -7671,7 +7682,7 @@ static int __ath11k_mac_register(struct + * for each band for a dual band capable radio. It will be tricky to + * handle it when the ht capability different for each band. + */ +- if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS) ++ if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS || ar->supports_6ghz) + ar->hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS; + + ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID; diff --git a/package/kernel/mac80211/patches/ath11k/0053-ath11k-Remove-unused-variable-in-ath11k_dp_rx_mon_me.patch b/package/kernel/mac80211/patches/ath11k/0053-ath11k-Remove-unused-variable-in-ath11k_dp_rx_mon_me.patch new file mode 100644 index 00000000000000..d6a2632e370cd5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0053-ath11k-Remove-unused-variable-in-ath11k_dp_rx_mon_me.patch @@ -0,0 +1,70 @@ +From 7210b4b77fe47697890d2d3b2ce57ac9f767bffc Mon Sep 17 00:00:00 2001 +From: Tim Gardner +Date: Tue, 5 Oct 2021 16:55:54 +0300 +Subject: [PATCH] ath11k: Remove unused variable in + ath11k_dp_rx_mon_merg_msdus() + +Coverity complains that a constant variable guards dead code. In fact, +mpdu_buf is set NULL and never updated. + +4834err_merge_fail: + null: At condition mpdu_buf, the value of mpdu_buf must be NULL. + dead_error_condition: The condition mpdu_buf cannot be true. +CID 92162 (#1 of 1): 'Constant' variable guards dead code (DEADCODE) +dead_error_line: Execution cannot reach the expression decap_format != + DP_RX_DECAP_TYPE_RAW inside this statement: if (mpdu_buf && decap_forma.... +Local variable mpdu_buf is assigned only once, to a constant value, making it + effectively constant throughout its scope. If this is not the intent, examine + the logic to see if there is a missing assignment that would make mpdu_buf not + remain constant. +4835 if (mpdu_buf && decap_format != DP_RX_DECAP_TYPE_RAW) { + +Fix this by removing mpdu_buf and unreachable code. + +Cc: Kalle Valo +Cc: "David S. Miller" +Cc: Jakub Kicinski +Cc: ath11k@lists.infradead.org +Cc: linux-wireless@vger.kernel.org +Cc: netdev@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Signed-off-by: Tim Gardner +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210927150743.19816-1-tim.gardner@canonical.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 10 +--------- + 1 file changed, 1 insertion(+), 9 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -4828,7 +4828,7 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11 + struct ieee80211_rx_status *rxs) + { + struct ath11k_base *ab = ar->ab; +- struct sk_buff *msdu, *mpdu_buf, *prev_buf; ++ struct sk_buff *msdu, *prev_buf; + u32 wifi_hdr_len; + struct hal_rx_desc *rx_desc; + char *hdr_desc; +@@ -4836,8 +4836,6 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11 + struct ieee80211_hdr_3addr *wh; + struct rx_attention *rx_attention; + +- mpdu_buf = NULL; +- + if (!head_msdu) + goto err_merge_fail; + +@@ -4920,12 +4918,6 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11 + return head_msdu; + + err_merge_fail: +- if (mpdu_buf && decap_format != DP_RX_DECAP_TYPE_RAW) { +- ath11k_dbg(ab, ATH11K_DBG_DATA, +- "err_merge_fail mpdu_buf %pK", mpdu_buf); +- /* Free the head buffer */ +- dev_kfree_skb_any(mpdu_buf); +- } + return NULL; + } + diff --git a/package/kernel/mac80211/patches/ath11k/0054-ath11k-Fix-spelling-mistake-incompaitiblity-incompat.patch b/package/kernel/mac80211/patches/ath11k/0054-ath11k-Fix-spelling-mistake-incompaitiblity-incompat.patch new file mode 100644 index 00000000000000..224e921196c502 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0054-ath11k-Fix-spelling-mistake-incompaitiblity-incompat.patch @@ -0,0 +1,26 @@ +From 567ec33a76c7d8e7fbd7a73c81dd16b9efc7ae6d Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Wed, 6 Oct 2021 09:32:17 +0100 +Subject: [PATCH] ath11k: Fix spelling mistake "incompaitiblity" -> + "incompatibility" + +There is a spelling mistake in an ath11k_warn message. Fix it. + +Signed-off-by: Colin Ian King +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211006083217.349596-1-colin.king@canonical.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7091,7 +7091,7 @@ ath11k_mac_op_set_bitrate_mask(struct ie + + if (!ath11k_mac_validate_vht_he_fixed_rate_settings(ar, band, mask)) + ath11k_warn(ar->ab, +- "could not update fixed rate settings to all peers due to mcs/nss incompaitiblity\n"); ++ "could not update fixed rate settings to all peers due to mcs/nss incompatibility\n"); + nss = min_t(u32, ar->num_tx_chains, + max(max(ath11k_mac_max_ht_nss(ht_mcs_mask), + ath11k_mac_max_vht_nss(vht_mcs_mask)), diff --git a/package/kernel/mac80211/patches/ath11k/0055-ath11k-fix-m68k-and-xtensa-build-failure-in-ath11k_p.patch b/package/kernel/mac80211/patches/ath11k/0055-ath11k-fix-m68k-and-xtensa-build-failure-in-ath11k_p.patch new file mode 100644 index 00000000000000..048c8726026eb3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0055-ath11k-fix-m68k-and-xtensa-build-failure-in-ath11k_p.patch @@ -0,0 +1,56 @@ +From 16bdce2ada5a4c3c91b7c4e81780d2de50bd6ab5 Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Fri, 8 Oct 2021 17:39:32 +0300 +Subject: [PATCH] ath11k: fix m68k and xtensa build failure in + ath11k_peer_assoc_h_smps() + +Stephen reported that ath11k was failing to build on m68k and xtensa: + +In file included from :0:0: +In function 'ath11k_peer_assoc_h_smps', + inlined from 'ath11k_peer_assoc_prepare' at drivers/net/wireless/ath/ath11k/mac.c:2362:2: +include/linux/compiler_types.h:317:38: error: call to '__compiletime_assert_650' declared with attribute error: FIELD_GET: type of reg too small for mask + _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) + ^ +include/linux/compiler_types.h:298:4: note: in definition of macro '__compiletime_assert' + prefix ## suffix(); \ + ^ +include/linux/compiler_types.h:317:2: note: in expansion of macro '_compiletime_assert' + _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) + ^ +include/linux/build_bug.h:39:37: note: in expansion of macro 'compiletime_assert' + #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) + ^ +include/linux/bitfield.h:52:3: note: in expansion of macro 'BUILD_BUG_ON_MSG' + BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ull, \ + ^ +include/linux/bitfield.h:108:3: note: in expansion of macro '__BF_FIELD_CHECK' + __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \ + ^ +drivers/net/wireless/ath/ath11k/mac.c:2079:10: note: in expansion of macro 'FIELD_GET' + smps = FIELD_GET(IEEE80211_HE_6GHZ_CAP_SM_PS, + +Fix the issue by using le16_get_bits() to specify the size explicitly. + +Fixes: 6f4d70308e5e ("ath11k: support SMPS configuration for 6 GHz") +Reported-by: Stephen Rothwell +Tested-by: Geert Uytterhoeven +Signed-off-by: Kalle Valo +Signed-off-by: David S. Miller +--- + drivers/net/wireless/ath/ath11k/mac.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2081,8 +2081,8 @@ static void ath11k_peer_assoc_h_smps(str + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + } else { +- smps = FIELD_GET(IEEE80211_HE_6GHZ_CAP_SM_PS, +- le16_to_cpu(sta->he_6ghz_capa.capa)); ++ smps = le16_get_bits(sta->he_6ghz_capa.capa, ++ IEEE80211_HE_6GHZ_CAP_SM_PS); + } + + switch (smps) { diff --git a/package/kernel/mac80211/patches/ath11k/0056-ath11k-Remove-redundant-assignment-to-variable-fw_si.patch b/package/kernel/mac80211/patches/ath11k/0056-ath11k-Remove-redundant-assignment-to-variable-fw_si.patch new file mode 100644 index 00000000000000..0e7054aa7387c5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0056-ath11k-Remove-redundant-assignment-to-variable-fw_si.patch @@ -0,0 +1,28 @@ +From 4f50bdfb4e5fc3d753c8cf94b94b43aaa2c49b95 Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Thu, 7 Oct 2021 18:16:24 +0300 +Subject: [PATCH] ath11k: Remove redundant assignment to variable fw_size + +Variable fw_size is being assigned a value that is never read and +being re-assigned a new value in the next statement. The assignment +is redundant and can be removed. + +Addresses-Coverity: ("Unused value") +Fixes: 336e7b53c82f ("ath11k: clean up BDF download functions") +Signed-off-by: Colin Ian King +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211006105529.1011239-1-colin.king@canonical.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2135,7 +2135,6 @@ static int ath11k_qmi_load_bdf_qmi(struc + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi bdf_type %d\n", bdf_type); + +- fw_size = bd.len; + fw_size = min_t(u32, ab->hw_params.fw.board_size, bd.len); + + ret = ath11k_qmi_load_file_target_mem(ab, bd.data, fw_size, bdf_type); diff --git a/package/kernel/mac80211/patches/ath11k/0057-ath11k-Use-kcalloc-instead-of-kzalloc.patch b/package/kernel/mac80211/patches/ath11k/0057-ath11k-Use-kcalloc-instead-of-kzalloc.patch new file mode 100644 index 00000000000000..53b1fcc6ce419f --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0057-ath11k-Use-kcalloc-instead-of-kzalloc.patch @@ -0,0 +1,40 @@ +From ec4363384c3f110561dc5ee2e59adee02dbd9f73 Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Thu, 7 Oct 2021 18:16:24 +0300 +Subject: [PATCH] ath11k: Use kcalloc() instead of kzalloc() + +Use 2-factor multiplication argument form kcalloc() instead +of kzalloc(). + +Link: https://github.com/KSPP/linux/issues/162 +Signed-off-by: Gustavo A. R. Silva +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211006181204.GA913553@embeddedor +--- + drivers/net/wireless/ath/ath11k/wmi.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -4066,8 +4066,8 @@ static int ath11k_wmi_tlv_mac_phy_caps_p + + len = min_t(u16, len, sizeof(struct wmi_mac_phy_capabilities)); + if (!svc_rdy_ext->n_mac_phy_caps) { +- svc_rdy_ext->mac_phy_caps = kzalloc((svc_rdy_ext->tot_phy_id) * len, +- GFP_ATOMIC); ++ svc_rdy_ext->mac_phy_caps = kcalloc(svc_rdy_ext->tot_phy_id, ++ len, GFP_ATOMIC); + if (!svc_rdy_ext->mac_phy_caps) + return -ENOMEM; + } +@@ -4467,8 +4467,8 @@ static struct cur_reg_rule + struct cur_reg_rule *reg_rule_ptr; + u32 count; + +- reg_rule_ptr = kzalloc((num_reg_rules * sizeof(*reg_rule_ptr)), +- GFP_ATOMIC); ++ reg_rule_ptr = kcalloc(num_reg_rules, sizeof(*reg_rule_ptr), ++ GFP_ATOMIC); + + if (!reg_rule_ptr) + return NULL; diff --git a/package/kernel/mac80211/patches/ath11k/0058-ath11k-Handle-MSI-enablement-during-rmmod-and-SSR.patch b/package/kernel/mac80211/patches/ath11k/0058-ath11k-Handle-MSI-enablement-during-rmmod-and-SSR.patch new file mode 100644 index 00000000000000..dcb1c0410714d3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0058-ath11k-Handle-MSI-enablement-during-rmmod-and-SSR.patch @@ -0,0 +1,139 @@ +From 96527d527b271d950367ad13e3de8b0673545622 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 11 Oct 2021 09:33:08 +0300 +Subject: [PATCH] ath11k: Handle MSI enablement during rmmod and SSR + +When doing "rmmod ath11k_pci", ath11k performs global SOC reset +and MHI reset, where 0 address access is captured by IOMMU. See +log below: + +... +[ 133.953860] ath11k_pci 0000:02:00.0: setting mhi state: DEINIT(1) +[ 133.959714] ath11k_pci 0000:02:00.0: AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x000a address=0x0 flags=0x0020] +[ 133.973854] ath11k_pci 0000:02:00.0: MHISTATUS 0xff04 +[ 133.974095] ath11k_pci 0000:02:00.0: AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x000a address=0x0 flags=0x0020] +... + +This issue is also observed in SSR process, cause a similar +sequence as above is performed. + +Such an invalid access occurs because, during rmmod or SSR, MSI +address is cleared but HW MSI functionality not disabled, thus HW +target is able to raise an MSI transaction with 0 as MSI address. + +So it can be fixed by simply disabling MSI before reset. For SSR, +since MSI functionality is still needed after target is brought +back, we need to reenable it. + +Also change naming of some interfaces related. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913180246.193388-5-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 41 +++++++++++++++++++++++---- + 1 file changed, 36 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -861,7 +861,32 @@ static void ath11k_pci_ce_irqs_enable(st + } + } + +-static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci) ++static void ath11k_pci_msi_config(struct ath11k_pci *ab_pci, bool enable) ++{ ++ struct pci_dev *dev = ab_pci->pdev; ++ u16 control; ++ ++ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); ++ ++ if (enable) ++ control |= PCI_MSI_FLAGS_ENABLE; ++ else ++ control &= ~PCI_MSI_FLAGS_ENABLE; ++ ++ pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); ++} ++ ++static void ath11k_pci_msi_enable(struct ath11k_pci *ab_pci) ++{ ++ ath11k_pci_msi_config(ab_pci, true); ++} ++ ++static void ath11k_pci_msi_disable(struct ath11k_pci *ab_pci) ++{ ++ ath11k_pci_msi_config(ab_pci, false); ++} ++ ++static int ath11k_pci_alloc_msi(struct ath11k_pci *ab_pci) + { + struct ath11k_base *ab = ab_pci->ab; + const struct ath11k_msi_config *msi_config = ab_pci->msi_config; +@@ -882,6 +907,7 @@ static int ath11k_pci_enable_msi(struct + else + return num_vectors; + } ++ ath11k_pci_msi_disable(ab_pci); + + msi_desc = irq_get_msi_desc(ab_pci->pdev->irq); + if (!msi_desc) { +@@ -904,7 +930,7 @@ free_msi_vector: + return ret; + } + +-static void ath11k_pci_disable_msi(struct ath11k_pci *ab_pci) ++static void ath11k_pci_free_msi(struct ath11k_pci *ab_pci) + { + pci_free_irq_vectors(ab_pci->pdev); + } +@@ -1025,6 +1051,8 @@ static int ath11k_pci_power_up(struct at + */ + ath11k_pci_aspm_disable(ab_pci); + ++ ath11k_pci_msi_enable(ab_pci); ++ + ret = ath11k_mhi_start(ab_pci); + if (ret) { + ath11k_err(ab, "failed to start mhi: %d\n", ret); +@@ -1045,6 +1073,9 @@ static void ath11k_pci_power_down(struct + ath11k_pci_aspm_restore(ab_pci); + + ath11k_pci_force_wake(ab_pci->ab); ++ ++ ath11k_pci_msi_disable(ab_pci); ++ + ath11k_mhi_stop(ab_pci); + clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); + ath11k_pci_sw_reset(ab_pci->ab, false); +@@ -1279,7 +1310,7 @@ static int ath11k_pci_probe(struct pci_d + goto err_pci_free_region; + } + +- ret = ath11k_pci_enable_msi(ab_pci); ++ ret = ath11k_pci_alloc_msi(ab_pci); + if (ret) { + ath11k_err(ab, "failed to enable msi: %d\n", ret); + goto err_pci_free_region; +@@ -1333,7 +1364,7 @@ err_mhi_unregister: + ath11k_mhi_unregister(ab_pci); + + err_pci_disable_msi: +- ath11k_pci_disable_msi(ab_pci); ++ ath11k_pci_free_msi(ab_pci); + + err_pci_free_region: + ath11k_pci_free_region(ab_pci); +@@ -1364,7 +1395,7 @@ qmi_fail: + ath11k_mhi_unregister(ab_pci); + + ath11k_pci_free_irq(ab); +- ath11k_pci_disable_msi(ab_pci); ++ ath11k_pci_free_msi(ab_pci); + ath11k_pci_free_region(ab_pci); + + ath11k_hal_srng_deinit(ab); diff --git a/package/kernel/mac80211/patches/ath11k/0059-ath11k-Change-number-of-TCL-rings-to-one-for-QCA6390.patch b/package/kernel/mac80211/patches/ath11k/0059-ath11k-Change-number-of-TCL-rings-to-one-for-QCA6390.patch new file mode 100644 index 00000000000000..add0216796a037 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0059-ath11k-Change-number-of-TCL-rings-to-one-for-QCA6390.patch @@ -0,0 +1,241 @@ +From 31582373a4a8e888b29ed759d28330a1995f2162 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 11 Oct 2021 09:33:09 +0300 +Subject: [PATCH] ath11k: Change number of TCL rings to one for QCA6390 + +Some targets, QCA6390 for example, use only one TCL ring, it is better to +initialize only one ring and leave others untouched for such targets. + +This is a theoretical fix found during code review, no visible impact. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210914163726.38604-1-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 10 +++++----- + drivers/net/wireless/ath/ath11k/debugfs.c | 2 +- + drivers/net/wireless/ath/ath11k/dp.c | 10 +++++----- + drivers/net/wireless/ath/ath11k/dp.h | 1 + + drivers/net/wireless/ath/ath11k/dp_tx.c | 13 +++++-------- + drivers/net/wireless/ath/ath11k/hw.h | 2 +- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + 7 files changed, 19 insertions(+), 21 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -58,7 +58,6 @@ static const struct ath11k_hw_params ath + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + .htt_peer_map_v2 = true, +- .tcl_0_only = false, + + .spectral = { + .fft_sz = 2, +@@ -81,6 +80,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, ++ .max_tx_ring = DP_TCL_NUM_RING_MAX, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -109,7 +109,6 @@ static const struct ath11k_hw_params ath + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + .htt_peer_map_v2 = true, +- .tcl_0_only = false, + + .spectral = { + .fft_sz = 4, +@@ -129,6 +128,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, ++ .max_tx_ring = DP_TCL_NUM_RING_MAX, + }, + { + .name = "qca6390 hw2.0", +@@ -157,7 +157,6 @@ static const struct ath11k_hw_params ath + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, +- .tcl_0_only = true, + + .spectral = { + .fft_sz = 0, +@@ -176,6 +175,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, ++ .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + }, + { + .name = "qcn9074 hw1.0", +@@ -203,7 +203,6 @@ static const struct ath11k_hw_params ath + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + .htt_peer_map_v2 = true, +- .tcl_0_only = false, + + .spectral = { + .fft_sz = 2, +@@ -223,6 +222,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .fix_l1ss = true, ++ .max_tx_ring = DP_TCL_NUM_RING_MAX, + }, + { + .name = "wcn6855 hw2.0", +@@ -251,7 +251,6 @@ static const struct ath11k_hw_params ath + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, +- .tcl_0_only = true, + + .spectral = { + .fft_sz = 0, +@@ -270,6 +269,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .fix_l1ss = false, ++ .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -806,7 +806,7 @@ static ssize_t ath11k_debugfs_dump_soc_d + len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n"); + len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n"); + +- for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) ++ for (i = 0; i < ab->hw_params.max_tx_ring; i++) + len += scnprintf(buf + len, size - len, "ring%d: %u\n", + i, soc_stats->tx_err.desc_na[i]); + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -311,7 +311,7 @@ void ath11k_dp_stop_shadow_timers(struct + if (!ab->hw_params.supports_shadow_regs) + return; + +- for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) ++ for (i = 0; i < ab->hw_params.max_tx_ring; i++) + ath11k_dp_shadow_stop_timer(ab, &ab->dp.tx_ring_timer[i]); + + ath11k_dp_shadow_stop_timer(ab, &ab->dp.reo_cmd_timer); +@@ -326,7 +326,7 @@ static void ath11k_dp_srng_common_cleanu + ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); +- for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { ++ for (i = 0; i < ab->hw_params.max_tx_ring; i++) { + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); + } +@@ -366,7 +366,7 @@ static int ath11k_dp_srng_common_setup(s + goto err; + } + +- for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { ++ for (i = 0; i < ab->hw_params.max_tx_ring; i++) { + ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_data_ring, + HAL_TCL_DATA, i, 0, + DP_TCL_DATA_RING_SIZE); +@@ -996,7 +996,7 @@ void ath11k_dp_free(struct ath11k_base * + + ath11k_dp_reo_cmd_list_cleanup(ab); + +- for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { ++ for (i = 0; i < ab->hw_params.max_tx_ring; i++) { + spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); + idr_for_each(&dp->tx_ring[i].txbuf_idr, + ath11k_dp_tx_pending_cleanup, ab); +@@ -1046,7 +1046,7 @@ int ath11k_dp_alloc(struct ath11k_base * + + size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE; + +- for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { ++ for (i = 0; i < ab->hw_params.max_tx_ring; i++) { + idr_init(&dp->tx_ring[i].txbuf_idr); + spin_lock_init(&dp->tx_ring[i].tx_idr_lock); + dp->tx_ring[i].tcl_data_ring_id = i; +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -170,6 +170,7 @@ struct ath11k_pdev_dp { + #define DP_BA_WIN_SZ_MAX 256 + + #define DP_TCL_NUM_RING_MAX 3 ++#define DP_TCL_NUM_RING_MAX_QCA6390 1 + + #define DP_IDLE_SCATTER_BUFS_MAX 16 + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -115,11 +115,8 @@ int ath11k_dp_tx(struct ath11k *ar, stru + + tcl_ring_sel: + tcl_ring_retry = false; +- /* For some chip, it can only use tcl0 to tx */ +- if (ar->ab->hw_params.tcl_0_only) +- ti.ring_id = 0; +- else +- ti.ring_id = ring_selector % DP_TCL_NUM_RING_MAX; ++ ++ ti.ring_id = ring_selector % ab->hw_params.max_tx_ring; + + ring_map |= BIT(ti.ring_id); + +@@ -131,7 +128,7 @@ tcl_ring_sel: + spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (ret < 0) { +- if (ring_map == (BIT(DP_TCL_NUM_RING_MAX) - 1)) { ++ if (ring_map == (BIT(ab->hw_params.max_tx_ring) - 1)) { + atomic_inc(&ab->soc_stats.tx_err.misc_fail); + return -ENOSPC; + } +@@ -248,8 +245,8 @@ tcl_ring_sel: + * checking this ring earlier for each pkt tx. + * Restart ring selection if some rings are not checked yet. + */ +- if (ring_map != (BIT(DP_TCL_NUM_RING_MAX) - 1) && +- !ar->ab->hw_params.tcl_0_only) { ++ if (ring_map != (BIT(ab->hw_params.max_tx_ring) - 1) && ++ ab->hw_params.max_tx_ring > 1) { + tcl_ring_retry = true; + ring_selector++; + } +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -152,7 +152,6 @@ struct ath11k_hw_params { + bool rx_mac_buf_ring; + bool vdev_start_delay; + bool htt_peer_map_v2; +- bool tcl_0_only; + + struct { + u8 fft_sz; +@@ -170,6 +169,7 @@ struct ath11k_hw_params { + bool supports_suspend; + u32 hal_desc_sz; + bool fix_l1ss; ++ u8 max_tx_ring; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5807,7 +5807,7 @@ err_vdev_del: + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_vif_txmgmt_idr_remove, vif); + +- for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { ++ for (i = 0; i < ab->hw_params.max_tx_ring; i++) { + spin_lock_bh(&ab->dp.tx_ring[i].tx_idr_lock); + idr_for_each(&ab->dp.tx_ring[i].txbuf_idr, + ath11k_mac_vif_unref, vif); diff --git a/package/kernel/mac80211/patches/ath11k/0060-ath11k-Identify-DFS-channel-when-sending-scan-channe.patch b/package/kernel/mac80211/patches/ath11k/0060-ath11k-Identify-DFS-channel-when-sending-scan-channe.patch new file mode 100644 index 00000000000000..1c762ff25e339b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0060-ath11k-Identify-DFS-channel-when-sending-scan-channe.patch @@ -0,0 +1,33 @@ +From 8cd5c0847160aa9482d7f93ed63c4d72bad70cdf Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 11 Oct 2021 18:18:00 +0300 +Subject: [PATCH] ath11k: Identify DFS channel when sending scan channel list + command + +WMI_CHAN_INFO_DFS flag should be set when configuring a DFS channel +included in scan channel list. Without it, firmware will not send a +probe request frame which is needed in connection to an AP configured +with hidden SSID/network_id. So fix this to allow probe request frames +to be sent in cases where a beacon frame has been seen on the channel +first. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211011054919.77071-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/wmi.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -2372,6 +2372,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(s + chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE; + if (tchan_info->psc_channel) + chan_info->info |= WMI_CHAN_INFO_PSC; ++ if (tchan_info->dfs_set) ++ chan_info->info |= WMI_CHAN_INFO_DFS; + + chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, + tchan_info->phy_mode); diff --git a/package/kernel/mac80211/patches/ath11k/0061-ath11k-change-return-buffer-manager-for-QCA6390.patch b/package/kernel/mac80211/patches/ath11k/0061-ath11k-change-return-buffer-manager-for-QCA6390.patch new file mode 100644 index 00000000000000..2c71533012a8d0 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0061-ath11k-change-return-buffer-manager-for-QCA6390.patch @@ -0,0 +1,299 @@ +From 734223d78428de3c7c7d7bc04daf258085780d90 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Wed, 20 Oct 2021 11:59:06 +0300 +Subject: [PATCH] ath11k: change return buffer manager for QCA6390 + +QCA6390 firmware uses HAL_RX_BUF_RBM_SW1_BM, not HAL_RX_BUF_RBM_SW3_BM. This is +needed to fix a case where an A-MSDU has an unexpected LLC/SNAP header in the +first subframe (CVE-2020-24588). + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210914163726.38604-2-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 5 ++++ + drivers/net/wireless/ath/ath11k/dp.c | 4 +++- + drivers/net/wireless/ath/ath11k/dp_rx.c | 29 +++++++++++++++--------- + drivers/net/wireless/ath/ath11k/hal_rx.c | 6 +++-- + drivers/net/wireless/ath/ath11k/hw.c | 11 ++++++++- + drivers/net/wireless/ath/ath11k/hw.h | 9 ++++++++ + 6 files changed, 49 insertions(+), 15 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -81,6 +81,7 @@ static const struct ath11k_hw_params ath + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, ++ .hal_params = &ath11k_hw_hal_params_ipq8074, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -129,6 +130,7 @@ static const struct ath11k_hw_params ath + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, ++ .hal_params = &ath11k_hw_hal_params_ipq8074, + }, + { + .name = "qca6390 hw2.0", +@@ -176,6 +178,7 @@ static const struct ath11k_hw_params ath + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, ++ .hal_params = &ath11k_hw_hal_params_qca6390, + }, + { + .name = "qcn9074 hw1.0", +@@ -223,6 +226,7 @@ static const struct ath11k_hw_params ath + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, ++ .hal_params = &ath11k_hw_hal_params_ipq8074, + }, + { + .name = "wcn6855 hw2.0", +@@ -270,6 +274,7 @@ static const struct ath11k_hw_params ath + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .fix_l1ss = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, ++ .hal_params = &ath11k_hw_hal_params_qca6390, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -739,6 +739,7 @@ int ath11k_dp_service_srng(struct ath11k + int budget) + { + struct napi_struct *napi = &irq_grp->napi; ++ const struct ath11k_hw_hal_params *hal_params; + int grp_id = irq_grp->grp_id; + int work_done = 0; + int i = 0, j; +@@ -821,8 +822,9 @@ int ath11k_dp_service_srng(struct ath11k + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + ++ hal_params = ab->hw_params.hal_params; + ath11k_dp_rxbufs_replenish(ab, id, rx_ring, 0, +- HAL_RX_BUF_RBM_SW3_BM); ++ hal_params->rx_buf_rbm); + } + } + } +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -499,7 +499,7 @@ static int ath11k_dp_rxdma_ring_buf_setu + + rx_ring->bufs_max = num_entries; + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, rx_ring, num_entries, +- HAL_RX_BUF_RBM_SW3_BM); ++ ar->ab->hw_params.hal_params->rx_buf_rbm); + return 0; + } + +@@ -2756,7 +2756,7 @@ try_again: + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], +- HAL_RX_BUF_RBM_SW3_BM); ++ ab->hw_params.hal_params->rx_buf_rbm); + } + + ath11k_dp_rx_process_received_packets(ab, napi, &msdu_list, +@@ -2949,6 +2949,7 @@ static int ath11k_dp_rx_reap_mon_status_ + int *budget, struct sk_buff_head *skb_list) + { + struct ath11k *ar; ++ const struct ath11k_hw_hal_params *hal_params; + struct ath11k_pdev_dp *dp; + struct dp_rxdma_ring *rx_ring; + struct hal_srng *srng; +@@ -3019,8 +3020,9 @@ move_next: + &buf_id); + + if (!skb) { ++ hal_params = ab->hw_params.hal_params; + ath11k_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0, +- HAL_RX_BUF_RBM_SW3_BM); ++ hal_params->rx_buf_rbm); + num_buffs_reaped++; + break; + } +@@ -3030,7 +3032,8 @@ move_next: + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + ath11k_hal_rx_buf_addr_info_set(rx_mon_status_desc, rxcb->paddr, +- cookie, HAL_RX_BUF_RBM_SW3_BM); ++ cookie, ++ ab->hw_params.hal_params->rx_buf_rbm); + ath11k_hal_srng_src_get_next_entry(ab, srng); + num_buffs_reaped++; + } +@@ -3419,7 +3422,8 @@ static int ath11k_dp_rx_h_defrag_reo_rei + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, dp->mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + +- ath11k_hal_rx_buf_addr_info_set(msdu0, paddr, cookie, HAL_RX_BUF_RBM_SW3_BM); ++ ath11k_hal_rx_buf_addr_info_set(msdu0, paddr, cookie, ++ ab->hw_params.hal_params->rx_buf_rbm); + + /* Fill mpdu details into reo entrace ring */ + srng = &ab->hal.srng_list[ab->dp.reo_reinject_ring.ring_id]; +@@ -3796,7 +3800,7 @@ int ath11k_dp_process_rx_err(struct ath1 + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, msdu_cookies, + &rbm); + if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && +- rbm != HAL_RX_BUF_RBM_SW3_BM) { ++ rbm != ab->hw_params.hal_params->rx_buf_rbm) { + ab->soc_stats.invalid_rbm++; + ath11k_warn(ab, "invalid return buffer manager %d\n", rbm); + ath11k_dp_rx_link_desc_return(ab, desc, +@@ -3852,7 +3856,7 @@ exit: + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, n_bufs_reaped[i], +- HAL_RX_BUF_RBM_SW3_BM); ++ ab->hw_params.hal_params->rx_buf_rbm); + } + + return tot_n_bufs_reaped; +@@ -4148,7 +4152,7 @@ int ath11k_dp_rx_process_wbm_err(struct + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], +- HAL_RX_BUF_RBM_SW3_BM); ++ ab->hw_params.hal_params->rx_buf_rbm); + } + + rcu_read_lock(); +@@ -4257,7 +4261,7 @@ int ath11k_dp_process_rxdma_err(struct a + + if (num_buf_freed) + ath11k_dp_rxbufs_replenish(ab, mac_id, rx_ring, num_buf_freed, +- HAL_RX_BUF_RBM_SW3_BM); ++ ab->hw_params.hal_params->rx_buf_rbm); + + return budget - quota; + } +@@ -4976,6 +4980,7 @@ static void ath11k_dp_rx_mon_dest_proces + { + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; ++ const struct ath11k_hw_hal_params *hal_params; + void *ring_entry; + void *mon_dst_srng; + u32 ppdu_id; +@@ -5039,16 +5044,18 @@ static void ath11k_dp_rx_mon_dest_proces + + if (rx_bufs_used) { + rx_mon_stats->dest_ppdu_done++; ++ hal_params = ar->ab->hw_params.hal_params; ++ + if (ar->ab->hw_params.rxdma1_enable) + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, + &dp->rxdma_mon_buf_ring, + rx_bufs_used, +- HAL_RX_BUF_RBM_SW3_BM); ++ hal_params->rx_buf_rbm); + else + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, + &dp->rx_refill_buf_ring, + rx_bufs_used, +- HAL_RX_BUF_RBM_SW3_BM); ++ hal_params->rx_buf_rbm); + } + } + +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -356,6 +356,7 @@ int ath11k_hal_wbm_desc_parse_err(struct + struct hal_wbm_release_ring *wbm_desc = desc; + enum hal_wbm_rel_desc_type type; + enum hal_wbm_rel_src_module rel_src; ++ enum hal_rx_buf_return_buf_manager ret_buf_mgr; + + type = FIELD_GET(HAL_WBM_RELEASE_INFO0_DESC_TYPE, + wbm_desc->info0); +@@ -371,8 +372,9 @@ int ath11k_hal_wbm_desc_parse_err(struct + rel_src != HAL_WBM_REL_SRC_MODULE_REO) + return -EINVAL; + +- if (FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, +- wbm_desc->buf_addr_info.info1) != HAL_RX_BUF_RBM_SW3_BM) { ++ ret_buf_mgr = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, ++ wbm_desc->buf_addr_info.info1); ++ if (ret_buf_mgr != ab->hw_params.hal_params->rx_buf_rbm) { + ab->soc_stats.invalid_rbm++; + return -EINVAL; + } +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -7,10 +7,11 @@ + #include + #include + +-#include "hw.h" + #include "core.h" + #include "ce.h" + #include "hif.h" ++#include "hal.h" ++#include "hw.h" + + /* Map from pdev index to hw mac index */ + static u8 ath11k_hw_ipq8074_mac_from_pdev_id(int pdev_idx) +@@ -2122,3 +2123,11 @@ const struct ath11k_hw_regs wcn6855_regs + .pcie_qserdes_sysclk_en_sel = 0x01e0c0ac, + .pcie_pcs_osc_dtct_config_base = 0x01e0c628, + }; ++ ++const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = { ++ .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, ++}; ++ ++const struct ath11k_hw_hal_params ath11k_hw_hal_params_qca6390 = { ++ .rx_buf_rbm = HAL_RX_BUF_RBM_SW1_BM, ++}; +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -6,6 +6,7 @@ + #ifndef ATH11K_HW_H + #define ATH11K_HW_H + ++#include "hal.h" + #include "wmi.h" + + /* Target configuration defines */ +@@ -119,6 +120,10 @@ struct ath11k_hw_ring_mask { + u8 host2rxdma[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + }; + ++struct ath11k_hw_hal_params { ++ enum hal_rx_buf_return_buf_manager rx_buf_rbm; ++}; ++ + struct ath11k_hw_params { + const char *name; + u16 hw_rev; +@@ -170,6 +175,7 @@ struct ath11k_hw_params { + u32 hal_desc_sz; + bool fix_l1ss; + u8 max_tx_ring; ++ const struct ath11k_hw_hal_params *hal_params; + }; + + struct ath11k_hw_ops { +@@ -223,6 +229,9 @@ extern const struct ath11k_hw_ring_mask + 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_hal_params ath11k_hw_hal_params_ipq8074; ++extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_qca6390; ++ + static inline + int ath11k_hw_get_mac_from_pdev_id(struct ath11k_hw_params *hw, + int pdev_idx) diff --git a/package/kernel/mac80211/patches/ath11k/0062-ath11k-set-correct-NL80211_FEATURE_DYNAMIC_SMPS-for-.patch b/package/kernel/mac80211/patches/ath11k/0062-ath11k-set-correct-NL80211_FEATURE_DYNAMIC_SMPS-for-.patch new file mode 100644 index 00000000000000..9c8b84a6747334 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0062-ath11k-set-correct-NL80211_FEATURE_DYNAMIC_SMPS-for-.patch @@ -0,0 +1,93 @@ +From 82c434c103408842a87404e873992b7698b6df2b Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Thu, 28 Oct 2021 10:46:28 +0300 +Subject: [PATCH] ath11k: set correct NL80211_FEATURE_DYNAMIC_SMPS for WCN6855 + +Commit 6f4d70308e5e ("ath11k: support SMPS configuration for 6 GHz") changed +"if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS)" to "if (ht_cap & +WMI_HT_CAP_DYNAMIC_SMPS || ar->supports_6ghz)" which means +NL80211_FEATURE_DYNAMIC_SMPS is enabled for all chips which support 6 GHz. +However, WCN6855 supports 6 GHz but it does not support feature +NL80211_FEATURE_DYNAMIC_SMPS, and this can lead to MU-MIMO test failures for +WCN6855. + +Disable NL80211_FEATURE_DYNAMIC_SMPS for WCN6855 since its ht_cap does not +support WMI_HT_CAP_DYNAMIC_SMPS. Enable the feature only on QCN9074 as that's +the only other device supporting 6 GHz band. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Jouni Malinen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210914163726.38604-3-jouni@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 5 +++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 3 ++- + 3 files changed, 8 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -82,6 +82,7 @@ static const struct ath11k_hw_params ath + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, ++ .supports_dynamic_smps_6ghz = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -131,6 +132,7 @@ static const struct ath11k_hw_params ath + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, ++ .supports_dynamic_smps_6ghz = false, + }, + { + .name = "qca6390 hw2.0", +@@ -179,6 +181,7 @@ static const struct ath11k_hw_params ath + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, ++ .supports_dynamic_smps_6ghz = false, + }, + { + .name = "qcn9074 hw1.0", +@@ -227,6 +230,7 @@ static const struct ath11k_hw_params ath + .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, ++ .supports_dynamic_smps_6ghz = true, + }, + { + .name = "wcn6855 hw2.0", +@@ -275,6 +279,7 @@ static const struct ath11k_hw_params ath + .fix_l1ss = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, ++ .supports_dynamic_smps_6ghz = false, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -176,6 +176,7 @@ struct ath11k_hw_params { + bool fix_l1ss; + u8 max_tx_ring; + const struct ath11k_hw_hal_params *hal_params; ++ bool supports_dynamic_smps_6ghz; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7682,7 +7682,8 @@ static int __ath11k_mac_register(struct + * for each band for a dual band capable radio. It will be tricky to + * handle it when the ht capability different for each band. + */ +- if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS || ar->supports_6ghz) ++ if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS || ++ (ar->supports_6ghz && ab->hw_params.supports_dynamic_smps_6ghz)) + ar->hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS; + + ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID; diff --git a/package/kernel/mac80211/patches/ath11k/0063-ath11k-convert-ath11k_wmi_pdev_set_ps_mode-to-use-en.patch b/package/kernel/mac80211/patches/ath11k/0063-ath11k-convert-ath11k_wmi_pdev_set_ps_mode-to-use-en.patch new file mode 100644 index 00000000000000..7d130b0c466464 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0063-ath11k-convert-ath11k_wmi_pdev_set_ps_mode-to-use-en.patch @@ -0,0 +1,73 @@ +From af3d89649bb69bd5be273cf6c001cd19c2604ca2 Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Mon, 8 Nov 2021 14:38:25 +0200 +Subject: [PATCH] ath11k: convert ath11k_wmi_pdev_set_ps_mode() to use enum + wmi_sta_ps_mode + +It's more descriptive to use the actual enum used by the firmware instead of a +boolean so change ath11k_wmi_pdev_set_ps_mode() to use a boolean. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211108123826.8463-1-kvalo@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 3 ++- + drivers/net/wireless/ath/ath11k/wmi.c | 7 ++++--- + drivers/net/wireless/ath/ath11k/wmi.h | 3 ++- + 3 files changed, 8 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5648,7 +5648,8 @@ static int ath11k_mac_op_add_interface(s + goto err_peer_del; + } + +- ret = ath11k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, false); ++ ret = ath11k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, ++ WMI_STA_PS_MODE_DISABLED); + if (ret) { + ath11k_warn(ar->ab, "failed to disable vdev %d ps mode: %d\n", + arvif->vdev_id, ret); +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -1244,7 +1244,8 @@ int ath11k_wmi_pdev_set_param(struct ath + return ret; + } + +-int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable) ++int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, ++ enum wmi_sta_ps_mode psmode) + { + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_set_ps_mode_cmd *cmd; +@@ -1259,7 +1260,7 @@ int ath11k_wmi_pdev_set_ps_mode(struct a + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_STA_POWERSAVE_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; +- cmd->sta_ps_mode = enable; ++ cmd->sta_ps_mode = psmode; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_POWERSAVE_MODE_CMDID); + if (ret) { +@@ -1269,7 +1270,7 @@ int ath11k_wmi_pdev_set_ps_mode(struct a + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev set psmode %d vdev id %d\n", +- enable, vdev_id); ++ psmode, vdev_id); + + return ret; + } +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5351,7 +5351,8 @@ int ath11k_wmi_set_peer_param(struct ath + u32 vdev_id, u32 param_id, u32 param_val); + int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, + u32 param_value, u8 pdev_id); +-int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable); ++int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, ++ enum wmi_sta_ps_mode psmode); + int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab); + int ath11k_wmi_cmd_init(struct ath11k_base *ab); + int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab); diff --git a/package/kernel/mac80211/patches/ath11k/0064-ath11k-enable-802.11-power-save-mode-in-station-mode.patch b/package/kernel/mac80211/patches/ath11k/0064-ath11k-enable-802.11-power-save-mode-in-station-mode.patch new file mode 100644 index 00000000000000..e2a6a4f92664cf --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0064-ath11k-enable-802.11-power-save-mode-in-station-mode.patch @@ -0,0 +1,192 @@ +From b2beffa7d9a67b59b085616a27f1d10b1e80784f Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 8 Nov 2021 14:38:26 +0200 +Subject: [PATCH] ath11k: enable 802.11 power save mode in station mode + +To reduce power consumption enable 802.11 power save mode in station mode. This +allows both radio and CPU to sleep more. + +Only enable the mode on QCA6390 and WCN6855, it's unknown how other hardware +families support this feature. + +To test that power save mode is running run "iw dev wls1 set power_save off", +check there is no NULL Data frame seen by a sniffer. And run "iw dev wls1 set power_save +on" and check there is a NULL Data frame in sniffer. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211108123826.8463-2-kvalo@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 5 ++ + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 87 ++++++++++++++++++++++++++ + 4 files changed, 94 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -76,6 +76,7 @@ static const struct ath11k_hw_params ath + .supports_monitor = true, + .supports_shadow_regs = false, + .idle_ps = false, ++ .supports_sta_ps = false, + .cold_boot_calib = true, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), +@@ -126,6 +127,7 @@ static const struct ath11k_hw_params ath + .supports_monitor = true, + .supports_shadow_regs = false, + .idle_ps = false, ++ .supports_sta_ps = false, + .cold_boot_calib = true, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), +@@ -175,6 +177,7 @@ static const struct ath11k_hw_params ath + .supports_monitor = false, + .supports_shadow_regs = true, + .idle_ps = true, ++ .supports_sta_ps = true, + .cold_boot_calib = false, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), +@@ -224,6 +227,7 @@ static const struct ath11k_hw_params ath + .supports_monitor = true, + .supports_shadow_regs = false, + .idle_ps = false, ++ .supports_sta_ps = false, + .cold_boot_calib = false, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), +@@ -273,6 +277,7 @@ static const struct ath11k_hw_params ath + .supports_monitor = false, + .supports_shadow_regs = true, + .idle_ps = true, ++ .supports_sta_ps = true, + .cold_boot_calib = false, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -246,6 +246,7 @@ struct ath11k_vif { + bool is_started; + bool is_up; + bool spectral_enabled; ++ bool ps; + u32 aid; + u8 bssid[ETH_ALEN]; + struct cfg80211_bitrate_mask bitrate_mask; +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -170,6 +170,7 @@ struct ath11k_hw_params { + bool supports_monitor; + bool supports_shadow_regs; + bool idle_ps; ++ bool supports_sta_ps; + bool cold_boot_calib; + bool supports_suspend; + u32 hal_desc_sz; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1050,6 +1050,83 @@ static int ath11k_mac_monitor_stop(struc + return 0; + } + ++static int ath11k_mac_vif_setup_ps(struct ath11k_vif *arvif) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ieee80211_vif *vif = arvif->vif; ++ struct ieee80211_conf *conf = &ar->hw->conf; ++ enum wmi_sta_powersave_param param; ++ enum wmi_sta_ps_mode psmode; ++ int ret; ++ int timeout; ++ bool enable_ps; ++ ++ lockdep_assert_held(&arvif->ar->conf_mutex); ++ ++ if (arvif->vif->type != NL80211_IFTYPE_STATION) ++ return 0; ++ ++ enable_ps = arvif->ps; ++ ++ if (!arvif->is_started) { ++ /* mac80211 can update vif powersave state while disconnected. ++ * Firmware doesn't behave nicely and consumes more power than ++ * necessary if PS is disabled on a non-started vdev. Hence ++ * force-enable PS for non-running vdevs. ++ */ ++ psmode = WMI_STA_PS_MODE_ENABLED; ++ } else if (enable_ps) { ++ psmode = WMI_STA_PS_MODE_ENABLED; ++ param = WMI_STA_PS_PARAM_INACTIVITY_TIME; ++ ++ timeout = conf->dynamic_ps_timeout; ++ if (timeout == 0) { ++ /* firmware doesn't like 0 */ ++ timeout = ieee80211_tu_to_usec(vif->bss_conf.beacon_int) / 1000; ++ } ++ ++ ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, ++ timeout); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set inactivity time for vdev %d: %i\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } else { ++ psmode = WMI_STA_PS_MODE_DISABLED; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %d psmode %s\n", ++ arvif->vdev_id, psmode ? "enable" : "disable"); ++ ++ ret = ath11k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, psmode); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set sta power save mode %d for vdev %d: %d\n", ++ psmode, arvif->vdev_id, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ath11k_mac_config_ps(struct ath11k *ar) ++{ ++ struct ath11k_vif *arvif; ++ int ret = 0; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ ret = ath11k_mac_vif_setup_ps(arvif); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to setup powersave: %d\n", ret); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ + static int ath11k_mac_op_config(struct ieee80211_hw *hw, u32 changed) + { + struct ath11k *ar = hw->priv; +@@ -2947,6 +3024,16 @@ static void ath11k_mac_op_bss_info_chang + ath11k_mac_txpower_recalc(ar); + } + ++ if (changed & BSS_CHANGED_PS && ++ ar->ab->hw_params.supports_sta_ps) { ++ arvif->ps = vif->bss_conf.ps; ++ ++ ret = ath11k_mac_config_ps(ar); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to setup ps on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ } ++ + if (changed & BSS_CHANGED_MCAST_RATE && + !ath11k_mac_vif_chan(arvif->vif, &def)) { + band = def.chan->band; diff --git a/package/kernel/mac80211/patches/ath11k/0066-ath11k-Clear-auth-flag-only-for-actual-association-i.patch b/package/kernel/mac80211/patches/ath11k/0066-ath11k-Clear-auth-flag-only-for-actual-association-i.patch new file mode 100644 index 00000000000000..60e76e53a80384 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0066-ath11k-Clear-auth-flag-only-for-actual-association-i.patch @@ -0,0 +1,72 @@ +From c802b6d7815d7c3f556efea28d0b79ef57ebcfd4 Mon Sep 17 00:00:00 2001 +From: Rameshkumar Sundaram +Date: Mon, 25 Oct 2021 21:33:06 +0530 +Subject: [PATCH] ath11k: Clear auth flag only for actual association in + security mode + +AUTH flag is needed when peer assoc command is sent from host in +security mode for non-assoc cases. Firmware will handle AUTH flag +when client is associating as AUTH flag will be set after key exchange. +For internally provided peer assoc commands from host, there won't be +any key exchange, so AUTH flag is expected to be set in host. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Lavanya Suresh +Signed-off-by: Lavanya Suresh +Signed-off-by: Rameshkumar Sundaram +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635177786-20854-1-git-send-email-quic_ramess@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 3 +++ + drivers/net/wireless/ath/ath11k/wmi.c | 2 +- + drivers/net/wireless/ath/ath11k/wmi.h | 1 + + 3 files changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2500,6 +2500,7 @@ static void ath11k_bss_assoc(struct ieee + + rcu_read_unlock(); + ++ peer_arg.is_assoc = true; + ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (ret) { + ath11k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n", +@@ -3778,6 +3779,7 @@ static int ath11k_station_assoc(struct a + + ath11k_peer_assoc_prepare(ar, vif, sta, &peer_arg, reassoc); + ++ peer_arg.is_assoc = true; + ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (ret) { + ath11k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", +@@ -3989,6 +3991,7 @@ static void ath11k_sta_rc_update_wk(stru + ath11k_peer_assoc_prepare(ar, arvif->vif, sta, + &peer_arg, true); + ++ peer_arg.is_assoc = false; + err = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (err) + ath11k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -1764,7 +1764,7 @@ ath11k_wmi_copy_peer_flags(struct wmi_pe + cmd->peer_flags |= WMI_PEER_AUTH; + if (param->need_ptk_4_way) { + cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; +- if (!hw_crypto_disabled) ++ if (!hw_crypto_disabled && param->is_assoc) + cmd->peer_flags &= ~WMI_PEER_AUTH; + } + if (param->need_gtk_2_way) +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3617,6 +3617,7 @@ struct peer_assoc_params { + u32 peer_he_tx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + bool twt_responder; + bool twt_requester; ++ bool is_assoc; + struct ath11k_ppe_threshold peer_ppet; + }; + diff --git a/package/kernel/mac80211/patches/ath11k/0067-ath11k-fix-fw-crash-due-to-peer-get-authorized-befor.patch b/package/kernel/mac80211/patches/ath11k/0067-ath11k-fix-fw-crash-due-to-peer-get-authorized-befor.patch new file mode 100644 index 00000000000000..5413ae8e6eaea8 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0067-ath11k-fix-fw-crash-due-to-peer-get-authorized-befor.patch @@ -0,0 +1,114 @@ +From 85f36923be47b6990215ad444545a6a85133a0c6 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Fri, 12 Nov 2021 10:03:40 +0200 +Subject: [PATCH] ath11k: fix fw crash due to peer get authorized before key + install + +Firmware expects host to authorize the peer after the successful key +install. But host authorize the peer before the key install, this trigger +the firmware assert which leads to Q6 crash. To avoid this Q6 crash, host +should authorize the peer after the key install. So introduce is_authorized +in peer object to identify that peer is authorize or not. When +IEEE80211_STA_CONTROL_PORT flag is unset, peer move to authorize state +before the vdev up. When the same flag is set then peer move to authorize +state after vdev up. So added authorise check in ath11k_bss_assoc() to +handle the earlier state transition case. Also added the WMI authorize +procedure in ath11k_mac_op_sta_state() to handle the non-earlier state +transition case. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01492-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636554200-12345-1-git-send-email-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 53 ++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/peer.h | 1 + + 2 files changed, 47 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2479,6 +2479,8 @@ static void ath11k_bss_assoc(struct ieee + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct peer_assoc_params peer_arg; + struct ieee80211_sta *ap_sta; ++ struct ath11k_peer *peer; ++ bool is_auth = false; + int ret; + + lockdep_assert_held(&ar->conf_mutex); +@@ -2541,13 +2543,22 @@ static void ath11k_bss_assoc(struct ieee + "mac vdev %d up (associated) bssid %pM aid %d\n", + arvif->vdev_id, bss_conf->bssid, bss_conf->aid); + +- /* Authorize BSS Peer */ +- ret = ath11k_wmi_set_peer_param(ar, arvif->bssid, +- arvif->vdev_id, +- WMI_PEER_AUTHORIZE, +- 1); +- if (ret) +- ath11k_warn(ar->ab, "Unable to authorize BSS peer: %d\n", ret); ++ spin_lock_bh(&ar->ab->base_lock); ++ ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, arvif->bssid); ++ if (peer && peer->is_authorized) ++ is_auth = true; ++ ++ spin_unlock_bh(&ar->ab->base_lock); ++ ++ if (is_auth) { ++ ret = ath11k_wmi_set_peer_param(ar, arvif->bssid, ++ arvif->vdev_id, ++ WMI_PEER_AUTHORIZE, ++ 1); ++ if (ret) ++ ath11k_warn(ar->ab, "Unable to authorize BSS peer: %d\n", ret); ++ } + + ret = ath11k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id, + &bss_conf->he_obss_pd); +@@ -4227,6 +4238,34 @@ static int ath11k_mac_op_sta_state(struc + ath11k_warn(ar->ab, "Failed to associate station: %pM\n", + sta->addr); + } else if (old_state == IEEE80211_STA_ASSOC && ++ new_state == IEEE80211_STA_AUTHORIZED) { ++ spin_lock_bh(&ar->ab->base_lock); ++ ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ if (peer) ++ peer->is_authorized = true; ++ ++ spin_unlock_bh(&ar->ab->base_lock); ++ ++ if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) { ++ ret = ath11k_wmi_set_peer_param(ar, sta->addr, ++ arvif->vdev_id, ++ WMI_PEER_AUTHORIZE, ++ 1); ++ if (ret) ++ ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", ++ sta->addr, arvif->vdev_id, ret); ++ } ++ } else if (old_state == IEEE80211_STA_AUTHORIZED && ++ new_state == IEEE80211_STA_ASSOC) { ++ spin_lock_bh(&ar->ab->base_lock); ++ ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ if (peer) ++ peer->is_authorized = false; ++ ++ spin_unlock_bh(&ar->ab->base_lock); ++ } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -28,6 +28,7 @@ struct ath11k_peer { + u8 ucast_keyidx; + u16 sec_type; + u16 sec_type_grp; ++ bool is_authorized; + }; + + void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); diff --git a/package/kernel/mac80211/patches/ath11k/0068-ath11k-fix-error-routine-when-fallback-of-add-interf.patch b/package/kernel/mac80211/patches/ath11k/0068-ath11k-fix-error-routine-when-fallback-of-add-interf.patch new file mode 100644 index 00000000000000..b9e95c6d2f001e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0068-ath11k-fix-error-routine-when-fallback-of-add-interf.patch @@ -0,0 +1,60 @@ +From 4ea03443ecdac6920eb7aa3a9da2cd0b8cc6dfc8 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Wed, 10 Nov 2021 21:05:57 +0530 +Subject: [PATCH] ath11k: fix error routine when fallback of add interface + fails + +When there is an error in add interface process from +ath11k_mac_set_kickout(), the code attempts to handle a +fallback for add_interface. When this fallback succeeds, the +driver returns zero rather than error code. This leads to +success for the non created VAP. In cleanup, driver gets +remove interface callback for the non created VAP and +proceeds to self peer delete request which leads to FW assert. +Since it was already deleted on the fallback of add interface, +return the actual error code instead of fallback return code. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00729-QCAHKSWPL_SILICONZ-3 v2 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636558557-2874-1-git-send-email-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5623,7 +5623,7 @@ static int ath11k_mac_op_add_interface(s + u32 param_id, param_value; + u16 nss; + int i; +- int ret; ++ int ret, fbret; + int bit; + + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; +@@ -5826,17 +5826,17 @@ err_peer_del: + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + reinit_completion(&ar->peer_delete_done); + +- ret = ath11k_wmi_send_peer_delete_cmd(ar, vif->addr, +- arvif->vdev_id); +- if (ret) { ++ fbret = ath11k_wmi_send_peer_delete_cmd(ar, vif->addr, ++ arvif->vdev_id); ++ if (fbret) { + ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", + arvif->vdev_id, vif->addr); + goto err; + } + +- ret = ath11k_wait_for_peer_delete_done(ar, arvif->vdev_id, +- vif->addr); +- if (ret) ++ fbret = ath11k_wait_for_peer_delete_done(ar, arvif->vdev_id, ++ vif->addr); ++ if (fbret) + goto err; + + ar->num_peers--; diff --git a/package/kernel/mac80211/patches/ath11k/0069-ath11k-avoid-unnecessary-BH-disable-lock-in-STA-kick.patch b/package/kernel/mac80211/patches/ath11k/0069-ath11k-avoid-unnecessary-BH-disable-lock-in-STA-kick.patch new file mode 100644 index 00000000000000..cf9adcada6df5f --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0069-ath11k-avoid-unnecessary-BH-disable-lock-in-STA-kick.patch @@ -0,0 +1,57 @@ +From 4c375743c5fe1ef84d1dd7269dd12585957f403e Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Wed, 10 Nov 2021 21:36:28 +0530 +Subject: [PATCH] ath11k: avoid unnecessary BH disable lock in STA kickout + event + +In STA kickout event processing, the peer object is protected +under spin lock BH. Release this lock after picking up the vdev_id +from the peer object instead after ieee80211_report_low_ack(). +This will minimize the lock hold period which will improve +performance since base_lock is used across the data path. +This was found in code review. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00729-QCAHKSWPL_SILICONZ-3 v2 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636560388-24955-1-git-send-email-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -6400,6 +6400,7 @@ static void ath11k_peer_sta_kickout_even + struct ieee80211_sta *sta; + struct ath11k_peer *peer; + struct ath11k *ar; ++ u32 vdev_id; + + if (ath11k_pull_peer_sta_kickout_ev(ab, skb, &arg) != 0) { + ath11k_warn(ab, "failed to extract peer sta kickout event"); +@@ -6415,10 +6416,15 @@ static void ath11k_peer_sta_kickout_even + if (!peer) { + ath11k_warn(ab, "peer not found %pM\n", + arg.mac_addr); ++ spin_unlock_bh(&ab->base_lock); + goto exit; + } + +- ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id); ++ vdev_id = peer->vdev_id; ++ ++ spin_unlock_bh(&ab->base_lock); ++ ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in peer sta kickout ev %d", + peer->vdev_id); +@@ -6439,7 +6445,6 @@ static void ath11k_peer_sta_kickout_even + ieee80211_report_low_ack(sta, 10); + + exit: +- spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + } + diff --git a/package/kernel/mac80211/patches/ath11k/0070-ath11k-fix-DMA-memory-free-in-CE-pipe-cleanup.patch b/package/kernel/mac80211/patches/ath11k/0070-ath11k-fix-DMA-memory-free-in-CE-pipe-cleanup.patch new file mode 100644 index 00000000000000..8fceae59aa05bc --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0070-ath11k-fix-DMA-memory-free-in-CE-pipe-cleanup.patch @@ -0,0 +1,74 @@ +From 31aeaf547d7e3b64ba5d5442dabc530bdb9e216e Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Wed, 10 Nov 2021 21:51:30 +0530 +Subject: [PATCH] ath11k: fix DMA memory free in CE pipe cleanup + +In CE pipe cleanup, DMA memory gets freed by the aligned address +(base_addr_owner_space) which is wrong. It needs to be freed +by the address (base_addr_owner_space_unaligned) returned by +dma_alloc. So free the dma memory by the proper address. +This was found in code review. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00729-QCAHKSWPL_SILICONZ-3 v2 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636561290-18966-1-git-send-email-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ce.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/ce.c ++++ b/drivers/net/wireless/ath/ath11k/ce.c +@@ -953,6 +953,7 @@ int ath11k_ce_init_pipes(struct ath11k_b + void ath11k_ce_free_pipes(struct ath11k_base *ab) + { + struct ath11k_ce_pipe *pipe; ++ struct ath11k_ce_ring *ce_ring; + int desc_sz; + int i; + +@@ -964,22 +965,24 @@ void ath11k_ce_free_pipes(struct ath11k_ + + if (pipe->src_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); ++ ce_ring = pipe->src_ring; + dma_free_coherent(ab->dev, + pipe->src_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, +- pipe->src_ring->base_addr_owner_space, +- pipe->src_ring->base_addr_ce_space); ++ ce_ring->base_addr_owner_space_unaligned, ++ ce_ring->base_addr_ce_space_unaligned); + kfree(pipe->src_ring); + pipe->src_ring = NULL; + } + + if (pipe->dest_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); ++ ce_ring = pipe->dest_ring; + dma_free_coherent(ab->dev, + pipe->dest_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, +- pipe->dest_ring->base_addr_owner_space, +- pipe->dest_ring->base_addr_ce_space); ++ ce_ring->base_addr_owner_space_unaligned, ++ ce_ring->base_addr_ce_space_unaligned); + kfree(pipe->dest_ring); + pipe->dest_ring = NULL; + } +@@ -987,11 +990,12 @@ void ath11k_ce_free_pipes(struct ath11k_ + if (pipe->status_ring) { + desc_sz = + ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); ++ ce_ring = pipe->status_ring; + dma_free_coherent(ab->dev, + pipe->status_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, +- pipe->status_ring->base_addr_owner_space, +- pipe->status_ring->base_addr_ce_space); ++ ce_ring->base_addr_owner_space_unaligned, ++ ce_ring->base_addr_ce_space_unaligned); + kfree(pipe->status_ring); + pipe->status_ring = NULL; + } diff --git a/package/kernel/mac80211/patches/ath11k/0071-ath11k-Fix-unused-but-set-parameter-error.patch b/package/kernel/mac80211/patches/ath11k/0071-ath11k-Fix-unused-but-set-parameter-error.patch new file mode 100644 index 00000000000000..7fb7011df4b8c2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0071-ath11k-Fix-unused-but-set-parameter-error.patch @@ -0,0 +1,30 @@ +From 624e0a3170309eeb5b729f7a43c1ba3234325f02 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Thu, 11 Nov 2021 11:22:47 +0530 +Subject: [PATCH] ath11k: Fix 'unused-but-set-parameter' error + +Below compilation error is reported when built with W=1, + +drivers/net/wireless/ath/ath11k/mac.c:5408:22: error: parameter 'changed_flags' set but not used [-Werror,-Wunused-but-set-parameter] + +changed_flags is set, but left unused. So, remove unnecessary set. +Compile tested only. + +Reported-by: kernel test robot +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636609967-5114-1-git-send-email-quic_seevalam@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5971,7 +5971,6 @@ static void ath11k_mac_op_configure_filt + + mutex_lock(&ar->conf_mutex); + +- changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + ar->filter_flags = *total_flags; + diff --git a/package/kernel/mac80211/patches/ath11k/0072-ath11k-fix-firmware-crash-during-channel-switch.patch b/package/kernel/mac80211/patches/ath11k/0072-ath11k-fix-firmware-crash-during-channel-switch.patch new file mode 100644 index 00000000000000..02c3314b303ef5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0072-ath11k-fix-firmware-crash-during-channel-switch.patch @@ -0,0 +1,65 @@ +From f187fe8e3bc65cc4d7b0916947e2d6cd65d9cd3a Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Thu, 11 Nov 2021 20:50:02 +0530 +Subject: [PATCH] ath11k: fix firmware crash during channel switch + +Currently the updated bandwidth for the peer will be configured +to the firmware after channel switch from the sta_rc_update_wk. +If the updated bandwidth is greater than the configured peer phymode +during the peer assoc may result firmware assert. + +For example, initially AP is in HE40 mode and the peer phymode is +configured as MODE_11AX_HE40 during peer assoc. Now user change the +channel width to HE80 then, the peer bandwidth will be updated as +HE80 to the firmware. + +This will trigger firmware assert due to peer bandwidth is greater than +the peer phymode. + +Fix this issue by sending peer assoc command before setting the updated +peer bandwith to firmware. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636644002-25446-1-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 26 +++++++++++++++++++++----- + 1 file changed, 21 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -3930,11 +3930,27 @@ static void ath11k_sta_rc_update_wk(stru + ath11k_mac_max_he_nss(he_mcs_mask))); + + if (changed & IEEE80211_RC_BW_CHANGED) { +- err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, +- WMI_PEER_CHWIDTH, bw); +- if (err) +- ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", +- sta->addr, bw, err); ++ /* Send peer assoc command before set peer bandwidth param to ++ * avoid the mismatch between the peer phymode and the peer ++ * bandwidth. ++ */ ++ ath11k_peer_assoc_prepare(ar, arvif->vif, sta, &peer_arg, true); ++ ++ peer_arg.is_assoc = false; ++ err = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); ++ if (err) { ++ ath11k_warn(ar->ab, "failed to send peer assoc for STA %pM vdev %i: %d\n", ++ sta->addr, arvif->vdev_id, err); ++ } else if (wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { ++ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, ++ WMI_PEER_CHWIDTH, bw); ++ if (err) ++ ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", ++ sta->addr, bw, err); ++ } else { ++ ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", ++ sta->addr, arvif->vdev_id); ++ } + } + + if (changed & IEEE80211_RC_NSS_CHANGED) { diff --git a/package/kernel/mac80211/patches/ath11k/0073-ath11k-disable-unused-CE8-interrupts-for-ipq8074.patch b/package/kernel/mac80211/patches/ath11k/0073-ath11k-disable-unused-CE8-interrupts-for-ipq8074.patch new file mode 100644 index 00000000000000..f05e91cad048ed --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0073-ath11k-disable-unused-CE8-interrupts-for-ipq8074.patch @@ -0,0 +1,38 @@ +From 2c5545bfa29dd5305fa770959890a23ea39b5e69 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:01:18 +0200 +Subject: [PATCH] ath11k: disable unused CE8 interrupts for ipq8074 + +Host driver doesn't need to process CE8 interrupts (used +by target independently) + +The volume of interrupts is huge within short interval, + CPU0 CPU1 CPU2 CPU3 +14022188 0 0 0 GIC 71 Edge ce8 + +Hence disabling unused CE8 interrupt will improve CPU usage. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-2-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/ce.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/ce.c ++++ b/drivers/net/wireless/ath/ath11k/ce.c +@@ -77,7 +77,7 @@ const struct ce_attr ath11k_host_ce_conf + + /* CE8: target autonomous hif_memcpy */ + { +- .flags = CE_ATTR_FLAGS, ++ .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, diff --git a/package/kernel/mac80211/patches/ath11k/0074-ath11k-allocate-dst-ring-descriptors-from-cacheable-.patch b/package/kernel/mac80211/patches/ath11k/0074-ath11k-allocate-dst-ring-descriptors-from-cacheable-.patch new file mode 100644 index 00000000000000..ea2c35daa5ddcb --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0074-ath11k-allocate-dst-ring-descriptors-from-cacheable-.patch @@ -0,0 +1,227 @@ +From 6452f0a3d5651bb7edfd9c709e78973aaa4d3bfc Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:01:26 +0200 +Subject: [PATCH] ath11k: allocate dst ring descriptors from cacheable memory + +tcl_data and reo_dst rings are currently being allocated using +dma_allocate_coherent() which is non cacheable. + +Allocating ring memory from cacheable memory area allows cached descriptor +access and prefetch next descriptors to optimize CPU usage during +descriptor processing on NAPI. Based on the hardware param we can enable +or disable this feature for the corresponding platform. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Pradeep Kumar Chitrapu +Signed-off-by: Pradeep Kumar Chitrapu +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-3-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 5 ++++ + drivers/net/wireless/ath/ath11k/dp.c | 38 ++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/dp.h | 1 + + drivers/net/wireless/ath/ath11k/hal.c | 28 +++++++++++++++++-- + drivers/net/wireless/ath/ath11k/hal.h | 1 + + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 6 files changed, 67 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -84,6 +84,7 @@ static const struct ath11k_hw_params ath + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, ++ .alloc_cacheable_memory = true, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -135,6 +136,7 @@ static const struct ath11k_hw_params ath + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, ++ .alloc_cacheable_memory = true, + }, + { + .name = "qca6390 hw2.0", +@@ -185,6 +187,7 @@ static const struct ath11k_hw_params ath + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, ++ .alloc_cacheable_memory = false, + }, + { + .name = "qcn9074 hw1.0", +@@ -235,6 +238,7 @@ static const struct ath11k_hw_params ath + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = true, ++ .alloc_cacheable_memory = true, + }, + { + .name = "wcn6855 hw2.0", +@@ -285,6 +289,7 @@ static const struct ath11k_hw_params ath + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, ++ .alloc_cacheable_memory = false, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -101,8 +101,11 @@ void ath11k_dp_srng_cleanup(struct ath11 + if (!ring->vaddr_unaligned) + return; + +- dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, +- ring->paddr_unaligned); ++ if (ring->cached) ++ kfree(ring->vaddr_unaligned); ++ else ++ dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, ++ ring->paddr_unaligned); + + ring->vaddr_unaligned = NULL; + } +@@ -222,6 +225,7 @@ int ath11k_dp_srng_setup(struct ath11k_b + int entry_sz = ath11k_hal_srng_get_entrysize(ab, type); + int max_entries = ath11k_hal_srng_get_max_entries(ab, type); + int ret; ++ bool cached = false; + + if (max_entries < 0 || entry_sz < 0) + return -EINVAL; +@@ -230,9 +234,28 @@ int ath11k_dp_srng_setup(struct ath11k_b + num_entries = max_entries; + + ring->size = (num_entries * entry_sz) + HAL_RING_BASE_ALIGN - 1; +- ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, +- &ring->paddr_unaligned, +- GFP_KERNEL); ++ ++ if (ab->hw_params.alloc_cacheable_memory) { ++ /* Allocate the reo dst and tx completion rings from cacheable memory */ ++ switch (type) { ++ case HAL_REO_DST: ++ cached = true; ++ break; ++ default: ++ cached = false; ++ } ++ ++ if (cached) { ++ ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL); ++ ring->paddr_unaligned = virt_to_phys(ring->vaddr_unaligned); ++ } ++ } ++ ++ if (!cached) ++ ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, ++ &ring->paddr_unaligned, ++ GFP_KERNEL); ++ + if (!ring->vaddr_unaligned) + return -ENOMEM; + +@@ -292,6 +315,11 @@ int ath11k_dp_srng_setup(struct ath11k_b + return -EINVAL; + } + ++ if (cached) { ++ params.flags |= HAL_SRNG_FLAGS_CACHED; ++ ring->cached = 1; ++ } ++ + ret = ath11k_hal_srng_setup(ab, type, ring_num, mac_id, ¶ms); + if (ret < 0) { + ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -64,6 +64,7 @@ struct dp_srng { + dma_addr_t paddr; + int size; + u32 ring_id; ++ u8 cached; + }; + + struct dp_rxdma_ring { +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -627,6 +627,21 @@ u32 *ath11k_hal_srng_dst_peek(struct ath + return NULL; + } + ++static void ath11k_hal_srng_prefetch_desc(struct ath11k_base *ab, ++ struct hal_srng *srng) ++{ ++ u32 *desc; ++ ++ /* prefetch only if desc is available */ ++ desc = ath11k_hal_srng_dst_peek(ab, srng); ++ if (likely(desc)) { ++ dma_sync_single_for_cpu(ab->dev, virt_to_phys(desc), ++ (srng->entry_size * sizeof(u32)), ++ DMA_FROM_DEVICE); ++ prefetch(desc); ++ } ++} ++ + u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng) + { +@@ -642,6 +657,10 @@ u32 *ath11k_hal_srng_dst_get_next_entry( + srng->u.dst_ring.tp = (srng->u.dst_ring.tp + srng->entry_size) % + srng->ring_size; + ++ /* Try to prefetch the next descriptor in the ring */ ++ if (srng->flags & HAL_SRNG_FLAGS_CACHED) ++ ath11k_hal_srng_prefetch_desc(ab, srng); ++ + return desc; + } + +@@ -775,11 +794,16 @@ void ath11k_hal_srng_access_begin(struct + { + lockdep_assert_held(&srng->lock); + +- if (srng->ring_dir == HAL_SRNG_DIR_SRC) ++ if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.cached_tp = + *(volatile u32 *)srng->u.src_ring.tp_addr; +- else ++ } else { + srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; ++ ++ /* Try to prefetch the next descriptor in the ring */ ++ if (srng->flags & HAL_SRNG_FLAGS_CACHED) ++ ath11k_hal_srng_prefetch_desc(ab, srng); ++ } + } + + /* Update cached ring head/tail pointers to HW. ath11k_hal_srng_access_begin() +--- a/drivers/net/wireless/ath/ath11k/hal.h ++++ b/drivers/net/wireless/ath/ath11k/hal.h +@@ -513,6 +513,7 @@ enum hal_srng_dir { + #define HAL_SRNG_FLAGS_DATA_TLV_SWAP 0x00000020 + #define HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN 0x00010000 + #define HAL_SRNG_FLAGS_MSI_INTR 0x00020000 ++#define HAL_SRNG_FLAGS_CACHED 0x20000000 + #define HAL_SRNG_FLAGS_LMAC_RING 0x80000000 + + #define HAL_SRNG_TLV_HDR_TAG GENMASK(9, 1) +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -178,6 +178,7 @@ struct ath11k_hw_params { + u8 max_tx_ring; + const struct ath11k_hw_hal_params *hal_params; + bool supports_dynamic_smps_6ghz; ++ bool alloc_cacheable_memory; + }; + + struct ath11k_hw_ops { diff --git a/package/kernel/mac80211/patches/ath11k/0075-ath11k-modify-dp_rx-desc-access-wrapper-calls-inline.patch b/package/kernel/mac80211/patches/ath11k/0075-ath11k-modify-dp_rx-desc-access-wrapper-calls-inline.patch new file mode 100644 index 00000000000000..9a610f9b81bd11 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0075-ath11k-modify-dp_rx-desc-access-wrapper-calls-inline.patch @@ -0,0 +1,285 @@ +From 5e76fe03dbf9f9dbc4fd454283b02594226c0718 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:02:03 +0200 +Subject: [PATCH] ath11k: modify dp_rx desc access wrapper calls inline + +In data path, to reduce the CPU cycles spending on descriptor access +wrapper function, changed those functions as static inline. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-4-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 114 ++++++++++++------------ + 1 file changed, 59 insertions(+), 55 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -20,13 +20,15 @@ + + #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) + +-static u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc) ++static inline ++u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_hdr_status(desc); + } + +-static enum hal_encrypt_type ath11k_dp_rx_h_mpdu_start_enctype(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline ++enum hal_encrypt_type ath11k_dp_rx_h_mpdu_start_enctype(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + if (!ab->hw_params.hw_ops->rx_desc_encrypt_valid(desc)) + return HAL_ENCRYPT_TYPE_OPEN; +@@ -34,32 +36,34 @@ static enum hal_encrypt_type ath11k_dp_r + return ab->hw_params.hw_ops->rx_desc_get_encrypt_type(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_start_decap_type(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_msdu_start_decap_type(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_decap_type(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_start_mesh_ctl_present(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline ++u8 ath11k_dp_rx_h_msdu_start_mesh_ctl_present(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mesh_ctl(desc); + } + +-static bool ath11k_dp_rx_h_mpdu_start_seq_ctrl_valid(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline ++bool ath11k_dp_rx_h_mpdu_start_seq_ctrl_valid(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mpdu_seq_ctl_vld(desc); + } + +-static bool ath11k_dp_rx_h_mpdu_start_fc_valid(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline bool ath11k_dp_rx_h_mpdu_start_fc_valid(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mpdu_fc_valid(desc); + } + +-static bool ath11k_dp_rx_h_mpdu_start_more_frags(struct ath11k_base *ab, +- struct sk_buff *skb) ++static inline bool ath11k_dp_rx_h_mpdu_start_more_frags(struct ath11k_base *ab, ++ struct sk_buff *skb) + { + struct ieee80211_hdr *hdr; + +@@ -67,8 +71,8 @@ static bool ath11k_dp_rx_h_mpdu_start_mo + return ieee80211_has_morefrags(hdr->frame_control); + } + +-static u16 ath11k_dp_rx_h_mpdu_start_frag_no(struct ath11k_base *ab, +- struct sk_buff *skb) ++static inline u16 ath11k_dp_rx_h_mpdu_start_frag_no(struct ath11k_base *ab, ++ struct sk_buff *skb) + { + struct ieee80211_hdr *hdr; + +@@ -76,37 +80,37 @@ static u16 ath11k_dp_rx_h_mpdu_start_fra + return le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; + } + +-static u16 ath11k_dp_rx_h_mpdu_start_seq_no(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u16 ath11k_dp_rx_h_mpdu_start_seq_no(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mpdu_start_seq_no(desc); + } + +-static void *ath11k_dp_rx_get_attention(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline void *ath11k_dp_rx_get_attention(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_attention(desc); + } + +-static bool ath11k_dp_rx_h_attn_msdu_done(struct rx_attention *attn) ++static inline bool ath11k_dp_rx_h_attn_msdu_done(struct rx_attention *attn) + { + return !!FIELD_GET(RX_ATTENTION_INFO2_MSDU_DONE, + __le32_to_cpu(attn->info2)); + } + +-static bool ath11k_dp_rx_h_attn_l4_cksum_fail(struct rx_attention *attn) ++static inline bool ath11k_dp_rx_h_attn_l4_cksum_fail(struct rx_attention *attn) + { + return !!FIELD_GET(RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL, + __le32_to_cpu(attn->info1)); + } + +-static bool ath11k_dp_rx_h_attn_ip_cksum_fail(struct rx_attention *attn) ++static inline bool ath11k_dp_rx_h_attn_ip_cksum_fail(struct rx_attention *attn) + { + return !!FIELD_GET(RX_ATTENTION_INFO1_IP_CKSUM_FAIL, + __le32_to_cpu(attn->info1)); + } + +-static bool ath11k_dp_rx_h_attn_is_decrypted(struct rx_attention *attn) ++static inline bool ath11k_dp_rx_h_attn_is_decrypted(struct rx_attention *attn) + { + return (FIELD_GET(RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE, + __le32_to_cpu(attn->info2)) == +@@ -154,68 +158,68 @@ static bool ath11k_dp_rx_h_attn_msdu_len + return errmap & DP_RX_MPDU_ERR_MSDU_LEN; + } + +-static u16 ath11k_dp_rx_h_msdu_start_msdu_len(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u16 ath11k_dp_rx_h_msdu_start_msdu_len(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_msdu_len(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_start_sgi(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_msdu_start_sgi(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_msdu_sgi(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_start_rate_mcs(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_msdu_start_rate_mcs(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_msdu_rate_mcs(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_start_rx_bw(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_msdu_start_rx_bw(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_msdu_rx_bw(desc); + } + +-static u32 ath11k_dp_rx_h_msdu_start_freq(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u32 ath11k_dp_rx_h_msdu_start_freq(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_msdu_freq(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_start_pkt_type(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_msdu_start_pkt_type(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_msdu_pkt_type(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_start_nss(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_msdu_start_nss(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return hweight8(ab->hw_params.hw_ops->rx_desc_get_msdu_nss(desc)); + } + +-static u8 ath11k_dp_rx_h_mpdu_start_tid(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_mpdu_start_tid(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mpdu_tid(desc); + } + +-static u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mpdu_peer_id(desc); + } + +-static u8 ath11k_dp_rx_h_msdu_end_l3pad(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline u8 ath11k_dp_rx_h_msdu_end_l3pad(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_l3_pad_bytes(desc); + } + +-static bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++static inline bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_first_msdu(desc); + } +@@ -233,14 +237,14 @@ static void ath11k_dp_rx_desc_end_tlv_co + ab->hw_params.hw_ops->rx_desc_copy_attn_end_tlv(fdesc, ldesc); + } + +-static u32 ath11k_dp_rxdesc_get_mpdulen_err(struct rx_attention *attn) ++static inline u32 ath11k_dp_rxdesc_get_mpdulen_err(struct rx_attention *attn) + { + return FIELD_GET(RX_ATTENTION_INFO1_MPDU_LEN_ERR, + __le32_to_cpu(attn->info1)); + } + +-static u8 *ath11k_dp_rxdesc_get_80211hdr(struct ath11k_base *ab, +- struct hal_rx_desc *rx_desc) ++static inline u8 *ath11k_dp_rxdesc_get_80211hdr(struct ath11k_base *ab, ++ struct hal_rx_desc *rx_desc) + { + u8 *rx_pkt_hdr; + +@@ -249,8 +253,8 @@ static u8 *ath11k_dp_rxdesc_get_80211hdr + return rx_pkt_hdr; + } + +-static bool ath11k_dp_rxdesc_mpdu_valid(struct ath11k_base *ab, +- struct hal_rx_desc *rx_desc) ++static inline bool ath11k_dp_rxdesc_mpdu_valid(struct ath11k_base *ab, ++ struct hal_rx_desc *rx_desc) + { + u32 tlv_tag; + +@@ -259,15 +263,15 @@ static bool ath11k_dp_rxdesc_mpdu_valid( + return tlv_tag == HAL_RX_MPDU_START; + } + +-static u32 ath11k_dp_rxdesc_get_ppduid(struct ath11k_base *ab, +- struct hal_rx_desc *rx_desc) ++static inline u32 ath11k_dp_rxdesc_get_ppduid(struct ath11k_base *ab, ++ struct hal_rx_desc *rx_desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mpdu_ppdu_id(rx_desc); + } + +-static void ath11k_dp_rxdesc_set_msdu_len(struct ath11k_base *ab, +- struct hal_rx_desc *desc, +- u16 len) ++static inline void ath11k_dp_rxdesc_set_msdu_len(struct ath11k_base *ab, ++ struct hal_rx_desc *desc, ++ u16 len) + { + ab->hw_params.hw_ops->rx_desc_set_msdu_len(desc, len); + } diff --git a/package/kernel/mac80211/patches/ath11k/0076-ath11k-avoid-additional-access-to-ath11k_hal_srng_ds.patch b/package/kernel/mac80211/patches/ath11k/0076-ath11k-avoid-additional-access-to-ath11k_hal_srng_ds.patch new file mode 100644 index 00000000000000..d8a5b30eb60ebc --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0076-ath11k-avoid-additional-access-to-ath11k_hal_srng_ds.patch @@ -0,0 +1,55 @@ +From a1775e732eb90486519de3813b83a11b7fcee2d0 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:02:12 +0200 +Subject: [PATCH] ath11k: avoid additional access to + ath11k_hal_srng_dst_num_free + +In ath11k_dp_process_rx(), after processing rx_desc from +ath11k_hal_srng_dst_get_next_entry(), ath11k_hal_srng_dst_num_free() +is accessed everytime because of done flag is not set. + +To avoid this additional access to ath11k_hal_srng_dst_num_free(), +increment total_msdu_reaped only when continuation is not set and +update done flag correspondingly. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-5-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2701,7 +2701,6 @@ try_again: + DMA_FROM_DEVICE); + + num_buffs_reaped[mac_id]++; +- total_msdu_reaped++; + + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc.info0); +@@ -2728,10 +2727,15 @@ try_again: + rxcb->mac_id = mac_id; + __skb_queue_tail(&msdu_list, msdu); + +- if (total_msdu_reaped >= quota && !rxcb->is_continuation) { ++ if (rxcb->is_continuation) { ++ done = false; ++ } else { ++ total_msdu_reaped++; + done = true; +- break; + } ++ ++ if (total_msdu_reaped >= budget) ++ break; + } + + /* Hw might have updated the head pointer after we cached it. diff --git a/package/kernel/mac80211/patches/ath11k/0077-ath11k-avoid-active-pdev-check-for-each-msdu.patch b/package/kernel/mac80211/patches/ath11k/0077-ath11k-avoid-active-pdev-check-for-each-msdu.patch new file mode 100644 index 00000000000000..16ca894000f7b4 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0077-ath11k-avoid-active-pdev-check-for-each-msdu.patch @@ -0,0 +1,174 @@ +From c4d12cb37ea2e6c2b70880350d7bf1bbbd825c6c Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:02:18 +0200 +Subject: [PATCH] ath11k: avoid active pdev check for each msdu + +The Active Pdev and CAC check are done for each msdu in +ath11k_dp_rx_process_received_packets which is a overhead. +To avoid this overhead, collect all msdus in a per mac msdu +list and pass to function. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-6-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 70 ++++++++++++------------- + 1 file changed, 34 insertions(+), 36 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2600,13 +2600,11 @@ free_out: + static void ath11k_dp_rx_process_received_packets(struct ath11k_base *ab, + struct napi_struct *napi, + struct sk_buff_head *msdu_list, +- int *quota, int ring_id) ++ int *quota, int mac_id) + { +- struct ath11k_skb_rxcb *rxcb; + struct sk_buff *msdu; + struct ath11k *ar; + struct ieee80211_rx_status rx_status = {0}; +- u8 mac_id; + int ret; + + if (skb_queue_empty(msdu_list)) +@@ -2614,20 +2612,20 @@ static void ath11k_dp_rx_process_receive + + rcu_read_lock(); + +- while (*quota && (msdu = __skb_dequeue(msdu_list))) { +- rxcb = ATH11K_SKB_RXCB(msdu); +- mac_id = rxcb->mac_id; +- ar = ab->pdevs[mac_id].ar; +- if (!rcu_dereference(ab->pdevs_active[mac_id])) { +- dev_kfree_skb_any(msdu); +- continue; +- } ++ ar = ab->pdevs[mac_id].ar; ++ if (!rcu_dereference(ab->pdevs_active[mac_id])) { ++ __skb_queue_purge(msdu_list); ++ rcu_read_unlock(); ++ return; ++ } + +- if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { +- dev_kfree_skb_any(msdu); +- continue; +- } ++ if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { ++ __skb_queue_purge(msdu_list); ++ rcu_read_unlock(); ++ return; ++ } + ++ while ((msdu = __skb_dequeue(msdu_list))) { + ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status); + if (ret) { + ath11k_dbg(ab, ATH11K_DBG_DATA, +@@ -2649,7 +2647,7 @@ int ath11k_dp_process_rx(struct ath11k_b + struct ath11k_dp *dp = &ab->dp; + struct dp_rxdma_ring *rx_ring; + int num_buffs_reaped[MAX_RADIOS] = {0}; +- struct sk_buff_head msdu_list; ++ struct sk_buff_head msdu_list[MAX_RADIOS]; + struct ath11k_skb_rxcb *rxcb; + int total_msdu_reaped = 0; + struct hal_srng *srng; +@@ -2658,10 +2656,13 @@ int ath11k_dp_process_rx(struct ath11k_b + bool done = false; + int buf_id, mac_id; + struct ath11k *ar; +- u32 *rx_desc; ++ struct hal_reo_dest_ring *desc; ++ enum hal_reo_dest_ring_push_reason push_reason; ++ u32 cookie; + int i; + +- __skb_queue_head_init(&msdu_list); ++ for (i = 0; i < MAX_RADIOS; i++) ++ __skb_queue_head_init(&msdu_list[i]); + + srng = &ab->hal.srng_list[dp->reo_dst_ring[ring_id].ring_id]; + +@@ -2670,13 +2671,11 @@ int ath11k_dp_process_rx(struct ath11k_b + ath11k_hal_srng_access_begin(ab, srng); + + try_again: +- while ((rx_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { +- struct hal_reo_dest_ring desc = *(struct hal_reo_dest_ring *)rx_desc; +- enum hal_reo_dest_ring_push_reason push_reason; +- u32 cookie; +- ++ while (likely(desc = ++ (struct hal_reo_dest_ring *)ath11k_hal_srng_dst_get_next_entry(ab, ++ srng))) { + cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, +- desc.buf_addr_info.info1); ++ desc->buf_addr_info.info1); + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + cookie); + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, cookie); +@@ -2703,7 +2702,7 @@ try_again: + num_buffs_reaped[mac_id]++; + + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, +- desc.info0); ++ desc->info0); + if (push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { + dev_kfree_skb_any(msdu); +@@ -2711,21 +2710,21 @@ try_again: + continue; + } + +- rxcb->is_first_msdu = !!(desc.rx_msdu_info.info0 & ++ rxcb->is_first_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); +- rxcb->is_last_msdu = !!(desc.rx_msdu_info.info0 & ++ rxcb->is_last_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); +- rxcb->is_continuation = !!(desc.rx_msdu_info.info0 & ++ rxcb->is_continuation = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); + rxcb->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID, +- desc.rx_mpdu_info.meta_data); ++ desc->rx_mpdu_info.meta_data); + rxcb->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM, +- desc.rx_mpdu_info.info0); ++ desc->rx_mpdu_info.info0); + rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM, +- desc.info0); ++ desc->info0); + + rxcb->mac_id = mac_id; +- __skb_queue_tail(&msdu_list, msdu); ++ __skb_queue_tail(&msdu_list[mac_id], msdu); + + if (rxcb->is_continuation) { + done = false; +@@ -2760,16 +2759,15 @@ try_again: + if (!num_buffs_reaped[i]) + continue; + ++ ath11k_dp_rx_process_received_packets(ab, napi, &msdu_list[i], ++ "a, i); ++ + ar = ab->pdevs[i].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], + ab->hw_params.hal_params->rx_buf_rbm); + } +- +- ath11k_dp_rx_process_received_packets(ab, napi, &msdu_list, +- "a, ring_id); +- + exit: + return budget - quota; + } diff --git a/package/kernel/mac80211/patches/ath11k/0078-ath11k-remove-usage-quota-while-processing-rx-packet.patch b/package/kernel/mac80211/patches/ath11k/0078-ath11k-remove-usage-quota-while-processing-rx-packet.patch new file mode 100644 index 00000000000000..80a610349b8d13 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0078-ath11k-remove-usage-quota-while-processing-rx-packet.patch @@ -0,0 +1,69 @@ +From db2ecf9f0567a8f1a96f23a392cc5a30eaec4369 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:02:24 +0200 +Subject: [PATCH] ath11k: remove usage quota while processing rx packets + +The usage of quota variable inside ath11k_dp_rx_process_received_packets() +is redundant. Since we would queue only max packets to the list before +calling this function so it would never exceed quota. Hence removing +usage of quota variable. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-7-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2600,7 +2600,7 @@ free_out: + static void ath11k_dp_rx_process_received_packets(struct ath11k_base *ab, + struct napi_struct *napi, + struct sk_buff_head *msdu_list, +- int *quota, int mac_id) ++ int mac_id) + { + struct sk_buff *msdu; + struct ath11k *ar; +@@ -2635,7 +2635,6 @@ static void ath11k_dp_rx_process_receive + } + + ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); +- (*quota)--; + } + + rcu_read_unlock(); +@@ -2652,7 +2651,6 @@ int ath11k_dp_process_rx(struct ath11k_b + int total_msdu_reaped = 0; + struct hal_srng *srng; + struct sk_buff *msdu; +- int quota = budget; + bool done = false; + int buf_id, mac_id; + struct ath11k *ar; +@@ -2759,8 +2757,7 @@ try_again: + if (!num_buffs_reaped[i]) + continue; + +- ath11k_dp_rx_process_received_packets(ab, napi, &msdu_list[i], +- "a, i); ++ ath11k_dp_rx_process_received_packets(ab, napi, &msdu_list[i], i); + + ar = ab->pdevs[i].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; +@@ -2769,7 +2766,7 @@ try_again: + ab->hw_params.hal_params->rx_buf_rbm); + } + exit: +- return budget - quota; ++ return total_msdu_reaped; + } + + static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, diff --git a/package/kernel/mac80211/patches/ath11k/0079-ath11k-add-branch-predictors-in-process_rx.patch b/package/kernel/mac80211/patches/ath11k/0079-ath11k-add-branch-predictors-in-process_rx.patch new file mode 100644 index 00000000000000..ca2c55540b2dee --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0079-ath11k-add-branch-predictors-in-process_rx.patch @@ -0,0 +1,102 @@ +From 400588039a17a460292eb974ebba5811b8cbdb91 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:02:31 +0200 +Subject: [PATCH] ath11k: add branch predictors in process_rx + +In datapath, add branch predictors where required in the process rx(). +This protects high value rx path without having performance overhead. +Also while processing rx packets, the pointer that is returned by +rcu_dereference() is not dereferenced. so it is preferable to use +rcu_access_pointer() here. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-8-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 24 +++++++++--------------- + 1 file changed, 9 insertions(+), 15 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2610,24 +2610,20 @@ static void ath11k_dp_rx_process_receive + if (skb_queue_empty(msdu_list)) + return; + +- rcu_read_lock(); +- +- ar = ab->pdevs[mac_id].ar; +- if (!rcu_dereference(ab->pdevs_active[mac_id])) { ++ if (unlikely(!rcu_access_pointer(ab->pdevs_active[mac_id]))) { + __skb_queue_purge(msdu_list); +- rcu_read_unlock(); + return; + } + +- if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { ++ ar = ab->pdevs[mac_id].ar; ++ if (unlikely(test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags))) { + __skb_queue_purge(msdu_list); +- rcu_read_unlock(); + return; + } + + while ((msdu = __skb_dequeue(msdu_list))) { + ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status); +- if (ret) { ++ if (unlikely(ret)) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "Unable to process msdu %d", ret); + dev_kfree_skb_any(msdu); +@@ -2636,8 +2632,6 @@ static void ath11k_dp_rx_process_receive + + ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); + } +- +- rcu_read_unlock(); + } + + int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, +@@ -2682,7 +2676,7 @@ try_again: + rx_ring = &ar->dp.rx_refill_buf_ring; + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); +- if (!msdu) { ++ if (unlikely(!msdu)) { + ath11k_warn(ab, "frame rx with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); +@@ -2701,8 +2695,8 @@ try_again: + + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc->info0); +- if (push_reason != +- HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { ++ if (unlikely(push_reason != ++ HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION)) { + dev_kfree_skb_any(msdu); + ab->soc_stats.hal_reo_error[dp->reo_dst_ring[ring_id].ring_id]++; + continue; +@@ -2741,7 +2735,7 @@ try_again: + * head pointer so that we can reap complete MPDU in the current + * rx processing. + */ +- if (!done && ath11k_hal_srng_dst_num_free(ab, srng, true)) { ++ if (unlikely(!done && ath11k_hal_srng_dst_num_free(ab, srng, true))) { + ath11k_hal_srng_access_end(ab, srng); + goto try_again; + } +@@ -2750,7 +2744,7 @@ try_again: + + spin_unlock_bh(&srng->lock); + +- if (!total_msdu_reaped) ++ if (unlikely(!total_msdu_reaped)) + goto exit; + + for (i = 0; i < ab->num_radios; i++) { diff --git a/package/kernel/mac80211/patches/ath11k/0080-ath11k-allocate-HAL_WBM2SW_RELEASE-ring-from-cacheab.patch b/package/kernel/mac80211/patches/ath11k/0080-ath11k-allocate-HAL_WBM2SW_RELEASE-ring-from-cacheab.patch new file mode 100644 index 00000000000000..f1c0513cf1ca87 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0080-ath11k-allocate-HAL_WBM2SW_RELEASE-ring-from-cacheab.patch @@ -0,0 +1,32 @@ +From d0e2523bfa9cb391fe966b0b6948c7e438981361 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:02:38 +0200 +Subject: [PATCH] ath11k: allocate HAL_WBM2SW_RELEASE ring from cacheable + memory + +Similar to REO destination ring, also allocate HAL_WBM2SW_RELEASE +from cacheable memory so that descriptors could be prefetched during +tx completion handling. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 v2 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-9-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -239,6 +239,7 @@ int ath11k_dp_srng_setup(struct ath11k_b + /* Allocate the reo dst and tx completion rings from cacheable memory */ + switch (type) { + case HAL_REO_DST: ++ case HAL_WBM2SW_RELEASE: + cached = true; + break; + default: diff --git a/package/kernel/mac80211/patches/ath11k/0081-ath11k-remove-mod-operator-in-dst-ring-processing.patch b/package/kernel/mac80211/patches/ath11k/0081-ath11k-remove-mod-operator-in-dst-ring-processing.patch new file mode 100644 index 00000000000000..b00a73a1c7a6b2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0081-ath11k-remove-mod-operator-in-dst-ring-processing.patch @@ -0,0 +1,36 @@ +From a8508bf7ced2e43f30b46333f09cbc79a1675616 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:02:45 +0200 +Subject: [PATCH] ath11k: remove mod operator in dst ring processing + +Replace use of mod operator with a manual wrap around +to avoid additional cost of using mod operation. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 v2 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-10-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/hal.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -654,8 +654,11 @@ u32 *ath11k_hal_srng_dst_get_next_entry( + + desc = srng->ring_base_vaddr + srng->u.dst_ring.tp; + +- srng->u.dst_ring.tp = (srng->u.dst_ring.tp + srng->entry_size) % +- srng->ring_size; ++ srng->u.dst_ring.tp += srng->entry_size; ++ ++ /* wrap around to start of ring*/ ++ if (srng->u.dst_ring.tp == srng->ring_size) ++ srng->u.dst_ring.tp = 0; + + /* Try to prefetch the next descriptor in the ring */ + if (srng->flags & HAL_SRNG_FLAGS_CACHED) diff --git a/package/kernel/mac80211/patches/ath11k/0082-ath11k-avoid-while-loop-in-ring-selection-of-tx-comp.patch b/package/kernel/mac80211/patches/ath11k/0082-ath11k-avoid-while-loop-in-ring-selection-of-tx-comp.patch new file mode 100644 index 00000000000000..dae9e00c3bba26 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0082-ath11k-avoid-while-loop-in-ring-selection-of-tx-comp.patch @@ -0,0 +1,43 @@ +From cbfbed495d3289d5a0bc7c614cea639008086cfe Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:05:58 +0200 +Subject: [PATCH] ath11k: avoid while loop in ring selection of tx completion + interrupt + +Currently while loop is used to find the tx completion ring number and +it is not required since the tx ring mask and the group id can be combined +to directly fetch the ring number. Hence remove the while loop +and directly get the ring number from tx mask and group id. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 v2 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-11-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -771,13 +771,12 @@ int ath11k_dp_service_srng(struct ath11k + const struct ath11k_hw_hal_params *hal_params; + int grp_id = irq_grp->grp_id; + int work_done = 0; +- int i = 0, j; ++ int i, j; + int tot_work_done = 0; + +- while (ab->hw_params.ring_mask->tx[grp_id] >> i) { +- if (ab->hw_params.ring_mask->tx[grp_id] & BIT(i)) +- ath11k_dp_tx_completion_handler(ab, i); +- i++; ++ if (ab->hw_params.ring_mask->tx[grp_id]) { ++ i = __fls(ab->hw_params.ring_mask->tx[grp_id]); ++ ath11k_dp_tx_completion_handler(ab, i); + } + + if (ab->hw_params.ring_mask->rx_err[grp_id]) { diff --git a/package/kernel/mac80211/patches/ath11k/0083-ath11k-add-branch-predictors-in-dp_tx-path.patch b/package/kernel/mac80211/patches/ath11k/0083-ath11k-add-branch-predictors-in-dp_tx-path.patch new file mode 100644 index 00000000000000..e09322f8ae9da2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0083-ath11k-add-branch-predictors-in-dp_tx-path.patch @@ -0,0 +1,201 @@ +From bcef57ea400cc20a5389fa0e38d61063331558f8 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:06:04 +0200 +Subject: [PATCH] ath11k: add branch predictors in dp_tx path + +Add branch prediction in dp_tx code path in tx and tx completion handlers. +Also in ath11k_dp_tx_complete_msdu , the pointer that is returned by +rcu_dereference() is not dereferenced. so it is preferable to use +rcu_access_pointer() here. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 v2 + +Co-developed-by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-12-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 52 ++++++++++++------------- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + 2 files changed, 25 insertions(+), 29 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -95,11 +95,11 @@ int ath11k_dp_tx(struct ath11k *ar, stru + u8 ring_selector = 0, ring_map = 0; + bool tcl_ring_retry; + +- if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) ++ if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))) + return -ESHUTDOWN; + +- if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && +- !ieee80211_is_data(hdr->frame_control)) ++ if (unlikely(!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && ++ !ieee80211_is_data(hdr->frame_control))) + return -ENOTSUPP; + + pool_id = skb_get_queue_mapping(skb) & (ATH11K_HW_MAX_QUEUES - 1); +@@ -127,7 +127,7 @@ tcl_ring_sel: + DP_TX_IDR_SIZE - 1, GFP_ATOMIC); + spin_unlock_bh(&tx_ring->tx_idr_lock); + +- if (ret < 0) { ++ if (unlikely(ret < 0)) { + if (ring_map == (BIT(ab->hw_params.max_tx_ring) - 1)) { + atomic_inc(&ab->soc_stats.tx_err.misc_fail); + return -ENOSPC; +@@ -152,7 +152,7 @@ tcl_ring_sel: + ti.meta_data_flags = arvif->tcl_metadata; + } + +- if (ti.encap_type == HAL_TCL_ENCAP_TYPE_RAW) { ++ if (unlikely(ti.encap_type == HAL_TCL_ENCAP_TYPE_RAW)) { + if (skb_cb->flags & ATH11K_SKB_CIPHER_SET) { + ti.encrypt_type = + ath11k_dp_tx_get_encrypt_type(skb_cb->cipher); +@@ -173,8 +173,8 @@ tcl_ring_sel: + ti.bss_ast_idx = arvif->ast_idx; + ti.dscp_tid_tbl_idx = 0; + +- if (skb->ip_summed == CHECKSUM_PARTIAL && +- ti.encap_type != HAL_TCL_ENCAP_TYPE_RAW) { ++ if (likely(skb->ip_summed == CHECKSUM_PARTIAL && ++ ti.encap_type != HAL_TCL_ENCAP_TYPE_RAW)) { + ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_IP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP6_CKSUM_EN, 1) | +@@ -211,7 +211,7 @@ tcl_ring_sel: + } + + ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); +- if (dma_mapping_error(ab->dev, ti.paddr)) { ++ if (unlikely(dma_mapping_error(ab->dev, ti.paddr))) { + atomic_inc(&ab->soc_stats.tx_err.misc_fail); + ath11k_warn(ab, "failed to DMA map data Tx buffer\n"); + ret = -ENOMEM; +@@ -231,7 +231,7 @@ tcl_ring_sel: + ath11k_hal_srng_access_begin(ab, tcl_ring); + + hal_tcl_desc = (void *)ath11k_hal_srng_src_get_next_entry(ab, tcl_ring); +- if (!hal_tcl_desc) { ++ if (unlikely(!hal_tcl_desc)) { + /* NOTE: It is highly unlikely we'll be running out of tcl_ring + * desc because the desc is directly enqueued onto hw queue. + */ +@@ -245,7 +245,7 @@ tcl_ring_sel: + * checking this ring earlier for each pkt tx. + * Restart ring selection if some rings are not checked yet. + */ +- if (ring_map != (BIT(ab->hw_params.max_tx_ring) - 1) && ++ if (unlikely(ring_map != (BIT(ab->hw_params.max_tx_ring)) - 1) && + ab->hw_params.max_tx_ring > 1) { + tcl_ring_retry = true; + ring_selector++; +@@ -327,7 +327,7 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, ts->msdu_id); +- if (!msdu) { ++ if (unlikely(!msdu)) { + ath11k_warn(ab, "htt tx completion for unknown msdu_id %d\n", + ts->msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); +@@ -435,16 +435,14 @@ static void ath11k_dp_tx_complete_msdu(s + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + +- rcu_read_lock(); +- +- if (!rcu_dereference(ab->pdevs_active[ar->pdev_idx])) { ++ if (unlikely(!rcu_access_pointer(ab->pdevs_active[ar->pdev_idx]))) { + dev_kfree_skb_any(msdu); +- goto exit; ++ return; + } + +- if (!skb_cb->vif) { ++ if (unlikely(!skb_cb->vif)) { + dev_kfree_skb_any(msdu); +- goto exit; ++ return; + } + + info = IEEE80211_SKB_CB(msdu); +@@ -465,7 +463,7 @@ static void ath11k_dp_tx_complete_msdu(s + (info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + +- if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) { ++ if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar))) { + if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) { + if (ar->last_ppdu_id == 0) { + ar->last_ppdu_id = ts->ppdu_id; +@@ -494,9 +492,6 @@ static void ath11k_dp_tx_complete_msdu(s + */ + + ieee80211_tx_status(ar->hw, msdu); +- +-exit: +- rcu_read_unlock(); + } + + static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, +@@ -505,11 +500,11 @@ static inline void ath11k_dp_tx_status_p + { + ts->buf_rel_source = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0); +- if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && +- ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM) ++ if (unlikely(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && ++ ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) + return; + +- if (ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) ++ if (unlikely(ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW)) + return; + + ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, +@@ -556,8 +551,9 @@ void ath11k_dp_tx_completion_handler(str + ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head); + } + +- if ((ath11k_hal_srng_dst_peek(ab, status_ring) != NULL) && +- (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) == tx_ring->tx_status_tail)) { ++ if (unlikely((ath11k_hal_srng_dst_peek(ab, status_ring) != NULL) && ++ (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) == ++ tx_ring->tx_status_tail))) { + /* TODO: Process pending tx_status messages when kfifo_is_full() */ + ath11k_warn(ab, "Unable to process some of the tx_status ring desc because status_fifo is full\n"); + } +@@ -580,7 +576,7 @@ void ath11k_dp_tx_completion_handler(str + mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, desc_id); + msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, desc_id); + +- if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { ++ if (unlikely(ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW)) { + ath11k_dp_tx_process_htt_tx_complete(ab, + (void *)tx_status, + mac_id, msdu_id, +@@ -590,7 +586,7 @@ void ath11k_dp_tx_completion_handler(str + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, msdu_id); +- if (!msdu) { ++ if (unlikely(!msdu)) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5293,7 +5293,7 @@ static void ath11k_mac_op_tx(struct ieee + arsta = (struct ath11k_sta *)control->sta->drv_priv; + + ret = ath11k_dp_tx(ar, arvif, arsta, skb); +- if (ret) { ++ if (unlikely(ret)) { + ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->hw, skb); + } diff --git a/package/kernel/mac80211/patches/ath11k/0084-ath11k-avoid-unnecessary-lock-contention-in-tx_compl.patch b/package/kernel/mac80211/patches/ath11k/0084-ath11k-avoid-unnecessary-lock-contention-in-tx_compl.patch new file mode 100644 index 00000000000000..7eb791212cb9b1 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0084-ath11k-avoid-unnecessary-lock-contention-in-tx_compl.patch @@ -0,0 +1,102 @@ +From be8867cb47652418e488170785bd9ffbadae3f1f Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 12 Nov 2021 11:06:11 +0200 +Subject: [PATCH] ath11k: avoid unnecessary lock contention in tx_completion + path + +Avoid unnecessary idr_find calls before the idr_remove calls. Because +idr_remove gives the valid ptr if id is valid otherwise return NULL ptr. +So removed the idr_find before idr_remove in tx completion path. Also no +need to disable the bottom half preempt if it is already in the +bottom half context, so modify the spin_lock_bh to spin_lock in the +data tx completion path. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 v2 + +Co-developed-by: Karthikeyan Periyasamy +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Jouni Malinen +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1630560820-21905-13-git-send-email-ppranees@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 32 +++++++++++-------------- + 1 file changed, 14 insertions(+), 18 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -293,20 +293,18 @@ static void ath11k_dp_tx_free_txbuf(stru + struct sk_buff *msdu; + struct ath11k_skb_cb *skb_cb; + +- spin_lock_bh(&tx_ring->tx_idr_lock); +- msdu = idr_find(&tx_ring->txbuf_idr, msdu_id); +- if (!msdu) { ++ spin_lock(&tx_ring->tx_idr_lock); ++ msdu = idr_remove(&tx_ring->txbuf_idr, msdu_id); ++ spin_unlock(&tx_ring->tx_idr_lock); ++ ++ if (unlikely(!msdu)) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); +- spin_unlock_bh(&tx_ring->tx_idr_lock); + return; + } + + skb_cb = ATH11K_SKB_CB(msdu); + +- idr_remove(&tx_ring->txbuf_idr, msdu_id); +- spin_unlock_bh(&tx_ring->tx_idr_lock); +- + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + dev_kfree_skb_any(msdu); + +@@ -325,12 +323,13 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + struct ath11k_skb_cb *skb_cb; + struct ath11k *ar; + +- spin_lock_bh(&tx_ring->tx_idr_lock); +- msdu = idr_find(&tx_ring->txbuf_idr, ts->msdu_id); ++ spin_lock(&tx_ring->tx_idr_lock); ++ msdu = idr_remove(&tx_ring->txbuf_idr, ts->msdu_id); ++ spin_unlock(&tx_ring->tx_idr_lock); ++ + if (unlikely(!msdu)) { + ath11k_warn(ab, "htt tx completion for unknown msdu_id %d\n", + ts->msdu_id); +- spin_unlock_bh(&tx_ring->tx_idr_lock); + return; + } + +@@ -339,9 +338,6 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + + ar = skb_cb->ar; + +- idr_remove(&tx_ring->txbuf_idr, ts->msdu_id); +- spin_unlock_bh(&tx_ring->tx_idr_lock); +- + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + +@@ -584,16 +580,16 @@ void ath11k_dp_tx_completion_handler(str + continue; + } + +- spin_lock_bh(&tx_ring->tx_idr_lock); +- msdu = idr_find(&tx_ring->txbuf_idr, msdu_id); ++ spin_lock(&tx_ring->tx_idr_lock); ++ msdu = idr_remove(&tx_ring->txbuf_idr, msdu_id); + if (unlikely(!msdu)) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); +- spin_unlock_bh(&tx_ring->tx_idr_lock); ++ spin_unlock(&tx_ring->tx_idr_lock); + continue; + } +- idr_remove(&tx_ring->txbuf_idr, msdu_id); +- spin_unlock_bh(&tx_ring->tx_idr_lock); ++ ++ spin_unlock(&tx_ring->tx_idr_lock); + + ar = ab->pdevs[mac_id].ar; + diff --git a/package/kernel/mac80211/patches/ath11k/0085-ath11k-enable-IEEE80211_VHT_EXT_NSS_BW_CAPABLE-if-NS.patch b/package/kernel/mac80211/patches/ath11k/0085-ath11k-enable-IEEE80211_VHT_EXT_NSS_BW_CAPABLE-if-NS.patch new file mode 100644 index 00000000000000..036779d5d7d6fc --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0085-ath11k-enable-IEEE80211_VHT_EXT_NSS_BW_CAPABLE-if-NS.patch @@ -0,0 +1,48 @@ +From 78406044bdd0cc8987bc082b76867c63ab1c6af8 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 13 Oct 2021 03:37:04 -0400 +Subject: [PATCH] ath11k: enable IEEE80211_VHT_EXT_NSS_BW_CAPABLE if NSS ratio + enabled +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When NSS ratio enabled reported by firmware, SUPPORTS_VHT_EXT_NSS_BW +is set in ath11k, meanwhile IEEE80211_VHT_EXT_NSS_BW_CAPABLE also +need to be set, otherwise it is invalid because spec in IEEE Std +802.11™‐2020 as below. + +Table 9-273-Supported VHT-MCS and NSS Set subfields, it has subfield +VHT Extended NSS BW Capable, its definition is: +Indicates whether the STA is capable of interpreting the Extended NSS +BW Support subfield of the VHT Capabilities Information field. + +dmesg have a message without this patch: + +ieee80211 phy0: copying sband (band 1) due to VHT EXT NSS BW flag + +It means mac80211 will set IEEE80211_VHT_EXT_NSS_BW_CAPABLE if ath11k not +set it in ieee80211_register_hw(). So it is better to set it in ath11k. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211013073704.15888-1-wgong@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4712,6 +4712,10 @@ ath11k_create_vht_cap(struct ath11k *ar, + vht_cap.vht_supported = 1; + vht_cap.cap = ar->pdev->cap.vht_cap; + ++ if (ar->pdev->cap.nss_ratio_enabled) ++ vht_cap.vht_mcs.tx_highest |= ++ cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); ++ + ath11k_set_vht_txbf_cap(ar, &vht_cap.cap); + + rxmcs_map = 0; diff --git a/package/kernel/mac80211/patches/ath11k/0086-ath11k-remove-return-for-empty-tx-bitrate-in-mac_op_.patch b/package/kernel/mac80211/patches/ath11k/0086-ath11k-remove-return-for-empty-tx-bitrate-in-mac_op_.patch new file mode 100644 index 00000000000000..69991617f0062d --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0086-ath11k-remove-return-for-empty-tx-bitrate-in-mac_op_.patch @@ -0,0 +1,66 @@ +From 1d795645e1eef97fe5d409e3dd5747a942f00e08 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 11 Oct 2021 04:49:57 -0400 +Subject: [PATCH] ath11k: remove return for empty tx bitrate in + mac_op_sta_statistics + +Currently in ath11k_mac_op_sta_statistics() there is the following +logic: + + if (!arsta->txrate.legacy && !arsta->txrate.nss) + return; + +Unfortunately if this condition is true then the function returns without +setting parameters that follow the txrate. To address this issue remove the +return and instead invert the logic to set the txrate logic if +(arsta->txrate.legacy || arsta->txrate.nss). + +The same was done also in ath10k in commit 1cd6ba8ae33e ("ath10k: remove return +for NL80211_STA_INFO_TX_BITRATE"). + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211011084957.31024-1-wgong@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mac.c | 29 +++++++++++++-------------- + 1 file changed, 14 insertions(+), 15 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7436,21 +7436,20 @@ static void ath11k_mac_op_sta_statistics + sinfo->tx_duration = arsta->tx_duration; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION); + +- if (!arsta->txrate.legacy && !arsta->txrate.nss) +- return; +- +- if (arsta->txrate.legacy) { +- sinfo->txrate.legacy = arsta->txrate.legacy; +- } else { +- sinfo->txrate.mcs = arsta->txrate.mcs; +- sinfo->txrate.nss = arsta->txrate.nss; +- sinfo->txrate.bw = arsta->txrate.bw; +- sinfo->txrate.he_gi = arsta->txrate.he_gi; +- sinfo->txrate.he_dcm = arsta->txrate.he_dcm; +- sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; ++ if (arsta->txrate.legacy || arsta->txrate.nss) { ++ if (arsta->txrate.legacy) { ++ sinfo->txrate.legacy = arsta->txrate.legacy; ++ } else { ++ sinfo->txrate.mcs = arsta->txrate.mcs; ++ sinfo->txrate.nss = arsta->txrate.nss; ++ sinfo->txrate.bw = arsta->txrate.bw; ++ sinfo->txrate.he_gi = arsta->txrate.he_gi; ++ sinfo->txrate.he_dcm = arsta->txrate.he_dcm; ++ sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; ++ } ++ sinfo->txrate.flags = arsta->txrate.flags; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + } +- sinfo->txrate.flags = arsta->txrate.flags; +- sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + /* TODO: Use real NF instead of default one. */ + sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; diff --git a/package/kernel/mac80211/patches/ath11k/0087-ath11k-fix-the-value-of-msecs_to_jiffies-in-ath11k_d.patch b/package/kernel/mac80211/patches/ath11k/0087-ath11k-fix-the-value-of-msecs_to_jiffies-in-ath11k_d.patch new file mode 100644 index 00000000000000..de1e350a2915ac --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0087-ath11k-fix-the-value-of-msecs_to_jiffies-in-ath11k_d.patch @@ -0,0 +1,28 @@ +From c8f2d41bbff6794329d681d108a817366aed0ba7 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 25 Oct 2021 23:20:14 -0400 +Subject: [PATCH] ath11k: fix the value of msecs_to_jiffies in + ath11k_debugfs_fw_stats_request + +parameter of msecs_to_jiffies should be (3 * 1000) instead of (3 * HZ) + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211026032014.27010-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/debugfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -195,7 +195,7 @@ static int ath11k_debugfs_fw_stats_reque + * received 'update stats' event, we keep a 3 seconds timeout in case, + * fw_stats_done is not marked yet + */ +- timeout = jiffies + msecs_to_jiffies(3 * HZ); ++ timeout = jiffies + msecs_to_jiffies(3 * 1000); + + ath11k_debugfs_fw_stats_reset(ar); + diff --git a/package/kernel/mac80211/patches/ath11k/0088-ath11k-move-peer-delete-after-vdev-stop-of-station-f.patch b/package/kernel/mac80211/patches/ath11k/0088-ath11k-move-peer-delete-after-vdev-stop-of-station-f.patch new file mode 100644 index 00000000000000..2cbc69a4a401ad --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0088-ath11k-move-peer-delete-after-vdev-stop-of-station-f.patch @@ -0,0 +1,89 @@ +From b4a0f54156ac7720de1750b6ea06657c91c52163 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 27 Oct 2021 05:38:25 -0400 +Subject: [PATCH] ath11k: move peer delete after vdev stop of station for + QCA6390 and WCN6855 + +When station connect to AP, the wmi command sequence is: + +peer_create->vdev_start->vdev_up + +and sequence of station disconnect fo AP is: + +peer_delete->vdev_down->vdev_stop + +The sequence of disconnect is not opposite of connect, it caused firmware +crash when it handle wmi vdev stop cmd when the AP is support TWT of +802.11 ax, because firmware need access the bss peer for vdev stop cmd. + +[ 390.438564] ath11k_pci 0000:05:00.0: wmi cmd send 0x6001 ret 0 +[ 390.438567] ath11k_pci 0000:05:00.0: WMI peer create vdev_id 0 peer_addr c4:04:15:3b:e0:39 +[ 390.472724] ath11k_pci 0000:05:00.0: mac vdev 0 start center_freq 2437 phymode 11ax-he20-2g +[ 390.472731] ath11k_pci 0000:05:00.0: wmi cmd send 0x5003 ret 0 +[ 390.560849] ath11k_pci 0000:05:00.0: wmi cmd send 0x5005 ret 0 +[ 390.560850] ath11k_pci 0000:05:00.0: WMI mgmt vdev up id 0x0 assoc id 1 bssid c4:04:15:3b:e0:39 + +[ 399.432896] ath11k_pci 0000:05:00.0: WMI peer delete vdev_id 0 peer_addr c4:04:15:3b:e0:39 +[ 399.432902] ath11k_pci 0000:05:00.0: wmi cmd send 0x6002 ret 0 +[ 399.441380] ath11k_pci 0000:05:00.0: wmi cmd send 0x5007 ret 0 +[ 399.441381] ath11k_pci 0000:05:00.0: WMI vdev down id 0x0 +[ 399.454681] ath11k_pci 0000:05:00.0: wmi cmd send 0x5006 ret 0 +[ 399.454682] ath11k_pci 0000:05:00.0: WMI vdev stop id 0x0 + +The opposite sequence of disconnect should be: + +vdev_down->vdev_stop->peer_delete + +This patch change the sequence of disconnect for station as above +opposite sequence for QCA6390, firmware not crash again with this patch. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211027093825.12167-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4218,6 +4218,10 @@ static int ath11k_mac_op_sta_state(struc + new_state == IEEE80211_STA_NOTEXIST)) { + ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + ++ if (ar->ab->hw_params.vdev_start_delay && ++ vif->type == NL80211_IFTYPE_STATION) ++ goto free; ++ + ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", +@@ -4239,6 +4243,7 @@ static int ath11k_mac_op_sta_state(struc + } + spin_unlock_bh(&ar->ab->base_lock); + ++free: + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; + +@@ -6627,6 +6632,19 @@ ath11k_mac_op_unassign_vif_chanctx(struc + arvif->is_started = false; + + if (ab->hw_params.vdev_start_delay && ++ arvif->vdev_type == WMI_VDEV_TYPE_STA) { ++ ret = ath11k_peer_delete(ar, arvif->vdev_id, arvif->bssid); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "failed to delete peer %pM for vdev %d: %d\n", ++ arvif->bssid, arvif->vdev_id, ret); ++ else ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac removed peer %pM vdev %d after vdev stop\n", ++ arvif->bssid, arvif->vdev_id); ++ } ++ ++ if (ab->hw_params.vdev_start_delay && + arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + ath11k_wmi_vdev_down(ar, arvif->vdev_id); + diff --git a/package/kernel/mac80211/patches/ath11k/0089-ath11k-fix-FCS_ERR-flag-in-radio-tap-header.patch b/package/kernel/mac80211/patches/ath11k/0089-ath11k-fix-FCS_ERR-flag-in-radio-tap-header.patch new file mode 100644 index 00000000000000..6fe0ffdc908239 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0089-ath11k-fix-FCS_ERR-flag-in-radio-tap-header.patch @@ -0,0 +1,73 @@ +From 787264893c69ed091a46335dfd0f50dabb457718 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Mon, 25 Oct 2021 17:44:20 +0530 +Subject: [PATCH] ath11k: fix FCS_ERR flag in radio tap header + +In radio tap header, BAD FCS flag is not updated properly because +driver failed to update FCS_ERR flag in monitor mode. + +In rx_desc, FCS_ERR information is available in rx_attention +structure and presence of this field indicates corresponding frame +failed FCS check. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635164060-18423-1-git-send-email-quic_ppranees@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -4826,7 +4826,7 @@ static struct sk_buff * + ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, + u32 mac_id, struct sk_buff *head_msdu, + struct sk_buff *last_msdu, +- struct ieee80211_rx_status *rxs) ++ struct ieee80211_rx_status *rxs, bool *fcs_err) + { + struct ath11k_base *ab = ar->ab; + struct sk_buff *msdu, *prev_buf; +@@ -4836,12 +4836,17 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11 + u8 *dest, decap_format; + struct ieee80211_hdr_3addr *wh; + struct rx_attention *rx_attention; ++ u32 err_bitmap; + + if (!head_msdu) + goto err_merge_fail; + + rx_desc = (struct hal_rx_desc *)head_msdu->data; + rx_attention = ath11k_dp_rx_get_attention(ab, rx_desc); ++ err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_attention); ++ ++ if (err_bitmap & DP_RX_MPDU_ERR_FCS) ++ *fcs_err = true; + + if (ath11k_dp_rxdesc_get_mpdulen_err(rx_attention)) + return NULL; +@@ -4930,9 +4935,10 @@ static int ath11k_dp_rx_mon_deliver(stru + struct ath11k_pdev_dp *dp = &ar->dp; + struct sk_buff *mon_skb, *skb_next, *header; + struct ieee80211_rx_status *rxs = &dp->rx_status; ++ bool fcs_err = false; + + mon_skb = ath11k_dp_rx_mon_merg_msdus(ar, mac_id, head_msdu, +- tail_msdu, rxs); ++ tail_msdu, rxs, &fcs_err); + + if (!mon_skb) + goto mon_deliver_fail; +@@ -4940,6 +4946,10 @@ static int ath11k_dp_rx_mon_deliver(stru + header = mon_skb; + + rxs->flag = 0; ++ ++ if (fcs_err) ++ rxs->flag = RX_FLAG_FAILED_FCS_CRC; ++ + do { + skb_next = mon_skb->next; + if (!skb_next) diff --git a/package/kernel/mac80211/patches/ath11k/0090-ath11k-send-proper-txpower-and-maxregpower-values-to.patch b/package/kernel/mac80211/patches/ath11k/0090-ath11k-send-proper-txpower-and-maxregpower-values-to.patch new file mode 100644 index 00000000000000..ef39902d2000fe --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0090-ath11k-send-proper-txpower-and-maxregpower-values-to.patch @@ -0,0 +1,64 @@ +From 9212c1b9e80a869e732769a4fe7f82d392b219be Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Mon, 25 Oct 2021 17:47:09 +0530 +Subject: [PATCH] ath11k: send proper txpower and maxregpower values to + firmware + +Set proper values for max_regpower, max_power, max_antenna_gain as it +is because firmware will convert power values to 0.5dbm steps by +multiplying it with 2. + +If txpower is not set, it will lead to cca stuck resulting in latency +issues for QCN9074. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01386-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Lavanya Suresh +Signed-off-by: Lavanya Suresh +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635164229-22880-1-git-send-email-quic_ppranees@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 12 ++++++------ + drivers/net/wireless/ath/ath11k/wmi.c | 2 ++ + 2 files changed, 8 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -776,9 +776,9 @@ static int ath11k_mac_monitor_vdev_start + arg.channel.chan_radar = !!(channel->flags & IEEE80211_CHAN_RADAR); + + arg.channel.min_power = 0; +- arg.channel.max_power = channel->max_power * 2; +- arg.channel.max_reg_power = channel->max_reg_power * 2; +- arg.channel.max_antenna_gain = channel->max_antenna_gain * 2; ++ arg.channel.max_power = channel->max_power; ++ arg.channel.max_reg_power = channel->max_reg_power; ++ arg.channel.max_antenna_gain = channel->max_antenna_gain; + + arg.pref_tx_streams = ar->num_tx_chains; + arg.pref_rx_streams = ar->num_rx_chains; +@@ -6133,9 +6133,9 @@ ath11k_mac_vdev_start_restart(struct ath + ath11k_phymodes[chandef->chan->band][chandef->width]; + + arg.channel.min_power = 0; +- arg.channel.max_power = chandef->chan->max_power * 2; +- arg.channel.max_reg_power = chandef->chan->max_reg_power * 2; +- arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2; ++ arg.channel.max_power = chandef->chan->max_power; ++ arg.channel.max_reg_power = chandef->chan->max_reg_power; ++ arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain; + + arg.pref_tx_streams = ar->num_tx_chains; + arg.pref_rx_streams = ar->num_rx_chains; +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -2388,6 +2388,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(s + tchan_info->reg_class_id); + *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX, + tchan_info->antennamax); ++ *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_MAX_TX_PWR, ++ tchan_info->maxregpower); + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI chan scan list chan[%d] = %u, chan_info->info %8x\n", diff --git a/package/kernel/mac80211/patches/ath11k/0091-ath11k-Increment-pending_mgmt_tx-count-before-tx-sen.patch b/package/kernel/mac80211/patches/ath11k/0091-ath11k-Increment-pending_mgmt_tx-count-before-tx-sen.patch new file mode 100644 index 00000000000000..0788b5d46e98bc --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0091-ath11k-Increment-pending_mgmt_tx-count-before-tx-sen.patch @@ -0,0 +1,43 @@ +From c0b0d2e87d91ce283c8766b4b3c2ec9ac90ebf96 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Mon, 25 Oct 2021 18:54:42 +0530 +Subject: [PATCH] ath11k: Increment pending_mgmt_tx count before tx send invoke + +There is a race condition whereby the tx completion handler can be invoked +before the 'num_pending_mgmt_tx" count is incremented. If that occurs, we +could get warning trace indicating that 'num_pending_mgmt_tx' is 0 (because +it was not yet incremented). Ideally, this trace should be seen only if +mgmt tx has not happened but tx completion is received, and it is not +expected in this race condition. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01386-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Lavanya Suresh +Signed-off-by: Lavanya Suresh +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635168282-8845-1-git-send-email-quic_ppranees@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5212,13 +5212,15 @@ static void ath11k_mgmt_over_wmi_tx_work + arvif = ath11k_vif_to_arvif(skb_cb->vif); + if (ar->allocated_vdev_map & (1LL << arvif->vdev_id) && + arvif->is_started) { ++ atomic_inc(&ar->num_pending_mgmt_tx); + ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb); + if (ret) { ++ if (atomic_dec_if_positive(&ar->num_pending_mgmt_tx) < 0) ++ WARN_ON_ONCE(1); ++ + ath11k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n", + arvif->vdev_id, ret); + ieee80211_free_txskb(ar->hw, skb); +- } else { +- atomic_inc(&ar->num_pending_mgmt_tx); + } + } else { + ath11k_warn(ar->ab, diff --git a/package/kernel/mac80211/patches/ath11k/0093-ath11k-Disabling-credit-flow-for-WMI-path.patch b/package/kernel/mac80211/patches/ath11k/0093-ath11k-Disabling-credit-flow-for-WMI-path.patch new file mode 100644 index 00000000000000..cd3dce096c267a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0093-ath11k-Disabling-credit-flow-for-WMI-path.patch @@ -0,0 +1,499 @@ +From f951380a6022440335f668f85296096ba13071ba Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Mon, 15 Nov 2021 11:50:52 +0200 +Subject: [PATCH] ath11k: Disabling credit flow for WMI path + +Firmware credit flow control is enabled for WMI control services, +which expects available tokens should be acquired before sending a +command to the target. Also the token gets released when firmware +receives the command. + +This credit-based flow limits driver to send WMI command only +when the token available which is causing WMI commands to timeout and +return -EAGAIN, whereas firmware has enough capability to process the +WMI command. To fix this Tx starvation issue, introduce the ability to +disable the credit flow for the WMI path. + +The driver sends WMI configuration for disabling credit flow to firmware +by two ways. + 1. By using a global flag + (HTC_MSG_SETUP_COMPLETE_EX_ID msg type flags) + 2. By using a local flag + (ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3) + +Ath11k uses both these configurations to disable credit flow for the +WMI path completely. + +Also added a hw_param member for credit flow control by which we can +enable or disable it based on per-target basis. Currently we are disabling +credit flow for IPQ8074, IPQ6018, and QCN9074 as recommended by firmware. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01492-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ6018 hw1.0 AHB WLAN.HK.2.4.0.1-00330-QCAHKSWPL_SILICONZ-1 + +Co-developed-by: Pravas Kumar Panda +Signed-off-by: Pravas Kumar Panda +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635156494-20059-1-git-send-email-quic_ppranees@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ce.c | 37 +++++++++++--- + drivers/net/wireless/ath/ath11k/ce.h | 3 +- + drivers/net/wireless/ath/ath11k/core.c | 5 ++ + drivers/net/wireless/ath/ath11k/htc.c | 71 +++++++++++++++++++------- + drivers/net/wireless/ath/ath11k/htc.h | 9 ++-- + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/wmi.c | 54 +++++++++++++++++--- + drivers/net/wireless/ath/ath11k/wmi.h | 1 + + 8 files changed, 146 insertions(+), 35 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/ce.c ++++ b/drivers/net/wireless/ath/ath11k/ce.c +@@ -14,6 +14,7 @@ const struct ce_attr ath11k_host_ce_conf + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, ++ .send_cb = ath11k_htc_tx_completion_handler, + }, + + /* CE1: target->host HTT + HTC control */ +@@ -40,6 +41,7 @@ const struct ce_attr ath11k_host_ce_conf + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, ++ .send_cb = ath11k_htc_tx_completion_handler, + }, + + /* CE4: host->target HTT */ +@@ -73,6 +75,7 @@ const struct ce_attr ath11k_host_ce_conf + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, ++ .send_cb = ath11k_htc_tx_completion_handler, + }, + + /* CE8: target autonomous hif_memcpy */ +@@ -89,6 +92,7 @@ const struct ce_attr ath11k_host_ce_conf + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, ++ .send_cb = ath11k_htc_tx_completion_handler, + }, + + /* CE10: target->host HTT */ +@@ -142,6 +146,7 @@ const struct ce_attr ath11k_host_ce_conf + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, ++ .send_cb = ath11k_htc_tx_completion_handler, + }, + + /* CE4: host->target HTT */ +@@ -175,6 +180,7 @@ const struct ce_attr ath11k_host_ce_conf + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, ++ .send_cb = ath11k_htc_tx_completion_handler, + }, + + /* CE8: target autonomous hif_memcpy */ +@@ -220,6 +226,7 @@ const struct ce_attr ath11k_host_ce_conf + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, ++ .send_cb = ath11k_htc_tx_completion_handler, + }, + + /* CE4: host->target HTT */ +@@ -489,18 +496,32 @@ err_unlock: + return skb; + } + +-static void ath11k_ce_send_done_cb(struct ath11k_ce_pipe *pipe) ++static void ath11k_ce_tx_process_cb(struct ath11k_ce_pipe *pipe) + { + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; ++ struct sk_buff_head list; + ++ __skb_queue_head_init(&list); + while (!IS_ERR(skb = ath11k_ce_completed_send_next(pipe))) { + if (!skb) + continue; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(skb)->paddr, skb->len, + DMA_TO_DEVICE); +- dev_kfree_skb_any(skb); ++ ++ if ((!pipe->send_cb) || ab->hw_params.credit_flow) { ++ dev_kfree_skb_any(skb); ++ continue; ++ } ++ ++ __skb_queue_tail(&list, skb); ++ } ++ ++ while ((skb = __skb_dequeue(&list))) { ++ ath11k_dbg(ab, ATH11K_DBG_AHB, "tx ce pipe %d len %d\n", ++ pipe->pipe_num, skb->len); ++ pipe->send_cb(ab, skb); + } + } + +@@ -636,7 +657,7 @@ static int ath11k_ce_alloc_pipe(struct a + pipe->attr_flags = attr->flags; + + if (attr->src_nentries) { +- pipe->send_cb = ath11k_ce_send_done_cb; ++ pipe->send_cb = attr->send_cb; + nentries = roundup_pow_of_two(attr->src_nentries); + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); +@@ -667,9 +688,10 @@ static int ath11k_ce_alloc_pipe(struct a + void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id) + { + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; ++ const struct ce_attr *attr = &ab->hw_params.host_ce_config[ce_id]; + +- if (pipe->send_cb) +- pipe->send_cb(pipe); ++ if (attr->src_nentries) ++ ath11k_ce_tx_process_cb(pipe); + + if (pipe->recv_cb) + ath11k_ce_recv_process_cb(pipe); +@@ -678,9 +700,10 @@ void ath11k_ce_per_engine_service(struct + void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id) + { + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id]; ++ const struct ce_attr *attr = &ab->hw_params.host_ce_config[pipe_id]; + +- if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && pipe->send_cb) +- pipe->send_cb(pipe); ++ if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && attr->src_nentries) ++ ath11k_ce_tx_process_cb(pipe); + } + EXPORT_SYMBOL(ath11k_ce_per_engine_service); + +--- a/drivers/net/wireless/ath/ath11k/ce.h ++++ b/drivers/net/wireless/ath/ath11k/ce.h +@@ -101,6 +101,7 @@ struct ce_attr { + unsigned int dest_nentries; + + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); ++ void (*send_cb)(struct ath11k_base *, struct sk_buff *); + }; + + #define CE_DESC_RING_ALIGN 8 +@@ -154,7 +155,7 @@ struct ath11k_ce_pipe { + unsigned int buf_sz; + unsigned int rx_buf_needed; + +- void (*send_cb)(struct ath11k_ce_pipe *); ++ void (*send_cb)(struct ath11k_base *, struct sk_buff *); + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); + + struct tasklet_struct intr_tq; +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -81,6 +81,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, ++ .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, +@@ -133,6 +134,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, ++ .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, +@@ -184,6 +186,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, ++ .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, +@@ -235,6 +238,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .fix_l1ss = true, ++ .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = true, +@@ -286,6 +290,7 @@ static const struct ath11k_hw_params ath + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .fix_l1ss = false, ++ .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, +--- a/drivers/net/wireless/ath/ath11k/htc.c ++++ b/drivers/net/wireless/ath/ath11k/htc.c +@@ -81,6 +81,8 @@ int ath11k_htc_send(struct ath11k_htc *h + struct ath11k_base *ab = htc->ab; + int credits = 0; + int ret; ++ bool credit_flow_enabled = (ab->hw_params.credit_flow && ++ ep->tx_credit_flow_enabled); + + if (eid >= ATH11K_HTC_EP_COUNT) { + ath11k_warn(ab, "Invalid endpoint id: %d\n", eid); +@@ -89,7 +91,7 @@ int ath11k_htc_send(struct ath11k_htc *h + + skb_push(skb, sizeof(struct ath11k_htc_hdr)); + +- if (ep->tx_credit_flow_enabled) { ++ if (credit_flow_enabled) { + credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); + spin_lock_bh(&htc->tx_lock); + if (ep->tx_credits < credits) { +@@ -126,7 +128,7 @@ int ath11k_htc_send(struct ath11k_htc *h + err_unmap: + dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); + err_credits: +- if (ep->tx_credit_flow_enabled) { ++ if (credit_flow_enabled) { + spin_lock_bh(&htc->tx_lock); + ep->tx_credits += credits; + ath11k_dbg(ab, ATH11K_DBG_HTC, +@@ -203,23 +205,25 @@ static int ath11k_htc_process_trailer(st + break; + } + +- switch (record->hdr.id) { +- case ATH11K_HTC_RECORD_CREDITS: +- len = sizeof(struct ath11k_htc_credit_report); +- if (record->hdr.len < len) { +- ath11k_warn(ab, "Credit report too long\n"); +- status = -EINVAL; ++ if (ab->hw_params.credit_flow) { ++ switch (record->hdr.id) { ++ case ATH11K_HTC_RECORD_CREDITS: ++ len = sizeof(struct ath11k_htc_credit_report); ++ if (record->hdr.len < len) { ++ ath11k_warn(ab, "Credit report too long\n"); ++ status = -EINVAL; ++ break; ++ } ++ ath11k_htc_process_credit_report(htc, ++ record->credit_report, ++ record->hdr.len, ++ src_eid); ++ break; ++ default: ++ ath11k_warn(ab, "Unhandled record: id:%d length:%d\n", ++ record->hdr.id, record->hdr.len); + break; + } +- ath11k_htc_process_credit_report(htc, +- record->credit_report, +- record->hdr.len, +- src_eid); +- break; +- default: +- ath11k_warn(ab, "Unhandled record: id:%d length:%d\n", +- record->hdr.id, record->hdr.len); +- break; + } + + if (status) +@@ -245,6 +249,29 @@ static void ath11k_htc_suspend_complete( + complete(&ab->htc_suspend); + } + ++void ath11k_htc_tx_completion_handler(struct ath11k_base *ab, ++ struct sk_buff *skb) ++{ ++ struct ath11k_htc *htc = &ab->htc; ++ struct ath11k_htc_ep *ep; ++ void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *); ++ u8 eid; ++ ++ eid = ATH11K_SKB_CB(skb)->eid; ++ if (eid >= ATH11K_HTC_EP_COUNT) ++ return; ++ ++ ep = &htc->endpoint[eid]; ++ spin_lock_bh(&htc->tx_lock); ++ ep_tx_complete = ep->ep_ops.ep_tx_complete; ++ spin_unlock_bh(&htc->tx_lock); ++ if (!ep_tx_complete) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ep_tx_complete(htc->ab, skb); ++} ++ + void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, + struct sk_buff *skb) + { +@@ -607,6 +634,11 @@ int ath11k_htc_connect_service(struct at + disable_credit_flow_ctrl = true; + } + ++ if (!ab->hw_params.credit_flow) { ++ flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; ++ disable_credit_flow_ctrl = true; ++ } ++ + req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags); + req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID, + conn_req->service_id); +@@ -732,7 +764,10 @@ int ath11k_htc_start(struct ath11k_htc * + msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID, + ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID); + +- ath11k_dbg(ab, ATH11K_DBG_HTC, "HTC is using TX credit flow control\n"); ++ if (ab->hw_params.credit_flow) ++ ath11k_dbg(ab, ATH11K_DBG_HTC, "HTC is using TX credit flow control\n"); ++ else ++ msg->flags |= ATH11K_GLOBAL_DISABLE_CREDIT_FLOW; + + status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb); + if (status) { +--- a/drivers/net/wireless/ath/ath11k/htc.h ++++ b/drivers/net/wireless/ath/ath11k/htc.h +@@ -83,8 +83,8 @@ enum ath11k_htc_conn_flags { + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3, +- ATH11K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 1 << 2, +- ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3 ++ ATH11K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 0x4, ++ ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 0x8, + }; + + enum ath11k_htc_conn_svc_status { +@@ -116,6 +116,8 @@ struct ath11k_htc_conn_svc_resp { + u32 svc_meta_pad; + } __packed; + ++#define ATH11K_GLOBAL_DISABLE_CREDIT_FLOW BIT(1) ++ + struct ath11k_htc_setup_complete_extended { + u32 msg_id; + u32 flags; +@@ -305,5 +307,6 @@ int ath11k_htc_send(struct ath11k_htc *h + struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ar, int size); + void ath11k_htc_rx_completion_handler(struct ath11k_base *ar, + struct sk_buff *skb); +- ++void ath11k_htc_tx_completion_handler(struct ath11k_base *ab, ++ struct sk_buff *skb); + #endif +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -175,6 +175,7 @@ struct ath11k_hw_params { + bool supports_suspend; + u32 hal_desc_sz; + bool fix_l1ss; ++ bool credit_flow; + u8 max_tx_ring; + const struct ath11k_hw_hal_params *hal_params; + bool supports_dynamic_smps_6ghz; +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -267,21 +267,39 @@ int ath11k_wmi_cmd_send(struct ath11k_pd + { + struct ath11k_wmi_base *wmi_sc = wmi->wmi_ab; + int ret = -EOPNOTSUPP; ++ struct ath11k_base *ab = wmi_sc->ab; + + might_sleep(); + +- wait_event_timeout(wmi_sc->tx_credits_wq, ({ +- ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); ++ if (ab->hw_params.credit_flow) { ++ wait_event_timeout(wmi_sc->tx_credits_wq, ({ ++ ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); ++ ++ if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, ++ &wmi_sc->ab->dev_flags)) ++ ret = -ESHUTDOWN; ++ ++ (ret != -EAGAIN); ++ }), WMI_SEND_TIMEOUT_HZ); ++ } else { ++ wait_event_timeout(wmi->tx_ce_desc_wq, ({ ++ ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); + +- if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, &wmi_sc->ab->dev_flags)) +- ret = -ESHUTDOWN; ++ if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, ++ &wmi_sc->ab->dev_flags)) ++ ret = -ESHUTDOWN; + +- (ret != -EAGAIN); +- }), WMI_SEND_TIMEOUT_HZ); ++ (ret != -ENOBUFS); ++ }), WMI_SEND_TIMEOUT_HZ); ++ } + + if (ret == -EAGAIN) + ath11k_warn(wmi_sc->ab, "wmi command %d timeout\n", cmd_id); + ++ if (ret == -ENOBUFS) ++ ath11k_warn(wmi_sc->ab, "ce desc not available for wmi command %d\n", ++ cmd_id); ++ + return ret; + } + +@@ -5817,7 +5835,30 @@ static void ath11k_wmi_op_ep_tx_credits( + static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) + { ++ struct ath11k_pdev_wmi *wmi = NULL; ++ u32 i; ++ u8 wmi_ep_count; ++ u8 eid; ++ ++ eid = ATH11K_SKB_CB(skb)->eid; + dev_kfree_skb(skb); ++ ++ if (eid >= ATH11K_HTC_EP_COUNT) ++ return; ++ ++ wmi_ep_count = ab->htc.wmi_ep_count; ++ if (wmi_ep_count > ab->hw_params.max_radios) ++ return; ++ ++ for (i = 0; i < ab->htc.wmi_ep_count; i++) { ++ if (ab->wmi_ab.wmi[i].eid == eid) { ++ wmi = &ab->wmi_ab.wmi[i]; ++ break; ++ } ++ } ++ ++ if (wmi) ++ wake_up(&wmi->tx_ce_desc_wq); + } + + static bool ath11k_reg_is_world_alpha(char *alpha) +@@ -7208,6 +7249,7 @@ static int ath11k_connect_pdev_htc_servi + ab->wmi_ab.wmi_endpoint_id[pdev_idx] = conn_resp.eid; + ab->wmi_ab.wmi[pdev_idx].eid = conn_resp.eid; + ab->wmi_ab.max_msg_len[pdev_idx] = conn_resp.max_msg_len; ++ init_waitqueue_head(&ab->wmi_ab.wmi[pdev_idx].tx_ce_desc_wq); + + return 0; + } +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -2522,6 +2522,7 @@ struct ath11k_pdev_wmi { + enum ath11k_htc_ep_id eid; + const struct wmi_peer_flags_map *peer_flags; + u32 rx_decap_mode; ++ wait_queue_head_t tx_ce_desc_wq; + }; + + struct vdev_create_params { diff --git a/package/kernel/mac80211/patches/ath11k/0094-ath11k-use-cache-line-aligned-buffers-for-dbring.patch b/package/kernel/mac80211/patches/ath11k/0094-ath11k-use-cache-line-aligned-buffers-for-dbring.patch new file mode 100644 index 00000000000000..22a6c0bfe4db28 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0094-ath11k-use-cache-line-aligned-buffers-for-dbring.patch @@ -0,0 +1,91 @@ +From bd77f6b1d7104cf6451399a7c67d08afecb9a7c7 Mon Sep 17 00:00:00 2001 +From: Rameshkumar Sundaram +Date: Tue, 2 Nov 2021 11:11:33 +0530 +Subject: [PATCH] ath11k: use cache line aligned buffers for dbring + +The DMA buffers of dbring which is used for spectral/cfr +starts at certain offset from original kmalloc() returned buffer. +This is not cache line aligned. +And also driver tries to access the data that is immediately before +this offset address (i.e. buff->paddr) after doing dma map. +This will cause cache line sharing issues and data corruption, +if CPU happen to write back cache after HW has dma'ed the data. + +Fix this by mapping a cache line aligned buffer to dma. + +Tested on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Rameshkumar Sundaram +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635831693-15962-1-git-send-email-quic_ramess@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dbring.c | 16 ++++++++++++---- + drivers/net/wireless/ath/ath11k/dbring.h | 2 +- + 2 files changed, 13 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dbring.c ++++ b/drivers/net/wireless/ath/ath11k/dbring.c +@@ -87,17 +87,23 @@ static int ath11k_dbring_fill_bufs(struc + req_entries = min(num_free, ring->bufs_max); + num_remain = req_entries; + align = ring->buf_align; +- size = sizeof(*buff) + ring->buf_sz + align - 1; ++ size = ring->buf_sz + align - 1; + + while (num_remain > 0) { +- buff = kzalloc(size, GFP_ATOMIC); ++ buff = kzalloc(sizeof(*buff), GFP_ATOMIC); + if (!buff) + break; + ++ buff->payload = kzalloc(size, GFP_ATOMIC); ++ if (!buff->payload) { ++ kfree(buff); ++ break; ++ } + ret = ath11k_dbring_bufs_replenish(ar, ring, buff); + if (ret) { + ath11k_warn(ar->ab, "failed to replenish db ring num_remain %d req_ent %d\n", + num_remain, req_entries); ++ kfree(buff->payload); + kfree(buff); + break; + } +@@ -282,7 +288,7 @@ int ath11k_dbring_buffer_release_event(s + + srng = &ab->hal.srng_list[ring->refill_srng.ring_id]; + num_entry = ev->fixed.num_buf_release_entry; +- size = sizeof(*buff) + ring->buf_sz + ring->buf_align - 1; ++ size = ring->buf_sz + ring->buf_align - 1; + num_buff_reaped = 0; + + spin_lock_bh(&srng->lock); +@@ -319,7 +325,8 @@ int ath11k_dbring_buffer_release_event(s + ring->handler(ar, &handler_data); + } + +- memset(buff, 0, size); ++ buff->paddr = 0; ++ memset(buff->payload, 0, size); + ath11k_dbring_bufs_replenish(ar, ring, buff); + } + +@@ -346,6 +353,7 @@ void ath11k_dbring_buf_cleanup(struct at + idr_remove(&ring->bufs_idr, buf_id); + dma_unmap_single(ar->ab->dev, buff->paddr, + ring->buf_sz, DMA_FROM_DEVICE); ++ kfree(buff->payload); + kfree(buff); + } + +--- a/drivers/net/wireless/ath/ath11k/dbring.h ++++ b/drivers/net/wireless/ath/ath11k/dbring.h +@@ -13,7 +13,7 @@ + + struct ath11k_dbring_element { + dma_addr_t paddr; +- u8 payload[0]; ++ u8 *payload; + }; + + struct ath11k_dbring_data { diff --git a/package/kernel/mac80211/patches/ath11k/0095-ath11k-Add-missing-qmi_txn_cancel.patch b/package/kernel/mac80211/patches/ath11k/0095-ath11k-Add-missing-qmi_txn_cancel.patch new file mode 100644 index 00000000000000..486185d2ffea1a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0095-ath11k-Add-missing-qmi_txn_cancel.patch @@ -0,0 +1,142 @@ +From 1ad6e4b00f29d017b196dda7ab96d1cfcbabd7d2 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Tue, 2 Nov 2021 18:22:38 +0530 +Subject: [PATCH] ath11k: Add missing qmi_txn_cancel() + +Currently many functions do not follow this guidance when +qmi_send_request() fails, therefore add missing +qmi_txn_cancel() in the qmi_send_request() error path. + +Also remove initialization on 'struct qmi_txn' +since qmi_tx_init() performs all necessary initialization. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635857558-21733-1-git-send-email-akolli@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/qmi.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1586,7 +1586,7 @@ static int ath11k_qmi_host_cap_send(stru + { + struct qmi_wlanfw_host_cap_req_msg_v01 req; + struct qmi_wlanfw_host_cap_resp_msg_v01 resp; +- struct qmi_txn txn = {}; ++ struct qmi_txn txn; + int ret = 0; + + memset(&req, 0, sizeof(req)); +@@ -1640,6 +1640,7 @@ static int ath11k_qmi_host_cap_send(stru + QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_host_cap_req_msg_v01_ei, &req); + if (ret < 0) { ++ qmi_txn_cancel(&txn); + ath11k_warn(ab, "failed to send host capability request: %d\n", ret); + goto out; + } +@@ -1705,6 +1706,7 @@ static int ath11k_qmi_fw_ind_register_se + QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_ind_register_req_msg_v01_ei, req); + if (ret < 0) { ++ qmi_txn_cancel(&txn); + ath11k_warn(ab, "failed to send indication register request: %d\n", + ret); + goto out; +@@ -1734,7 +1736,7 @@ static int ath11k_qmi_respond_fw_mem_req + { + struct qmi_wlanfw_respond_mem_req_msg_v01 *req; + struct qmi_wlanfw_respond_mem_resp_msg_v01 resp; +- struct qmi_txn txn = {}; ++ struct qmi_txn txn; + int ret = 0, i; + bool delayed; + +@@ -1783,6 +1785,7 @@ static int ath11k_qmi_respond_fw_mem_req + QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_respond_mem_req_msg_v01_ei, req); + if (ret < 0) { ++ qmi_txn_cancel(&txn); + ath11k_warn(ab, "failed to respond qmi memory request: %d\n", + ret); + goto out; +@@ -1911,7 +1914,7 @@ static int ath11k_qmi_request_target_cap + { + struct qmi_wlanfw_cap_req_msg_v01 req; + struct qmi_wlanfw_cap_resp_msg_v01 resp; +- struct qmi_txn txn = {}; ++ struct qmi_txn txn; + int ret = 0; + int r; + +@@ -1930,6 +1933,7 @@ static int ath11k_qmi_request_target_cap + QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_cap_req_msg_v01_ei, &req); + if (ret < 0) { ++ qmi_txn_cancel(&txn); + ath11k_warn(ab, "failed to send qmi cap request: %d\n", + ret); + goto out; +@@ -2000,7 +2004,7 @@ static int ath11k_qmi_load_file_target_m + { + struct qmi_wlanfw_bdf_download_req_msg_v01 *req; + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; +- struct qmi_txn txn = {}; ++ struct qmi_txn txn; + const u8 *temp = data; + void __iomem *bdf_addr = NULL; + int ret; +@@ -2245,7 +2249,7 @@ static int ath11k_qmi_wlanfw_m3_info_sen + struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; + struct qmi_wlanfw_m3_info_req_msg_v01 req; + struct qmi_wlanfw_m3_info_resp_msg_v01 resp; +- struct qmi_txn txn = {}; ++ struct qmi_txn txn; + int ret = 0; + + memset(&req, 0, sizeof(req)); +@@ -2277,6 +2281,7 @@ static int ath11k_qmi_wlanfw_m3_info_sen + QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN, + qmi_wlanfw_m3_info_req_msg_v01_ei, &req); + if (ret < 0) { ++ qmi_txn_cancel(&txn); + ath11k_warn(ab, "failed to send m3 information request: %d\n", + ret); + goto out; +@@ -2303,7 +2308,7 @@ static int ath11k_qmi_wlanfw_mode_send(s + { + struct qmi_wlanfw_wlan_mode_req_msg_v01 req; + struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp; +- struct qmi_txn txn = {}; ++ struct qmi_txn txn; + int ret = 0; + + memset(&req, 0, sizeof(req)); +@@ -2325,6 +2330,7 @@ static int ath11k_qmi_wlanfw_mode_send(s + QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_mode_req_msg_v01_ei, &req); + if (ret < 0) { ++ qmi_txn_cancel(&txn); + ath11k_warn(ab, "failed to send wlan mode request (mode %d): %d\n", + mode, ret); + goto out; +@@ -2358,7 +2364,7 @@ static int ath11k_qmi_wlanfw_wlan_cfg_se + struct qmi_wlanfw_wlan_cfg_resp_msg_v01 resp; + struct ce_pipe_config *ce_cfg; + struct service_to_pipe *svc_cfg; +- struct qmi_txn txn = {}; ++ struct qmi_txn txn; + int ret = 0, pipe_num; + + ce_cfg = (struct ce_pipe_config *)ab->qmi.ce_cfg.tgt_ce; +@@ -2419,6 +2425,7 @@ static int ath11k_qmi_wlanfw_wlan_cfg_se + QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_cfg_req_msg_v01_ei, req); + if (ret < 0) { ++ qmi_txn_cancel(&txn); + ath11k_warn(ab, "failed to send wlan config request: %d\n", + ret); + goto out; diff --git a/package/kernel/mac80211/patches/ath11k/0096-ath11k-add-trace-log-support.patch b/package/kernel/mac80211/patches/ath11k/0096-ath11k-add-trace-log-support.patch new file mode 100644 index 00000000000000..49974b060790a7 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0096-ath11k-add-trace-log-support.patch @@ -0,0 +1,309 @@ +From fb12305aff12e735e599c79514dde5dac40f5a59 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Tue, 9 Nov 2021 12:05:55 +0530 +Subject: [PATCH] ath11k: add trace log support + +This change is to add trace log support for, + * WMI events + * WMI commands + * ath11k_dbg messages + * ath11k_dbg_dump messages + * ath11k_log_info messages + * ath11k_log_warn messages + * ath11k_log_err messages + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00652-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636439755-30419-1-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/debug.c | 12 +- + drivers/net/wireless/ath/ath11k/debug.h | 3 +- + drivers/net/wireless/ath/ath11k/trace.c | 1 + + drivers/net/wireless/ath/ath11k/trace.h | 172 ++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 4 + + 5 files changed, 187 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debug.c ++++ b/drivers/net/wireless/ath/ath11k/debug.c +@@ -17,7 +17,7 @@ void ath11k_info(struct ath11k_base *ab, + va_start(args, fmt); + vaf.va = &args; + dev_info(ab->dev, "%pV", &vaf); +- /* TODO: Trace the log */ ++ trace_ath11k_log_info(ab, &vaf); + va_end(args); + } + EXPORT_SYMBOL(ath11k_info); +@@ -32,7 +32,7 @@ void ath11k_err(struct ath11k_base *ab, + va_start(args, fmt); + vaf.va = &args; + dev_err(ab->dev, "%pV", &vaf); +- /* TODO: Trace the log */ ++ trace_ath11k_log_err(ab, &vaf); + va_end(args); + } + EXPORT_SYMBOL(ath11k_err); +@@ -47,7 +47,7 @@ void ath11k_warn(struct ath11k_base *ab, + va_start(args, fmt); + vaf.va = &args; + dev_warn_ratelimited(ab->dev, "%pV", &vaf); +- /* TODO: Trace the log */ ++ trace_ath11k_log_warn(ab, &vaf); + va_end(args); + } + EXPORT_SYMBOL(ath11k_warn); +@@ -68,7 +68,7 @@ void __ath11k_dbg(struct ath11k_base *ab + if (ath11k_debug_mask & mask) + dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + +- /* TODO: trace log */ ++ trace_ath11k_log_dbg(ab, mask, &vaf); + + va_end(args); + } +@@ -100,6 +100,10 @@ void ath11k_dbg_dump(struct ath11k_base + dev_printk(KERN_DEBUG, ab->dev, "%s\n", linebuf); + } + } ++ ++ /* tracing code doesn't like null strings */ ++ trace_ath11k_log_dbg_dump(ab, msg ? msg : "", prefix ? prefix : "", ++ buf, len); + } + EXPORT_SYMBOL(ath11k_dbg_dump); + +--- a/drivers/net/wireless/ath/ath11k/debug.h ++++ b/drivers/net/wireless/ath/ath11k/debug.h +@@ -60,7 +60,8 @@ static inline void ath11k_dbg_dump(struc + + #define ath11k_dbg(ar, dbg_mask, fmt, ...) \ + do { \ +- if (ath11k_debug_mask & dbg_mask) \ ++ if ((ath11k_debug_mask & dbg_mask) || \ ++ trace_ath11k_log_dbg_enabled()) \ + __ath11k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \ + } while (0) + +--- a/drivers/net/wireless/ath/ath11k/trace.c ++++ b/drivers/net/wireless/ath/ath11k/trace.c +@@ -7,3 +7,4 @@ + + #define CREATE_TRACE_POINTS + #include "trace.h" ++EXPORT_SYMBOL(__tracepoint_ath11k_log_dbg); +--- a/drivers/net/wireless/ath/ath11k/trace.h ++++ b/drivers/net/wireless/ath/ath11k/trace.h +@@ -14,12 +14,24 @@ + #if !defined(CPTCFG_ATH11K_TRACING) + #undef TRACE_EVENT + #define TRACE_EVENT(name, proto, ...) \ ++static inline void trace_ ## name(proto) {} \ ++static inline bool trace_##name##_enabled(void) \ ++{ \ ++ return false; \ ++} ++ ++#undef DECLARE_EVENT_CLASS ++#define DECLARE_EVENT_CLASS(...) ++#undef DEFINE_EVENT ++#define DEFINE_EVENT(evt_class, name, proto, ...) \ + static inline void trace_ ## name(proto) {} + #endif /* !CPTCFG_ATH11K_TRACING || __CHECKER__ */ + + #undef TRACE_SYSTEM + #define TRACE_SYSTEM ath11k + ++#define ATH11K_MSG_MAX 400 ++ + TRACE_EVENT(ath11k_htt_pktlog, + TP_PROTO(struct ath11k *ar, const void *buf, u16 buf_len, + u32 pktlog_checksum), +@@ -108,6 +120,166 @@ TRACE_EVENT(ath11k_htt_rxdesc, + ) + ); + ++DECLARE_EVENT_CLASS(ath11k_log_event, ++ TP_PROTO(struct ath11k_base *ab, struct va_format *vaf), ++ TP_ARGS(ab, vaf), ++ TP_STRUCT__entry( ++ __string(device, dev_name(ab->dev)) ++ __string(driver, dev_driver_string(ab->dev)) ++ __dynamic_array(char, msg, ATH11K_MSG_MAX) ++ ), ++ TP_fast_assign( ++ __assign_str(device, dev_name(ab->dev)); ++ __assign_str(driver, dev_driver_string(ab->dev)); ++ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), ++ ATH11K_MSG_MAX, ++ vaf->fmt, ++ *vaf->va) >= ATH11K_MSG_MAX); ++ ), ++ TP_printk( ++ "%s %s %s", ++ __get_str(driver), ++ __get_str(device), ++ __get_str(msg) ++ ) ++); ++ ++DEFINE_EVENT(ath11k_log_event, ath11k_log_err, ++ TP_PROTO(struct ath11k_base *ab, struct va_format *vaf), ++ TP_ARGS(ab, vaf) ++); ++ ++DEFINE_EVENT(ath11k_log_event, ath11k_log_warn, ++ TP_PROTO(struct ath11k_base *ab, struct va_format *vaf), ++ TP_ARGS(ab, vaf) ++); ++ ++DEFINE_EVENT(ath11k_log_event, ath11k_log_info, ++ TP_PROTO(struct ath11k_base *ab, struct va_format *vaf), ++ TP_ARGS(ab, vaf) ++); ++ ++TRACE_EVENT(ath11k_wmi_cmd, ++ TP_PROTO(struct ath11k_base *ab, int id, const void *buf, size_t buf_len), ++ ++ TP_ARGS(ab, id, buf, buf_len), ++ ++ TP_STRUCT__entry( ++ __string(device, dev_name(ab->dev)) ++ __string(driver, dev_driver_string(ab->dev)) ++ __field(unsigned int, id) ++ __field(size_t, buf_len) ++ __dynamic_array(u8, buf, buf_len) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(device, dev_name(ab->dev)); ++ __assign_str(driver, dev_driver_string(ab->dev)); ++ __entry->id = id; ++ __entry->buf_len = buf_len; ++ memcpy(__get_dynamic_array(buf), buf, buf_len); ++ ), ++ ++ TP_printk( ++ "%s %s id %d len %zu", ++ __get_str(driver), ++ __get_str(device), ++ __entry->id, ++ __entry->buf_len ++ ) ++); ++ ++TRACE_EVENT(ath11k_wmi_event, ++ TP_PROTO(struct ath11k_base *ab, int id, const void *buf, size_t buf_len), ++ ++ TP_ARGS(ab, id, buf, buf_len), ++ ++ TP_STRUCT__entry( ++ __string(device, dev_name(ab->dev)) ++ __string(driver, dev_driver_string(ab->dev)) ++ __field(unsigned int, id) ++ __field(size_t, buf_len) ++ __dynamic_array(u8, buf, buf_len) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(device, dev_name(ab->dev)); ++ __assign_str(driver, dev_driver_string(ab->dev)); ++ __entry->id = id; ++ __entry->buf_len = buf_len; ++ memcpy(__get_dynamic_array(buf), buf, buf_len); ++ ), ++ ++ TP_printk( ++ "%s %s id %d len %zu", ++ __get_str(driver), ++ __get_str(device), ++ __entry->id, ++ __entry->buf_len ++ ) ++); ++ ++TRACE_EVENT(ath11k_log_dbg, ++ TP_PROTO(struct ath11k_base *ab, unsigned int level, struct va_format *vaf), ++ ++ TP_ARGS(ab, level, vaf), ++ ++ TP_STRUCT__entry( ++ __string(device, dev_name(ab->dev)) ++ __string(driver, dev_driver_string(ab->dev)) ++ __field(unsigned int, level) ++ __dynamic_array(char, msg, ATH11K_MSG_MAX) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(device, dev_name(ab->dev)); ++ __assign_str(driver, dev_driver_string(ab->dev)); ++ __entry->level = level; ++ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), ++ ATH11K_MSG_MAX, vaf->fmt, ++ *vaf->va) >= ATH11K_MSG_MAX); ++ ), ++ ++ TP_printk( ++ "%s %s %s", ++ __get_str(driver), ++ __get_str(device), ++ __get_str(msg) ++ ) ++); ++ ++TRACE_EVENT(ath11k_log_dbg_dump, ++ TP_PROTO(struct ath11k_base *ab, const char *msg, const char *prefix, ++ const void *buf, size_t buf_len), ++ ++ TP_ARGS(ab, msg, prefix, buf, buf_len), ++ ++ TP_STRUCT__entry( ++ __string(device, dev_name(ab->dev)) ++ __string(driver, dev_driver_string(ab->dev)) ++ __string(msg, msg) ++ __string(prefix, prefix) ++ __field(size_t, buf_len) ++ __dynamic_array(u8, buf, buf_len) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(device, dev_name(ab->dev)); ++ __assign_str(driver, dev_driver_string(ab->dev)); ++ __assign_str(msg, msg); ++ __assign_str(prefix, prefix); ++ __entry->buf_len = buf_len; ++ memcpy(__get_dynamic_array(buf), buf, buf_len); ++ ), ++ ++ TP_printk( ++ "%s %s %s/%s\n", ++ __get_str(driver), ++ __get_str(device), ++ __get_str(prefix), ++ __get_str(msg) ++ ) ++); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ + + /* we don't want to use include/trace/events */ +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -249,6 +249,8 @@ static int ath11k_wmi_cmd_send_nowait(st + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + cmd_hdr->cmd_id = cmd; + ++ trace_ath11k_wmi_cmd(ab, cmd_id, skb->data, skb->len); ++ + memset(skb_cb, 0, sizeof(*skb_cb)); + ret = ath11k_htc_send(&ab->htc, wmi->eid, skb); + +@@ -7104,6 +7106,8 @@ static void ath11k_wmi_tlv_op_rx(struct + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = FIELD_GET(WMI_CMD_HDR_CMD_ID, (cmd_hdr->cmd_id)); + ++ trace_ath11k_wmi_event(ab, id, skb->data, skb->len); ++ + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + goto out; + diff --git a/package/kernel/mac80211/patches/ath11k/0099-ath11k-add-support-for-BSS-color-change.patch b/package/kernel/mac80211/patches/ath11k/0099-ath11k-add-support-for-BSS-color-change.patch new file mode 100644 index 00000000000000..7796b85adf3113 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0099-ath11k-add-support-for-BSS-color-change.patch @@ -0,0 +1,308 @@ +From 886433a984254c6d2c2074688dc8f48c40b1c070 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 25 Oct 2021 21:40:54 +0530 +Subject: [PATCH] ath11k: add support for BSS color change + +Whenever the MAC detects a color collision, or any of +its associated stations detects one, the firmware will +send out an event. Add the code to parse and handle +this event and pass the data up to mac80211. + +The firmware does not provide an offload feature such +as the one used for CSA. The color change process is +hence triggered via the beacon offload tx completion +events sent out by firmware. + +BSS color feature is enabled depending on service flag +advertised by firmware, based on which color change +functionality is invoked. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-00680-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: John Crispin +Co-developed-by: Lavanya Suresh +Signed-off-by: Lavanya Suresh +Signed-off-by: Rameshkumar Sundaram +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1635178254-17732-1-git-send-email-quic_ramess@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 2 + + drivers/net/wireless/ath/ath11k/mac.c | 57 +++++++++++++++++-- + drivers/net/wireless/ath/ath11k/mac.h | 1 + + drivers/net/wireless/ath/ath11k/wmi.c | 78 +++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/wmi.h | 15 +++++ + 5 files changed, 147 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -256,6 +256,8 @@ struct ath11k_vif { + int txpower; + bool rsnie_present; + bool wpaie_present; ++ bool bcca_zero_sent; ++ bool do_not_send_tmpl; + struct ieee80211_chanctx_conf chanctx; + }; + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1236,6 +1236,26 @@ static int ath11k_mac_setup_bcn_tmpl(str + return ret; + } + ++void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif) ++{ ++ struct ieee80211_vif *vif = arvif->vif; ++ ++ if (!vif->color_change_active && !arvif->bcca_zero_sent) ++ return; ++ ++ if (vif->color_change_active && ieee80211_beacon_cntdwn_is_complete(vif)) { ++ arvif->bcca_zero_sent = true; ++ ieee80211_color_change_finish(vif); ++ return; ++ } ++ ++ arvif->bcca_zero_sent = false; ++ ++ if (vif->color_change_active) ++ ieee80211_beacon_update_cntdwn(vif); ++ ath11k_mac_setup_bcn_tmpl(arvif); ++} ++ + static void ath11k_control_beaconing(struct ath11k_vif *arvif, + struct ieee80211_bss_conf *info) + { +@@ -2899,10 +2919,17 @@ static void ath11k_mac_op_bss_info_chang + "Set staggered beacon mode for VDEV: %d\n", + arvif->vdev_id); + +- ret = ath11k_mac_setup_bcn_tmpl(arvif); +- if (ret) +- ath11k_warn(ar->ab, "failed to update bcn template: %d\n", +- ret); ++ if (!arvif->do_not_send_tmpl || !arvif->bcca_zero_sent) { ++ ret = ath11k_mac_setup_bcn_tmpl(arvif); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to update bcn template: %d\n", ++ ret); ++ } ++ ++ if (arvif->bcca_zero_sent) ++ arvif->do_not_send_tmpl = true; ++ else ++ arvif->do_not_send_tmpl = false; + } + + if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) { +@@ -3113,6 +3140,25 @@ static void ath11k_mac_op_bss_info_chang + if (ret) + ath11k_warn(ar->ab, "failed to set bss color collision on vdev %i: %d\n", + arvif->vdev_id, ret); ++ ++ param_id = WMI_VDEV_PARAM_BSS_COLOR; ++ if (info->he_bss_color.enabled) ++ param_value = info->he_bss_color.color << ++ IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET; ++ else ++ param_value = IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED; ++ ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ param_id, ++ param_value); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "failed to set bss color param on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "bss color param 0x%x set on vdev %i\n", ++ param_value, arvif->vdev_id); + } else if (vif->type == NL80211_IFTYPE_STATION) { + ret = ath11k_wmi_send_bss_color_change_enable_cmd(ar, + arvif->vdev_id, +@@ -7882,6 +7928,9 @@ static int __ath11k_mac_register(struct + + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); ++ if (test_bit(WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD, ar->ab->wmi_ab.svc_map)) ++ wiphy_ext_feature_set(ar->hw->wiphy, ++ NL80211_EXT_FEATURE_BSS_COLOR); + + ar->hw->wiphy->cipher_suites = cipher_suites; + ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -155,4 +155,5 @@ enum ath11k_supported_bw ath11k_mac_mac8 + enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher); + void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb); + void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id); ++void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif); + #endif +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -128,6 +128,8 @@ static const struct wmi_tlv_policy wmi_t + .min_len = sizeof(struct wmi_probe_resp_tx_status_event) }, + [WMI_TAG_VDEV_DELETE_RESP_EVENT] = { + .min_len = sizeof(struct wmi_vdev_delete_resp_event) }, ++ [WMI_TAG_OBSS_COLOR_COLLISION_EVT] = { ++ .min_len = sizeof(struct wmi_obss_color_collision_event) }, + }; + + #define PRIMAP(_hw_mode_) \ +@@ -1633,6 +1635,15 @@ int ath11k_wmi_bcn_tmpl(struct ath11k *a + void *ptr; + int ret, len; + size_t aligned_len = roundup(bcn->len, 4); ++ struct ieee80211_vif *vif; ++ struct ath11k_vif *arvif = ath11k_mac_get_arvif(ar, vdev_id); ++ ++ if (!arvif) { ++ ath11k_warn(ar->ab, "failed to find arvif with vdev id %d\n", vdev_id); ++ return -EINVAL; ++ } ++ ++ vif = arvif->vif; + + len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; + +@@ -1645,8 +1656,12 @@ int ath11k_wmi_bcn_tmpl(struct ath11k *a + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->tim_ie_offset = offs->tim_offset; +- cmd->csa_switch_count_offset = offs->cntdwn_counter_offs[0]; +- cmd->ext_csa_switch_count_offset = offs->cntdwn_counter_offs[1]; ++ ++ if (vif->csa_active) { ++ cmd->csa_switch_count_offset = offs->cntdwn_counter_offs[0]; ++ cmd->ext_csa_switch_count_offset = offs->cntdwn_counter_offs[1]; ++ } ++ + cmd->buf_len = bcn->len; + + ptr = skb->data + sizeof(*cmd); +@@ -3452,6 +3467,53 @@ int ath11k_wmi_fils_discovery(struct ath + } + + static void ++ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *skb) ++{ ++ const void **tb; ++ const struct wmi_obss_color_collision_event *ev; ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); ++ if (IS_ERR(tb)) { ++ ret = PTR_ERR(tb); ++ ath11k_warn(ab, "failed to parse tlv: %d\n", ret); ++ return; ++ } ++ ++ ev = tb[WMI_TAG_OBSS_COLOR_COLLISION_EVT]; ++ if (!ev) { ++ ath11k_warn(ab, "failed to fetch obss color collision ev"); ++ goto exit; ++ } ++ ++ arvif = ath11k_mac_get_arvif_by_vdev_id(ab, ev->vdev_id); ++ if (!arvif) { ++ ath11k_warn(ab, "failed to find arvif with vedv id %d in obss_color_collision_event\n", ++ ev->vdev_id); ++ goto exit; ++ } ++ ++ switch (ev->evt_type) { ++ case WMI_BSS_COLOR_COLLISION_DETECTION: ++ ieeee80211_obss_color_collision_notify(arvif->vif, ev->obss_color_bitmap); ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "OBSS color collision detected vdev:%d, event:%d, bitmap:%08llx\n", ++ ev->vdev_id, ev->evt_type, ev->obss_color_bitmap); ++ break; ++ case WMI_BSS_COLOR_COLLISION_DISABLE: ++ case WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY: ++ case WMI_BSS_COLOR_FREE_SLOT_AVAILABLE: ++ break; ++ default: ++ ath11k_warn(ab, "received unknown obss color collision detetction event\n"); ++ } ++ ++exit: ++ kfree(tb); ++} ++ ++static void + ath11k_fill_band_to_mac_param(struct ath11k_base *soc, + struct wmi_host_pdev_band_to_mac *band_to_mac) + { +@@ -6158,6 +6220,7 @@ static void ath11k_vdev_start_resp_event + + static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *skb) + { ++ struct ath11k_vif *arvif; + u32 vdev_id, tx_status; + + if (ath11k_pull_bcn_tx_status_ev(ab, skb->data, skb->len, +@@ -6165,6 +6228,14 @@ static void ath11k_bcn_tx_status_event(s + ath11k_warn(ab, "failed to extract bcn tx status"); + return; + } ++ ++ arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_id); ++ if (!arvif) { ++ ath11k_warn(ab, "invalid vdev id %d in bcn_tx_status", ++ vdev_id); ++ return; ++ } ++ ath11k_mac_bcn_tx_event(arvif); + } + + static void ath11k_vdev_stopped_event(struct ath11k_base *ab, struct sk_buff *skb) +@@ -7192,6 +7263,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID: + ath11k_probe_resp_tx_status_event(ab, skb); + break; ++ case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: ++ ath11k_wmi_obss_color_collision_event(ab, skb); ++ break; + /* add Unsupported events here */ + case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: + case WMI_PEER_OPER_MODE_CHANGE_EVENTID: +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -774,6 +774,8 @@ enum wmi_tlv_event_id { + WMI_MDNS_STATS_EVENTID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), + WMI_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), + WMI_SAP_OFL_DEL_STA_EVENTID, ++ WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID = ++ WMI_EVT_GRP_START_ID(WMI_GRP_OBSS_OFL), + WMI_OCB_SET_CONFIG_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_OCB), + WMI_OCB_GET_TSF_TIMER_RESP_EVENTID, + WMI_DCC_GET_STATS_RESP_EVENTID, +@@ -4916,6 +4918,13 @@ struct wmi_pdev_obss_pd_bitmap_cmd { + #define ATH11K_BSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS 10000 + #define ATH11K_BSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS 5000 + ++enum wmi_bss_color_collision { ++ WMI_BSS_COLOR_COLLISION_DISABLE = 0, ++ WMI_BSS_COLOR_COLLISION_DETECTION, ++ WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY, ++ WMI_BSS_COLOR_FREE_SLOT_AVAILABLE, ++}; ++ + struct wmi_obss_color_collision_cfg_params_cmd { + u32 tlv_header; + u32 vdev_id; +@@ -4933,6 +4942,12 @@ struct wmi_bss_color_change_enable_param + u32 enable; + } __packed; + ++struct wmi_obss_color_collision_event { ++ u32 vdev_id; ++ u32 evt_type; ++ u64 obss_color_bitmap; ++} __packed; ++ + #define ATH11K_IPV4_TH_SEED_SIZE 5 + #define ATH11K_IPV6_TH_SEED_SIZE 11 + diff --git a/package/kernel/mac80211/patches/ath11k/0102-ath11k-add-hw_param-for-wakeup_mhi.patch b/package/kernel/mac80211/patches/ath11k/0102-ath11k-add-hw_param-for-wakeup_mhi.patch new file mode 100644 index 00000000000000..fe000c3abc1df2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0102-ath11k-add-hw_param-for-wakeup_mhi.patch @@ -0,0 +1,137 @@ +From 081e2d6476e30399433b509684d5da4d1844e430 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Wed, 17 Nov 2021 09:39:41 +0200 +Subject: [PATCH] ath11k: add hw_param for wakeup_mhi + +Wakeup mhi is needed before pci_read/write only for QCA6390 and WCN6855. Since +wakeup & release mhi is enabled for all hardwares, below mhi assert is seen in +QCN9074 when doing 'rmmod ath11k_pci': + + Kernel panic - not syncing: dev_wake != 0 + CPU: 2 PID: 13535 Comm: procd Not tainted 4.4.60 #1 + Hardware name: Generic DT based system + [<80316dac>] (unwind_backtrace) from [<80313700>] (show_stack+0x10/0x14) + [<80313700>] (show_stack) from [<805135dc>] (dump_stack+0x7c/0x9c) + [<805135dc>] (dump_stack) from [<8032136c>] (panic+0x84/0x1f8) + [<8032136c>] (panic) from [<80549b24>] (mhi_pm_disable_transition+0x3b8/0x5b8) + [<80549b24>] (mhi_pm_disable_transition) from [<80549ddc>] (mhi_power_down+0xb8/0x100) + [<80549ddc>] (mhi_power_down) from [<7f5242b0>] (ath11k_mhi_op_status_cb+0x284/0x3ac [ath11k_pci]) + [E][__mhi_device_get_sync] Did not enter M0 state, cur_state:RESET pm_state:SHUTDOWN Process + [E][__mhi_device_get_sync] Did not enter M0 state, cur_state:RESET pm_state:SHUTDOWN Process + [E][__mhi_device_get_sync] Did not enter M0 state, cur_state:RESET pm_state:SHUTDOWN Process + [<7f5242b0>] (ath11k_mhi_op_status_cb [ath11k_pci]) from [<7f524878>] (ath11k_mhi_stop+0x10/0x20 [ath11k_pci]) + [<7f524878>] (ath11k_mhi_stop [ath11k_pci]) from [<7f525b94>] (ath11k_pci_power_down+0x54/0x90 [ath11k_pci]) + [<7f525b94>] (ath11k_pci_power_down [ath11k_pci]) from [<8056b2a8>] (pci_device_shutdown+0x30/0x44) + [<8056b2a8>] (pci_device_shutdown) from [<805cfa0c>] (device_shutdown+0x124/0x174) + [<805cfa0c>] (device_shutdown) from [<8033aaa4>] (kernel_restart+0xc/0x50) + [<8033aaa4>] (kernel_restart) from [<8033ada8>] (SyS_reboot+0x178/0x1ec) + [<8033ada8>] (SyS_reboot) from [<80301b80>] (ret_fast_syscall+0x0/0x34) + +Hence, disable wakeup/release mhi using hw_param for other hardwares. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01060-QCAHKSWPL_SILICONZ-1 + +Fixes: a05bd8513335 ("ath11k: read and write registers below unwindowed address") +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1636702019-26142-1-git-send-email-quic_seevalam@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 5 +++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/pci.c | 12 ++++++++---- + 3 files changed, 14 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -86,6 +86,7 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, ++ .wakeup_mhi = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -139,6 +140,7 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, ++ .wakeup_mhi = false, + }, + { + .name = "qca6390 hw2.0", +@@ -191,6 +193,7 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, ++ .wakeup_mhi = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -243,6 +246,7 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = true, + .alloc_cacheable_memory = true, ++ .wakeup_mhi = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -295,6 +299,7 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, ++ .wakeup_mhi = true, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -180,6 +180,7 @@ struct ath11k_hw_params { + const struct ath11k_hw_hal_params *hal_params; + bool supports_dynamic_smps_6ghz; + bool alloc_cacheable_memory; ++ bool wakeup_mhi; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -182,7 +182,8 @@ void ath11k_pci_write32(struct ath11k_ba + /* for offset beyond BAR + 4K - 32, may + * need to wakeup MHI to access. + */ +- if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && + offset >= ACCESS_ALWAYS_OFF) + mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + +@@ -206,7 +207,8 @@ void ath11k_pci_write32(struct ath11k_ba + } + } + +- if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && + offset >= ACCESS_ALWAYS_OFF) + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); + } +@@ -219,7 +221,8 @@ u32 ath11k_pci_read32(struct ath11k_base + /* for offset beyond BAR + 4K - 32, may + * need to wakeup MHI to access. + */ +- if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && + offset >= ACCESS_ALWAYS_OFF) + mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + +@@ -243,7 +246,8 @@ u32 ath11k_pci_read32(struct ath11k_base + } + } + +- if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && + offset >= ACCESS_ALWAYS_OFF) + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); + diff --git a/package/kernel/mac80211/patches/ath11k/0103-ath11k-get-msi_data-again-after-request_irq-is-calle.patch b/package/kernel/mac80211/patches/ath11k/0103-ath11k-get-msi_data-again-after-request_irq-is-calle.patch new file mode 100644 index 00000000000000..483ba4973b98c3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0103-ath11k-get-msi_data-again-after-request_irq-is-calle.patch @@ -0,0 +1,76 @@ +From 87b4072d7ef818e368b0f4162a1af2fb4727f51c Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Fri, 19 Nov 2021 15:36:26 +0200 +Subject: [PATCH] ath11k: get msi_data again after request_irq is called + +The reservation mode of interrupts in kernel assigns a dummy vector +when the interrupt is allocated and assigns a real vector when the +request_irq is called. The reservation mode helps to ease vector +pressure when devices with a large amount of queues/interrupts +are initialized, but only a minimal subset of those queues/interrupts +is actually used. + +So on reservation mode, the msi_data may change after request_irq +is called, so ath11k reads msi_data again after request_irq is called, +and then the correct msi_data is programmed into QCA6390 hardware +components. Without this change, spurious interrupt occurs in case of +one MSI vector. When VT-d in BIOS is enabled and ath11k can get 32 MSI +vectors, ath11k always get the same msi_data before and after request_irq, +that's why this change is only required when one MSI vector is to be +supported. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211026041636.5008-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 30 +++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -939,6 +939,25 @@ static void ath11k_pci_free_msi(struct a + pci_free_irq_vectors(ab_pci->pdev); + } + ++static int ath11k_pci_config_msi_data(struct ath11k_pci *ab_pci) ++{ ++ struct msi_desc *msi_desc; ++ ++ msi_desc = irq_get_msi_desc(ab_pci->pdev->irq); ++ if (!msi_desc) { ++ ath11k_err(ab_pci->ab, "msi_desc is NULL!\n"); ++ pci_free_irq_vectors(ab_pci->pdev); ++ return -EINVAL; ++ } ++ ++ ab_pci->msi_ep_base_data = msi_desc->msg.data; ++ ++ ath11k_dbg(ab_pci->ab, ATH11K_DBG_PCI, "pci after request_irq msi_ep_base_data %d\n", ++ ab_pci->msi_ep_base_data); ++ ++ return 0; ++} ++ + static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev) + { + struct ath11k_base *ab = ab_pci->ab; +@@ -1348,6 +1367,17 @@ static int ath11k_pci_probe(struct pci_d + goto err_ce_free; + } + ++ /* kernel may allocate a dummy vector before request_irq and ++ * then allocate a real vector when request_irq is called. ++ * So get msi_data here again to avoid spurious interrupt ++ * as msi_data will configured to srngs. ++ */ ++ ret = ath11k_pci_config_msi_data(ab_pci); ++ if (ret) { ++ ath11k_err(ab, "failed to config msi_data: %d\n", ret); ++ goto err_free_irq; ++ } ++ + ret = ath11k_core_init(ab); + if (ret) { + ath11k_err(ab, "failed to init core: %d\n", ret); diff --git a/package/kernel/mac80211/patches/ath11k/0104-ath11k-add-CE-and-ext-IRQ-flag-to-indicate-irq_handl.patch b/package/kernel/mac80211/patches/ath11k/0104-ath11k-add-CE-and-ext-IRQ-flag-to-indicate-irq_handl.patch new file mode 100644 index 00000000000000..cd6872a744cc7e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0104-ath11k-add-CE-and-ext-IRQ-flag-to-indicate-irq_handl.patch @@ -0,0 +1,92 @@ +From 01279bcd01d965b6526d575e036841778d8e3c4e Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Fri, 19 Nov 2021 15:36:26 +0200 +Subject: [PATCH] ath11k: add CE and ext IRQ flag to indicate irq_handler + +This change adds two flags to indicate whether IRQ handler for CE +and DP can be called. This is because in one MSI vector case, +interrupt is not disabled in hif_stop and hif_irq_disable. Otherwise, +MHI interrupt is disabled too. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Signed-off-by: Baochen Qiang +Link: https://lore.kernel.org/r/20211026041646.5060-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 2 ++ + drivers/net/wireless/ath/ath11k/pci.c | 16 ++++++++++++++++ + 2 files changed, 18 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -200,6 +200,8 @@ enum ath11k_dev_flags { + ATH11K_FLAG_REGISTERED, + ATH11K_FLAG_QMI_FAIL, + ATH11K_FLAG_HTC_SUSPEND_COMPLETE, ++ ATH11K_FLAG_CE_IRQ_ENABLED, ++ ATH11K_FLAG_EXT_IRQ_ENABLED, + }; + + enum ath11k_monitor_flags { +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -578,6 +578,8 @@ static void ath11k_pci_ce_irqs_disable(s + { + int i; + ++ clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); ++ + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; +@@ -611,6 +613,10 @@ static void ath11k_pci_ce_tasklet(struct + static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg) + { + struct ath11k_ce_pipe *ce_pipe = arg; ++ struct ath11k_base *ab = ce_pipe->ab; ++ ++ if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags)) ++ return IRQ_HANDLED; + + /* last interrupt received for this CE */ + ce_pipe->timestamp = jiffies; +@@ -633,6 +639,8 @@ static void __ath11k_pci_ext_irq_disable + { + int i; + ++ clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &sc->dev_flags); ++ + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i]; + +@@ -658,6 +666,8 @@ static void ath11k_pci_ext_irq_enable(st + { + int i; + ++ set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); ++ + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + +@@ -712,6 +722,10 @@ static int ath11k_pci_ext_grp_napi_poll( + static irqreturn_t ath11k_pci_ext_interrupt_handler(int irq, void *arg) + { + struct ath11k_ext_irq_grp *irq_grp = arg; ++ struct ath11k_base *ab = irq_grp->ab; ++ ++ if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags)) ++ return IRQ_HANDLED; + + ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq); + +@@ -858,6 +872,8 @@ static void ath11k_pci_ce_irqs_enable(st + { + int i; + ++ set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); ++ + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; diff --git a/package/kernel/mac80211/patches/ath11k/0105-ath11k-use-ATH11K_PCI_IRQ_DP_OFFSET-for-DP-IRQ.patch b/package/kernel/mac80211/patches/ath11k/0105-ath11k-use-ATH11K_PCI_IRQ_DP_OFFSET-for-DP-IRQ.patch new file mode 100644 index 00000000000000..e08db10ec0ba72 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0105-ath11k-use-ATH11K_PCI_IRQ_DP_OFFSET-for-DP-IRQ.patch @@ -0,0 +1,50 @@ +From 4ab4693f327ad015c4637ae42dc53c8471aed9c8 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Fri, 19 Nov 2021 15:36:26 +0200 +Subject: [PATCH] ath11k: use ATH11K_PCI_IRQ_DP_OFFSET for DP IRQ + +Like ATH11K_PCI_IRQ_CE0_OFFSET, define ATH11K_PCI_IRQ_DP_OFFSET for +DP to save the IRQ instead of base_vector from MSI config. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Signed-off-by: Baochen Qiang +Link: https://lore.kernel.org/r/20211026041655.5112-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -16,7 +16,8 @@ + #define ATH11K_PCI_BAR_NUM 0 + #define ATH11K_PCI_DMA_MASK 32 + +-#define ATH11K_PCI_IRQ_CE0_OFFSET 3 ++#define ATH11K_PCI_IRQ_CE0_OFFSET 3 ++#define ATH11K_PCI_IRQ_DP_OFFSET 14 + + #define WINDOW_ENABLE_BIT 0x40000000 + #define WINDOW_REG_ADDRESS 0x310c +@@ -742,9 +743,8 @@ static irqreturn_t ath11k_pci_ext_interr + static int ath11k_pci_ext_irq_config(struct ath11k_base *ab) + { + int i, j, ret, num_vectors = 0; +- u32 user_base_data = 0, base_vector = 0, base_idx; ++ u32 user_base_data = 0, base_vector = 0; + +- base_idx = ATH11K_PCI_IRQ_CE0_OFFSET + CE_COUNT_MAX; + ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), "DP", + &num_vectors, + &user_base_data, +@@ -774,7 +774,7 @@ static int ath11k_pci_ext_irq_config(str + } + + irq_grp->num_irq = num_irq; +- irq_grp->irqs[0] = base_idx + i; ++ irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i; + + for (j = 0; j < irq_grp->num_irq; j++) { + int irq_idx = irq_grp->irqs[j]; diff --git a/package/kernel/mac80211/patches/ath11k/0106-ath11k-refactor-multiple-MSI-vector-implementation.patch b/package/kernel/mac80211/patches/ath11k/0106-ath11k-refactor-multiple-MSI-vector-implementation.patch new file mode 100644 index 00000000000000..2716195f5a070c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0106-ath11k-refactor-multiple-MSI-vector-implementation.patch @@ -0,0 +1,173 @@ +From c41a6700b276ddf6ef93dcb43baca51ea0c4c7c1 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Fri, 19 Nov 2021 15:36:26 +0200 +Subject: [PATCH] ath11k: refactor multiple MSI vector implementation + +This is to prepare for one MSI vector support. IRQ enable and disable +of CE and DP are done only in case of multiple MSI vectors. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Signed-off-by: Baochen Qiang +Link: https://lore.kernel.org/r/20211026041705.5167-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 48 ++++++++++++++++++++++----- + drivers/net/wireless/ath/ath11k/pci.h | 3 ++ + 2 files changed, 43 insertions(+), 8 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -486,11 +486,11 @@ int ath11k_pci_get_user_msi_assignment(s + for (idx = 0; idx < msi_config->total_users; idx++) { + if (strcmp(user_name, msi_config->users[idx].name) == 0) { + *num_vectors = msi_config->users[idx].num_vectors; +- *user_base_data = msi_config->users[idx].base_vector +- + ab_pci->msi_ep_base_data; +- *base_vector = msi_config->users[idx].base_vector; ++ *base_vector = msi_config->users[idx].base_vector; ++ *user_base_data = *base_vector + ab_pci->msi_ep_base_data; + +- ath11k_dbg(ab, ATH11K_DBG_PCI, "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", ++ ath11k_dbg(ab, ATH11K_DBG_PCI, ++ "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", + user_name, *num_vectors, *user_base_data, + *base_vector); + +@@ -561,16 +561,30 @@ static void ath11k_pci_free_irq(struct a + + static void ath11k_pci_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) + { ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 irq_idx; + ++ /* In case of one MSI vector, we handle irq enable/disable in a ++ * uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; + enable_irq(ab->irq_num[irq_idx]); + } + + static void ath11k_pci_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) + { ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 irq_idx; + ++ /* In case of one MSI vector, we handle irq enable/disable in a ++ * uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; + disable_irq_nosync(ab->irq_num[irq_idx]); + } +@@ -630,8 +644,15 @@ static irqreturn_t ath11k_pci_ce_interru + + static void ath11k_pci_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) + { ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); + int i; + ++ /* In case of one MSI vector, we handle irq enable/disable ++ * in a uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ + for (i = 0; i < irq_grp->num_irq; i++) + disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); + } +@@ -657,8 +678,15 @@ static void __ath11k_pci_ext_irq_disable + + static void ath11k_pci_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) + { ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); + int i; + ++ /* In case of one MSI vector, we handle irq enable/disable in a ++ * uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); + } +@@ -742,6 +770,7 @@ static irqreturn_t ath11k_pci_ext_interr + + static int ath11k_pci_ext_irq_config(struct ath11k_base *ab) + { ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + int i, j, ret, num_vectors = 0; + u32 user_base_data = 0, base_vector = 0; + +@@ -788,16 +817,15 @@ static int ath11k_pci_ext_irq_config(str + + irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); + ret = request_irq(irq, ath11k_pci_ext_interrupt_handler, +- IRQF_SHARED, ++ ab_pci->irq_flags, + "DP_EXT_IRQ", irq_grp); + if (ret) { + ath11k_err(ab, "failed request irq %d: %d\n", + vector, ret); + return ret; + } +- +- disable_irq_nosync(ab->irq_num[irq_idx]); + } ++ ath11k_pci_ext_grp_disable(irq_grp); + } + + return 0; +@@ -805,6 +833,7 @@ static int ath11k_pci_ext_irq_config(str + + static int ath11k_pci_config_irq(struct ath11k_base *ab) + { ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct ath11k_ce_pipe *ce_pipe; + u32 msi_data_start; + u32 msi_data_count, msi_data_idx; +@@ -832,7 +861,7 @@ static int ath11k_pci_config_irq(struct + tasklet_setup(&ce_pipe->intr_tq, ath11k_pci_ce_tasklet); + + ret = request_irq(irq, ath11k_pci_ce_interrupt_handler, +- IRQF_SHARED, irq_name[irq_idx], ++ ab_pci->irq_flags, irq_name[irq_idx], + ce_pipe); + if (ret) { + ath11k_err(ab, "failed to request irq %d: %d\n", +@@ -926,6 +955,9 @@ static int ath11k_pci_alloc_msi(struct a + return -EINVAL; + else + return num_vectors; ++ } else { ++ set_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags); ++ ab_pci->irq_flags = IRQF_SHARED; + } + ath11k_pci_msi_disable(ab_pci); + +--- a/drivers/net/wireless/ath/ath11k/pci.h ++++ b/drivers/net/wireless/ath/ath11k/pci.h +@@ -68,6 +68,7 @@ enum ath11k_pci_flags { + ATH11K_PCI_FLAG_INIT_DONE, + ATH11K_PCI_FLAG_IS_MSI_64, + ATH11K_PCI_ASPM_RESTORE, ++ ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, + }; + + struct ath11k_pci { +@@ -87,6 +88,8 @@ struct ath11k_pci { + /* enum ath11k_pci_flags */ + unsigned long flags; + u16 link_ctl; ++ ++ unsigned long irq_flags; + }; + + static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) diff --git a/package/kernel/mac80211/patches/ath11k/0107-ath11k-add-support-one-MSI-vector.patch b/package/kernel/mac80211/patches/ath11k/0107-ath11k-add-support-one-MSI-vector.patch new file mode 100644 index 00000000000000..cb76177c7d06fe --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0107-ath11k-add-support-one-MSI-vector.patch @@ -0,0 +1,195 @@ +From ac6e73483f7b4b5bde23b14fc3aaafc8341ae0c7 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Fri, 19 Nov 2021 15:36:26 +0200 +Subject: [PATCH] ath11k: add support one MSI vector + +On some platforms it's not possible to allocate 32 MSI vectors for various +reasons, be it kernel configuration, VT-d disabled, buggy BIOS etc. So +ath11k was not able to use QCA6390 PCI devices on those platforms. Add +support for one MSI vector to solve that. + +In case of one MSI vector, interrupt migration needs to be disabled. This +is because when interrupt migration happens, the msi_data may change. +However, msi_data is already programmed to rings during initial phase and +ath11k has no way to know that msi_data is changed during run time and +reprogram again. + +In case of one MSI vector, MHI subsystem should not use IRQF_NO_SUSPEND +as QCA6390 doesn't set this flag too. Ath11k doesn't need to leave +IRQ enabled in suspend state. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Signed-off-by: Baochen Qiang +Link: https://lore.kernel.org/r/20211026041714.5219-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/mhi.c | 14 ++++++-- + drivers/net/wireless/ath/ath11k/pci.c | 52 ++++++++++++++++++++------- + 2 files changed, 51 insertions(+), 15 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -248,6 +248,7 @@ static int ath11k_mhi_get_msi(struct ath + u32 user_base_data, base_vector; + int ret, num_vectors, i; + int *irq; ++ unsigned int msi_data; + + ret = ath11k_pci_get_user_msi_assignment(ab_pci, + "MHI", &num_vectors, +@@ -262,9 +263,15 @@ static int ath11k_mhi_get_msi(struct ath + if (!irq) + return -ENOMEM; + +- for (i = 0; i < num_vectors; i++) ++ for (i = 0; i < num_vectors; i++) { ++ msi_data = base_vector; ++ ++ if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ msi_data += i; ++ + irq[i] = ath11k_pci_get_msi_irq(ab->dev, +- base_vector + i); ++ msi_data); ++ } + + ab_pci->mhi_ctrl->irq = irq; + ab_pci->mhi_ctrl->nr_irqs = num_vectors; +@@ -341,6 +348,9 @@ int ath11k_mhi_register(struct ath11k_pc + return ret; + } + ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; ++ + mhi_ctrl->iova_start = 0; + mhi_ctrl->iova_stop = 0xffffffff; + mhi_ctrl->sbl_size = SZ_512K; +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -77,6 +77,17 @@ static const struct ath11k_msi_config at + }, + }; + ++static const struct ath11k_msi_config msi_config_one_msi = { ++ .total_vectors = 1, ++ .total_users = 4, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, ++ { .name = "CE", .num_vectors = 1, .base_vector = 0 }, ++ { .name = "WAKE", .num_vectors = 1, .base_vector = 0 }, ++ { .name = "DP", .num_vectors = 1, .base_vector = 0 }, ++ }, ++}; ++ + static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { + "bhi", + "mhi-er0", +@@ -619,16 +630,18 @@ static void ath11k_pci_sync_ce_irqs(stru + static void ath11k_pci_ce_tasklet(struct tasklet_struct *t) + { + struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq); ++ int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; + + ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); + +- ath11k_pci_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); ++ enable_irq(ce_pipe->ab->irq_num[irq_idx]); + } + + static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg) + { + struct ath11k_ce_pipe *ce_pipe = arg; + struct ath11k_base *ab = ce_pipe->ab; ++ int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; + + if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags)) + return IRQ_HANDLED; +@@ -636,7 +649,8 @@ static irqreturn_t ath11k_pci_ce_interru + /* last interrupt received for this CE */ + ce_pipe->timestamp = jiffies; + +- ath11k_pci_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); ++ disable_irq_nosync(ab->irq_num[irq_idx]); ++ + tasklet_schedule(&ce_pipe->intr_tq); + + return IRQ_HANDLED; +@@ -735,11 +749,13 @@ static int ath11k_pci_ext_grp_napi_poll( + napi); + struct ath11k_base *ab = irq_grp->ab; + int work_done; ++ int i; + + work_done = ath11k_dp_service_srng(ab, irq_grp, budget); + if (work_done < budget) { + napi_complete_done(napi, work_done); +- ath11k_pci_ext_grp_enable(irq_grp); ++ for (i = 0; i < irq_grp->num_irq; i++) ++ enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); + } + + if (work_done > budget) +@@ -752,6 +768,7 @@ static irqreturn_t ath11k_pci_ext_interr + { + struct ath11k_ext_irq_grp *irq_grp = arg; + struct ath11k_base *ab = irq_grp->ab; ++ int i; + + if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags)) + return IRQ_HANDLED; +@@ -761,7 +778,8 @@ static irqreturn_t ath11k_pci_ext_interr + /* last interrupt received for this group */ + irq_grp->timestamp = jiffies; + +- ath11k_pci_ext_grp_disable(irq_grp); ++ for (i = 0; i < irq_grp->num_irq; i++) ++ disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); + + napi_schedule(&irq_grp->napi); + +@@ -947,18 +965,25 @@ static int ath11k_pci_alloc_msi(struct a + msi_config->total_vectors, + msi_config->total_vectors, + PCI_IRQ_MSI); +- if (num_vectors != msi_config->total_vectors) { +- ath11k_err(ab, "failed to get %d MSI vectors, only %d available", +- msi_config->total_vectors, num_vectors); +- +- if (num_vectors >= 0) +- return -EINVAL; +- else +- return num_vectors; +- } else { ++ if (num_vectors == msi_config->total_vectors) { + set_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags); + ab_pci->irq_flags = IRQF_SHARED; ++ } else { ++ num_vectors = pci_alloc_irq_vectors(ab_pci->pdev, ++ 1, ++ 1, ++ PCI_IRQ_MSI); ++ if (num_vectors < 0) { ++ ret = -EINVAL; ++ goto reset_msi_config; ++ } ++ clear_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags); ++ ab_pci->msi_config = &msi_config_one_msi; ++ ab_pci->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; ++ ath11k_dbg(ab, ATH11K_DBG_PCI, "request MSI one vector\n"); + } ++ ath11k_info(ab, "MSI vectors: %d\n", num_vectors); ++ + ath11k_pci_msi_disable(ab_pci); + + msi_desc = irq_get_msi_desc(ab_pci->pdev->irq); +@@ -979,6 +1004,7 @@ static int ath11k_pci_alloc_msi(struct a + free_msi_vector: + pci_free_irq_vectors(ab_pci->pdev); + ++reset_msi_config: + return ret; + } + diff --git a/package/kernel/mac80211/patches/ath11k/0108-ath11k-do-not-restore-ASPM-in-case-of-single-MSI-vec.patch b/package/kernel/mac80211/patches/ath11k/0108-ath11k-do-not-restore-ASPM-in-case-of-single-MSI-vec.patch new file mode 100644 index 00000000000000..18eec3d3ccc194 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0108-ath11k-do-not-restore-ASPM-in-case-of-single-MSI-vec.patch @@ -0,0 +1,41 @@ +From 915a081ff307d61d6551d6c16b542e03775353c4 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Fri, 19 Nov 2021 15:36:26 +0200 +Subject: [PATCH] ath11k: do not restore ASPM in case of single MSI vector + +Current code enables ASPM by default, it allows MHI to enter M2 state. +In case of one MSI vector, system hang is observed if ath11k does MHI +register reading in this state. The issue was reported on Dell XPS 13 +9310 but is seen also on XPS 15 and XPS 17 laptops. + +The workaround here is to prevent MHI from entering M2 state, this can +be done by disabling ASPM if only one MSI vector is used. When using 32 +vectors ASPM is enabled as before. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Signed-off-by: Baochen Qiang +Link: https://lore.kernel.org/r/20211026041722.5271-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -1229,7 +1229,13 @@ static int ath11k_pci_start(struct ath11 + + set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); + +- ath11k_pci_aspm_restore(ab_pci); ++ /* TODO: for now don't restore ASPM in case of single MSI ++ * vector as MHI register reading in M2 causes system hang. ++ */ ++ if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ ath11k_pci_aspm_restore(ab_pci); ++ else ++ ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n"); + + ath11k_pci_ce_irqs_enable(ab); + ath11k_ce_rx_post_buf(ab); diff --git a/package/kernel/mac80211/patches/ath11k/0109-ath11k-Set-IRQ-affinity-to-CPU0-in-case-of-one-MSI-v.patch b/package/kernel/mac80211/patches/ath11k/0109-ath11k-Set-IRQ-affinity-to-CPU0-in-case-of-one-MSI-v.patch new file mode 100644 index 00000000000000..e86edad03c342b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0109-ath11k-Set-IRQ-affinity-to-CPU0-in-case-of-one-MSI-v.patch @@ -0,0 +1,89 @@ +From e94b07493da31705c3fdd0b2854f0cffe1dacb3c Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Fri, 19 Nov 2021 15:36:26 +0200 +Subject: [PATCH] ath11k: Set IRQ affinity to CPU0 in case of one MSI vector + +With VT-d disabled on Intel platform, ath11k gets only one MSI +vector. In that case, ath11k does not free IRQ when doing suspend, +hence the kernel has to migrate it to CPU0 (if it was affine to +other CPUs) and allocates a new MSI vector. However, ath11k has +no chance to reconfig it to HW srngs during this phase, thus +ath11k fails to resume. + +This issue can be fixed by setting IRQ affinity to CPU0 before +request_irq is called. With such affinity, migration will not +happen and thus the vector keeps unchanged during suspend/resume. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211026041732.5323-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -849,6 +849,15 @@ static int ath11k_pci_ext_irq_config(str + return 0; + } + ++static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, ++ const struct cpumask *m) ++{ ++ if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return 0; ++ ++ return irq_set_affinity_hint(ab_pci->pdev->irq, m); ++} ++ + static int ath11k_pci_config_irq(struct ath11k_base *ab) + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +@@ -865,6 +874,12 @@ static int ath11k_pci_config_irq(struct + if (ret) + return ret; + ++ ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0)); ++ if (ret) { ++ ath11k_err(ab, "failed to set irq affinity %d\n", ret); ++ return ret; ++ } ++ + /* Configure CE irqs */ + for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +@@ -884,7 +899,7 @@ static int ath11k_pci_config_irq(struct + if (ret) { + ath11k_err(ab, "failed to request irq %d: %d\n", + irq_idx, ret); +- return ret; ++ goto err_irq_affinity_cleanup; + } + + ab->irq_num[irq_idx] = irq; +@@ -895,9 +910,13 @@ static int ath11k_pci_config_irq(struct + + ret = ath11k_pci_ext_irq_config(ab); + if (ret) +- return ret; ++ goto err_irq_affinity_cleanup; + + return 0; ++ ++err_irq_affinity_cleanup: ++ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); ++ return ret; + } + + static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab) +@@ -1494,6 +1513,8 @@ static void ath11k_pci_remove(struct pci + struct ath11k_base *ab = pci_get_drvdata(pdev); + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + ++ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); ++ + if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath11k_pci_power_down(ab); + ath11k_debugfs_soc_destroy(ab); diff --git a/package/kernel/mac80211/patches/ath11k/0110-ath11k-change-to-treat-alpha-code-na-as-world-wide-r.patch b/package/kernel/mac80211/patches/ath11k/0110-ath11k-change-to-treat-alpha-code-na-as-world-wide-r.patch new file mode 100644 index 00000000000000..a8de4524d6b417 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0110-ath11k-change-to-treat-alpha-code-na-as-world-wide-r.patch @@ -0,0 +1,35 @@ +From f8108250e331b8f0273c53afb9e2db5068e59b2e Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 22 Nov 2021 13:13:57 +0200 +Subject: [PATCH] ath11k: change to treat alpha code na as world wide regdomain + +Some firmware versions for WCN6855 report the default regdomain with +alpha code "na" by default when load as a world wide regdomain, ath11k +should treat it as a world wide alpha code. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211118094848.7776-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -5927,7 +5927,13 @@ static void ath11k_wmi_htc_tx_complete(s + + static bool ath11k_reg_is_world_alpha(char *alpha) + { +- return alpha[0] == '0' && alpha[1] == '0'; ++ if (alpha[0] == '0' && alpha[1] == '0') ++ return true; ++ ++ if (alpha[0] == 'n' && alpha[1] == 'a') ++ return true; ++ ++ return false; + } + + static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb) diff --git a/package/kernel/mac80211/patches/ath11k/0111-ath11k-calculate-the-correct-NSS-of-peer-for-HE-capa.patch b/package/kernel/mac80211/patches/ath11k/0111-ath11k-calculate-the-correct-NSS-of-peer-for-HE-capa.patch new file mode 100644 index 00000000000000..f0d3f46a69b831 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0111-ath11k-calculate-the-correct-NSS-of-peer-for-HE-capa.patch @@ -0,0 +1,80 @@ +From 3db26ecf7114370e451e296e33a0af3303d32819 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 22 Nov 2021 13:13:57 +0200 +Subject: [PATCH] ath11k: calculate the correct NSS of peer for HE capabilities + +When connected to 6G mode AP, it does not have VHT/HT capabilities, +so the NSS is not set, then it is 1 by default. + +This patch is to calculate the NSS with supported HE-MCS and NSS set +of HE capabilities. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01280-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211118095453.8030-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 37 ++++++++++++++++++++++++++- + 1 file changed, 36 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1921,7 +1921,6 @@ static void ath11k_peer_assoc_h_he(struc + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; +- u8 ampdu_factor; + enum nl80211_band band; + u16 *he_mcs_mask; + u8 max_nss, he_mcs; +@@ -1929,6 +1928,9 @@ static void ath11k_peer_assoc_h_he(struc + int i, he_nss, nss_idx; + bool user_rate_valid = true; + u32 rx_nss, tx_nss, nss_160; ++ u8 ampdu_factor, rx_mcs_80, rx_mcs_160; ++ u16 mcs_160_map, mcs_80_map; ++ bool support_160; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; +@@ -1943,6 +1945,39 @@ static void ath11k_peer_assoc_h_he(struc + return; + + arg->he_flag = true; ++ support_160 = !!(he_cap->he_cap_elem.phy_cap_info[0] & ++ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G); ++ ++ /* Supported HE-MCS and NSS Set of peer he_cap is intersection with self he_cp */ ++ mcs_160_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); ++ mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); ++ ++ if (support_160) { ++ for (i = 7; i >= 0; i--) { ++ u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; ++ ++ if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { ++ rx_mcs_160 = i + 1; ++ break; ++ } ++ } ++ } ++ ++ for (i = 7; i >= 0; i--) { ++ u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; ++ ++ if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { ++ rx_mcs_80 = i + 1; ++ break; ++ } ++ } ++ ++ if (support_160) ++ max_nss = min(rx_mcs_80, rx_mcs_160); ++ else ++ max_nss = rx_mcs_80; ++ ++ arg->peer_nss = min(sta->rx_nss, max_nss); + + memcpy_and_pad(&arg->peer_he_cap_macinfo, + sizeof(arg->peer_he_cap_macinfo), diff --git a/package/kernel/mac80211/patches/ath11k/0112-ath11k-fix-read-fail-for-htt_stats-and-htt_peer_stat.patch b/package/kernel/mac80211/patches/ath11k/0112-ath11k-fix-read-fail-for-htt_stats-and-htt_peer_stat.patch new file mode 100644 index 00000000000000..f5385af6c428c5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0112-ath11k-fix-read-fail-for-htt_stats-and-htt_peer_stat.patch @@ -0,0 +1,206 @@ +From 1370634054d4e1e4794057b06ac651b6366ce97d Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 22 Nov 2021 13:13:58 +0200 +Subject: [PATCH] ath11k: fix read fail for htt_stats and htt_peer_stats for + single pdev + +The pdev id is set to 0 for single pdev configured hardware, the real +pdev id is not 0 in firmware, for example, its pdev id is 1 for 5G/6G +phy and 2 for 2G band phy. For HTT_H2T_MSG_TYPE_EXT_STATS_CFG message, +firmware parse the pdev_mask to its pdev id, ath11k set it to 0 for +single pdev, it is not correct, need set it with the real pdev id of +firmware. + +Save the real pdev id report by firmware and set it correctly. + +Below commands run success with this patch: +cat /sys/kernel/debug/ieee80211/phy0/ath11k/htt_stats +cat /sys/kernel/debug/ieee80211/phy0/netdev\:wls1/stations/00\:03\:7f\:75\:59\:85/htt_peer_stats + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211118095700.8149-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 5 ++ + drivers/net/wireless/ath/ath11k/dp_tx.c | 9 +++- + drivers/net/wireless/ath/ath11k/mac.c | 61 +++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/mac.h | 4 ++ + drivers/net/wireless/ath/ath11k/wmi.c | 6 +++ + drivers/net/wireless/ath/ath11k/wmi.h | 4 +- + 6 files changed, 86 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -714,6 +714,11 @@ struct ath11k_base { + /* Protects data like peers */ + spinlock_t base_lock; + struct ath11k_pdev pdevs[MAX_RADIOS]; ++ struct { ++ enum WMI_HOST_WLAN_BAND supported_bands; ++ u32 pdev_id; ++ } target_pdev_ids[MAX_RADIOS]; ++ u8 target_pdev_count; + struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS]; + struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; + unsigned long long free_vdev_map; +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -9,6 +9,7 @@ + #include "debugfs_sta.h" + #include "hw.h" + #include "peer.h" ++#include "mac.h" + + static enum hal_tcl_encap_type + ath11k_dp_tx_get_encap_type(struct ath11k_vif *arvif, struct sk_buff *skb) +@@ -985,6 +986,7 @@ ath11k_dp_tx_htt_h2t_ext_stats_req(struc + struct ath11k_dp *dp = &ab->dp; + struct sk_buff *skb; + struct htt_ext_stats_cfg_cmd *cmd; ++ u32 pdev_id; + int len = sizeof(*cmd); + int ret; + +@@ -998,7 +1000,12 @@ ath11k_dp_tx_htt_h2t_ext_stats_req(struc + memset(cmd, 0, sizeof(*cmd)); + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_EXT_STATS_CFG; + +- cmd->hdr.pdev_mask = 1 << ar->pdev->pdev_id; ++ if (ab->hw_params.single_pdev_only) ++ pdev_id = ath11k_mac_get_target_pdev_id(ar); ++ else ++ pdev_id = ar->pdev->pdev_id; ++ ++ cmd->hdr.pdev_mask = 1 << pdev_id; + + cmd->hdr.stats_type = type; + cmd->cfg_param0 = cfg_params->cfg0; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -554,6 +554,67 @@ struct ath11k *ath11k_mac_get_ar_by_pdev + return NULL; + } + ++struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab) ++{ ++ struct ath11k *ar; ++ struct ath11k_pdev *pdev; ++ struct ath11k_vif *arvif; ++ int i; ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ if (arvif->is_up) ++ return arvif; ++ } ++ } ++ ++ return NULL; ++} ++ ++static bool ath11k_mac_band_match(enum nl80211_band band1, enum WMI_HOST_WLAN_BAND band2) ++{ ++ return (((band1 == NL80211_BAND_2GHZ) && (band2 & WMI_HOST_WLAN_2G_CAP)) || ++ (((band1 == NL80211_BAND_5GHZ) || (band1 == NL80211_BAND_6GHZ)) && ++ (band2 & WMI_HOST_WLAN_5G_CAP))); ++} ++ ++u8 ath11k_mac_get_target_pdev_id_from_vif(struct ath11k_vif *arvif) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ struct ieee80211_vif *vif = arvif->vif; ++ struct cfg80211_chan_def def; ++ enum nl80211_band band; ++ u8 pdev_id = ab->target_pdev_ids[0].pdev_id; ++ int i; ++ ++ if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) ++ return pdev_id; ++ ++ band = def.chan->band; ++ ++ for (i = 0; i < ab->target_pdev_count; i++) { ++ if (ath11k_mac_band_match(band, ab->target_pdev_ids[i].supported_bands)) ++ return ab->target_pdev_ids[i].pdev_id; ++ } ++ ++ return pdev_id; ++} ++ ++u8 ath11k_mac_get_target_pdev_id(struct ath11k *ar) ++{ ++ struct ath11k_vif *arvif; ++ ++ arvif = ath11k_mac_get_vif_up(ar->ab); ++ ++ if (arvif) ++ return ath11k_mac_get_target_pdev_id_from_vif(arvif); ++ else ++ return ar->ab->target_pdev_ids[0].pdev_id; ++} ++ + static void ath11k_pdev_caps_update(struct ath11k *ar) + { + struct ath11k_base *ab = ar->ab; +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -144,6 +144,10 @@ void ath11k_mac_scan_finish(struct ath11 + struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id); + struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab, + u32 vdev_id); ++u8 ath11k_mac_get_target_pdev_id(struct ath11k *ar); ++u8 ath11k_mac_get_target_pdev_id_from_vif(struct ath11k_vif *arvif); ++struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab); ++ + struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id); + struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id); + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -337,6 +337,7 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(st + struct ath11k_pdev *pdev) + { + struct wmi_mac_phy_capabilities *mac_phy_caps; ++ struct ath11k_base *ab = wmi_handle->wmi_ab->ab; + struct ath11k_band_cap *cap_band; + struct ath11k_pdev_cap *pdev_cap = &pdev->cap; + u32 phy_map; +@@ -368,6 +369,10 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(st + pdev->pdev_id = mac_phy_caps->pdev_id; + pdev_cap->supported_bands |= mac_phy_caps->supported_bands; + pdev_cap->ampdu_density = mac_phy_caps->ampdu_density; ++ ab->target_pdev_ids[ab->target_pdev_count].supported_bands = ++ mac_phy_caps->supported_bands; ++ ab->target_pdev_ids[ab->target_pdev_count].pdev_id = mac_phy_caps->pdev_id; ++ ab->target_pdev_count++; + + /* Take non-zero tx/rx chainmask. If tx/rx chainmask differs from + * band to band for a single radio, need to see how this should be +@@ -4230,6 +4235,7 @@ static int ath11k_wmi_tlv_ext_soc_hal_re + svc_rdy_ext->param.num_phy = svc_rdy_ext->soc_hal_reg_caps->num_phy; + + soc->num_radios = 0; ++ soc->target_pdev_count = 0; + phy_id_map = svc_rdy_ext->pref_hw_mode_caps.phy_id_map; + + while (phy_id_map && soc->num_radios < MAX_RADIOS) { +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -113,10 +113,10 @@ enum wmi_host_hw_mode_priority { + WMI_HOST_HW_MODE_MAX_PRI + }; + +-enum { ++enum WMI_HOST_WLAN_BAND { + WMI_HOST_WLAN_2G_CAP = 0x1, + WMI_HOST_WLAN_5G_CAP = 0x2, +- WMI_HOST_WLAN_2G_5G_CAP = 0x3, ++ WMI_HOST_WLAN_2G_5G_CAP = WMI_HOST_WLAN_2G_CAP | WMI_HOST_WLAN_5G_CAP, + }; + + /* Parameters used for WMI_VDEV_PARAM_AUTORATE_MISC_CFG command. diff --git a/package/kernel/mac80211/patches/ath11k/0113-ath11k-skip-sending-vdev-down-for-channel-switch.patch b/package/kernel/mac80211/patches/ath11k/0113-ath11k-skip-sending-vdev-down-for-channel-switch.patch new file mode 100644 index 00000000000000..d0ae98c49155ad --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0113-ath11k-skip-sending-vdev-down-for-channel-switch.patch @@ -0,0 +1,68 @@ +From a4146249a33381f41f6d15eaa1797d7ba1820a31 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 22 Nov 2021 13:13:58 +0200 +Subject: [PATCH] ath11k: skip sending vdev down for channel switch + +The ath11k driver currently sends vdev down to the firmware before +updating the channel context, which is followed by a vdev restart +command. + +Sending vdev down is not required before sending a vdev restart, +because the firmware internally does vdev down when ath11k sends +a vdev restart command. + +Firmware will happen crash while channel switch without this change. + +Hence skip the vdev down command sending when updating the channel +context and then fix the firmware crash issue. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211118095901.8271-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 32 +-------------------------- + 1 file changed, 1 insertion(+), 31 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6467,37 +6467,7 @@ ath11k_mac_update_vif_chan(struct ath11k + + lockdep_assert_held(&ar->conf_mutex); + +- for (i = 0; i < n_vifs; i++) { +- arvif = (void *)vifs[i].vif->drv_priv; +- +- if (vifs[i].vif->type == NL80211_IFTYPE_MONITOR) +- monitor_vif = true; +- +- ath11k_dbg(ab, ATH11K_DBG_MAC, +- "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", +- arvif->vdev_id, +- vifs[i].old_ctx->def.chan->center_freq, +- vifs[i].new_ctx->def.chan->center_freq, +- vifs[i].old_ctx->def.width, +- vifs[i].new_ctx->def.width); +- +- if (WARN_ON(!arvif->is_started)) +- continue; +- +- if (WARN_ON(!arvif->is_up)) +- continue; +- +- ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); +- if (ret) { +- ath11k_warn(ab, "failed to down vdev %d: %d\n", +- arvif->vdev_id, ret); +- continue; +- } +- +- ar->num_started_vdevs--; +- } +- +- /* All relevant vdevs are downed and associated channel resources ++ /* Associated channel resources of all relevant vdevs + * should be available for the channel switch now. + */ + diff --git a/package/kernel/mac80211/patches/ath11k/0114-ath11k-add-read-variant-from-SMBIOS-for-download-boa.patch b/package/kernel/mac80211/patches/ath11k/0114-ath11k-add-read-variant-from-SMBIOS-for-download-boa.patch new file mode 100644 index 00000000000000..9f93f4401c6b89 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0114-ath11k-add-read-variant-from-SMBIOS-for-download-boa.patch @@ -0,0 +1,157 @@ +From 46e46db313a2bf3c48cac4eb8bdb613b762f301b Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 22 Nov 2021 13:13:58 +0200 +Subject: [PATCH] ath11k: add read variant from SMBIOS for download board data + +This is to read variant from SMBIOS such as read from DT, the variant +string will be used to one part of string which used to search board +data from board-2.bin. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211118100033.8384-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 79 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 13 +++++ + drivers/net/wireless/ath/ath11k/qmi.c | 4 ++ + 3 files changed, 96 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -8,6 +8,9 @@ + #include + #include + #include ++#include ++#include ++ + #include "core.h" + #include "dp_tx.h" + #include "dp_rx.h" +@@ -384,6 +387,82 @@ int ath11k_core_resume(struct ath11k_bas + } + EXPORT_SYMBOL(ath11k_core_resume); + ++static void ath11k_core_check_bdfext(const struct dmi_header *hdr, void *data) ++{ ++ struct ath11k_base *ab = data; ++ const char *bdf_ext; ++ const char *magic = ATH11K_SMBIOS_BDF_EXT_MAGIC; ++ u8 bdf_enabled; ++ int i; ++ size_t len; ++ ++ if (ab->qmi.target.bdf_ext[0] != '\0') ++ return; ++ ++ if (hdr->type != ATH11K_SMBIOS_BDF_EXT_TYPE) ++ return; ++ ++ if (hdr->length != ATH11K_SMBIOS_BDF_EXT_LENGTH) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "wrong smbios bdf ext type length (%d).\n", ++ hdr->length); ++ return; ++ } ++ ++ bdf_enabled = *((u8 *)hdr + ATH11K_SMBIOS_BDF_EXT_OFFSET); ++ if (!bdf_enabled) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "bdf variant name not found.\n"); ++ return; ++ } ++ ++ /* Only one string exists (per spec) */ ++ bdf_ext = (char *)hdr + hdr->length; ++ ++ if (memcmp(bdf_ext, magic, strlen(magic)) != 0) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "bdf variant magic does not match.\n"); ++ return; ++ } ++ ++ len = strlen(bdf_ext); ++ for (i = 0; i < len; i++) { ++ if (!isascii(bdf_ext[i]) || !isprint(bdf_ext[i])) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "bdf variant name contains non ascii chars.\n"); ++ return; ++ } ++ } ++ ++ /* Copy extension name without magic prefix */ ++ if (strscpy(ab->qmi.target.bdf_ext, bdf_ext + strlen(magic), ++ sizeof(ab->qmi.target.bdf_ext)) < 0) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "bdf variant string is longer than the buffer can accommodate (variant: %s)\n", ++ bdf_ext); ++ return; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "found and validated bdf variant smbios_type 0x%x bdf %s\n", ++ ATH11K_SMBIOS_BDF_EXT_TYPE, bdf_ext); ++} ++ ++int ath11k_core_check_smbios(struct ath11k_base *ab) ++{ ++ int ret; ++ ++ ab->qmi.target.bdf_ext[0] = '\0'; ++ ++ ret = dmi_walk(ath11k_core_check_bdfext, ab); ++ if (ret) ++ return ret; ++ ++ if (ab->qmi.target.bdf_ext[0] == '\0') ++ return -ENODATA; ++ ++ return 0; ++} ++ + int ath11k_core_check_dt(struct ath11k_base *ab) + { + size_t max_len = sizeof(ab->qmi.target.bdf_ext); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -938,6 +938,18 @@ struct ath11k_fw_stats_bcn { + u32 tx_bcn_outage_cnt; + }; + ++/* SMBIOS type containing Board Data File Name Extension */ ++#define ATH11K_SMBIOS_BDF_EXT_TYPE 0xF8 ++ ++/* SMBIOS type structure length (excluding strings-set) */ ++#define ATH11K_SMBIOS_BDF_EXT_LENGTH 0x9 ++ ++/* Offset pointing to Board Data File Name Extension */ ++#define ATH11K_SMBIOS_BDF_EXT_OFFSET 0x8 ++ ++/* The magic used by QCA spec */ ++#define ATH11K_SMBIOS_BDF_EXT_MAGIC "BDF_" ++ + extern const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq8074[]; + extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq8074[]; + extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq6018[]; +@@ -959,6 +971,7 @@ int ath11k_core_fetch_bdf(struct ath11k_ + struct ath11k_board_data *bd); + void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); + int ath11k_core_check_dt(struct ath11k_base *ath11k); ++int ath11k_core_check_smbios(struct ath11k_base *ab); + + void ath11k_core_halt(struct ath11k *ar); + int ath11k_core_resume(struct ath11k_base *ab); +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1991,6 +1991,10 @@ static int ath11k_qmi_request_target_cap + ab->qmi.target.fw_build_timestamp, + ab->qmi.target.fw_build_id); + ++ r = ath11k_core_check_smbios(ab); ++ if (r) ++ ath11k_dbg(ab, ATH11K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); ++ + r = ath11k_core_check_dt(ab); + if (r) + ath11k_dbg(ab, ATH11K_DBG_QMI, "DT bdf variant name not set.\n"); diff --git a/package/kernel/mac80211/patches/ath11k/0115-ath11k-Fix-mon-status-ring-rx-tlv-processing.patch b/package/kernel/mac80211/patches/ath11k/0115-ath11k-Fix-mon-status-ring-rx-tlv-processing.patch new file mode 100644 index 00000000000000..b3e908d553dfe8 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0115-ath11k-Fix-mon-status-ring-rx-tlv-processing.patch @@ -0,0 +1,65 @@ +From 09f16f7390f302937409738d6cb6ce99b265f455 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Mon, 22 Nov 2021 13:13:58 +0200 +Subject: [PATCH] ath11k: Fix mon status ring rx tlv processing + +In HE monitor capture, HAL_TLV_STATUS_PPDU_DONE is received +on processing multiple skb. Do not clear the ppdu_info +till the HAL_TLV_STATUS_PPDU_DONE is received. + +This fixes below warning and packet drops in monitor mode. + "Rate marked as an HE rate but data is invalid: MCS: 6, NSS: 0" + WARNING: at + PC is at ieee80211_rx_napi+0x624/0x840 [mac80211] + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01693-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1637249433-10316-1-git-send-email-akolli@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -3061,10 +3061,10 @@ int ath11k_dp_rx_process_mon_status(stru + if (!num_buffs_reaped) + goto exit; + +- while ((skb = __skb_dequeue(&skb_list))) { +- memset(&ppdu_info, 0, sizeof(ppdu_info)); +- ppdu_info.peer_id = HAL_INVALID_PEERID; ++ memset(&ppdu_info, 0, sizeof(ppdu_info)); ++ ppdu_info.peer_id = HAL_INVALID_PEERID; + ++ while ((skb = __skb_dequeue(&skb_list))) { + if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { + log_type = ATH11K_PKTLOG_TYPE_LITE_RX; + rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; +@@ -3092,10 +3092,7 @@ int ath11k_dp_rx_process_mon_status(stru + ath11k_dbg(ab, ATH11K_DBG_DATA, + "failed to find the peer with peer_id %d\n", + ppdu_info.peer_id); +- spin_unlock_bh(&ab->base_lock); +- rcu_read_unlock(); +- dev_kfree_skb_any(skb); +- continue; ++ goto next_skb; + } + + arsta = (struct ath11k_sta *)peer->sta->drv_priv; +@@ -3104,10 +3101,13 @@ int ath11k_dp_rx_process_mon_status(stru + if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + ++next_skb: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + + dev_kfree_skb_any(skb); ++ memset(&ppdu_info, 0, sizeof(ppdu_info)); ++ ppdu_info.peer_id = HAL_INVALID_PEERID; + } + exit: + return num_buffs_reaped; diff --git a/package/kernel/mac80211/patches/ath11k/0116-Revert-ath11k-add-read-variant-from-SMBIOS-for-downl.patch b/package/kernel/mac80211/patches/ath11k/0116-Revert-ath11k-add-read-variant-from-SMBIOS-for-downl.patch new file mode 100644 index 00000000000000..f4241394d2c937 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0116-Revert-ath11k-add-read-variant-from-SMBIOS-for-downl.patch @@ -0,0 +1,168 @@ +From 72f4124347724e3b8aa434f6bc4a2cd69f7bb336 Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Wed, 24 Nov 2021 11:43:16 +0200 +Subject: [PATCH] Revert "ath11k: add read variant from SMBIOS for download + board data" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit 46e46db313a2bf3c48cac4eb8bdb613b762f301b. Mark reported +that it breaks QCA6390 hw2.0 on Dell XPS 13 9310: + +[    5.537034] ath11k_pci 0000:72:00.0: chip_id 0x0 chip_family 0xb board_id 0xff soc_id 0xffffffff +[    5.537038] ath11k_pci 0000:72:00.0: fw_version 0x101c06cc fw_build_timestamp 2020-06-24 19:50 fw_build_id +[    5.537236] ath11k_pci 0000:72:00.0: failed to fetch board data for bus=pci,qmi-chip-id=0,qmi-board-id=255,variant=DE_1901 from ath11k/QCA6390/hw2.0/board-2.bin +[    5.537255] ath11k_pci 0000:72:00.0: failed to fetch board-2.bin or board.bin from QCA6390/hw2.0 +[    5.537257] ath11k_pci 0000:72:00.0: qmi failed to fetch board file: -2 +[    5.537258] ath11k_pci 0000:72:00.0: failed to load board data file: -2 + +So we need to back to the drawing board and implement it so that backwards +compatiblity is not broken. + +Reported-by: Mark Herbert +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211124094316.9096-1-kvalo@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 79 -------------------------- + drivers/net/wireless/ath/ath11k/core.h | 13 ----- + drivers/net/wireless/ath/ath11k/qmi.c | 4 -- + 3 files changed, 96 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -8,9 +8,6 @@ + #include + #include + #include +-#include +-#include +- + #include "core.h" + #include "dp_tx.h" + #include "dp_rx.h" +@@ -387,82 +384,6 @@ int ath11k_core_resume(struct ath11k_bas + } + EXPORT_SYMBOL(ath11k_core_resume); + +-static void ath11k_core_check_bdfext(const struct dmi_header *hdr, void *data) +-{ +- struct ath11k_base *ab = data; +- const char *bdf_ext; +- const char *magic = ATH11K_SMBIOS_BDF_EXT_MAGIC; +- u8 bdf_enabled; +- int i; +- size_t len; +- +- if (ab->qmi.target.bdf_ext[0] != '\0') +- return; +- +- if (hdr->type != ATH11K_SMBIOS_BDF_EXT_TYPE) +- return; +- +- if (hdr->length != ATH11K_SMBIOS_BDF_EXT_LENGTH) { +- ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "wrong smbios bdf ext type length (%d).\n", +- hdr->length); +- return; +- } +- +- bdf_enabled = *((u8 *)hdr + ATH11K_SMBIOS_BDF_EXT_OFFSET); +- if (!bdf_enabled) { +- ath11k_dbg(ab, ATH11K_DBG_BOOT, "bdf variant name not found.\n"); +- return; +- } +- +- /* Only one string exists (per spec) */ +- bdf_ext = (char *)hdr + hdr->length; +- +- if (memcmp(bdf_ext, magic, strlen(magic)) != 0) { +- ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "bdf variant magic does not match.\n"); +- return; +- } +- +- len = strlen(bdf_ext); +- for (i = 0; i < len; i++) { +- if (!isascii(bdf_ext[i]) || !isprint(bdf_ext[i])) { +- ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "bdf variant name contains non ascii chars.\n"); +- return; +- } +- } +- +- /* Copy extension name without magic prefix */ +- if (strscpy(ab->qmi.target.bdf_ext, bdf_ext + strlen(magic), +- sizeof(ab->qmi.target.bdf_ext)) < 0) { +- ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "bdf variant string is longer than the buffer can accommodate (variant: %s)\n", +- bdf_ext); +- return; +- } +- +- ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "found and validated bdf variant smbios_type 0x%x bdf %s\n", +- ATH11K_SMBIOS_BDF_EXT_TYPE, bdf_ext); +-} +- +-int ath11k_core_check_smbios(struct ath11k_base *ab) +-{ +- int ret; +- +- ab->qmi.target.bdf_ext[0] = '\0'; +- +- ret = dmi_walk(ath11k_core_check_bdfext, ab); +- if (ret) +- return ret; +- +- if (ab->qmi.target.bdf_ext[0] == '\0') +- return -ENODATA; +- +- return 0; +-} +- + int ath11k_core_check_dt(struct ath11k_base *ab) + { + size_t max_len = sizeof(ab->qmi.target.bdf_ext); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -938,18 +938,6 @@ struct ath11k_fw_stats_bcn { + u32 tx_bcn_outage_cnt; + }; + +-/* SMBIOS type containing Board Data File Name Extension */ +-#define ATH11K_SMBIOS_BDF_EXT_TYPE 0xF8 +- +-/* SMBIOS type structure length (excluding strings-set) */ +-#define ATH11K_SMBIOS_BDF_EXT_LENGTH 0x9 +- +-/* Offset pointing to Board Data File Name Extension */ +-#define ATH11K_SMBIOS_BDF_EXT_OFFSET 0x8 +- +-/* The magic used by QCA spec */ +-#define ATH11K_SMBIOS_BDF_EXT_MAGIC "BDF_" +- + extern const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq8074[]; + extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq8074[]; + extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq6018[]; +@@ -971,7 +959,6 @@ int ath11k_core_fetch_bdf(struct ath11k_ + struct ath11k_board_data *bd); + void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); + int ath11k_core_check_dt(struct ath11k_base *ath11k); +-int ath11k_core_check_smbios(struct ath11k_base *ab); + + void ath11k_core_halt(struct ath11k *ar); + int ath11k_core_resume(struct ath11k_base *ab); +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1991,10 +1991,6 @@ static int ath11k_qmi_request_target_cap + ab->qmi.target.fw_build_timestamp, + ab->qmi.target.fw_build_id); + +- r = ath11k_core_check_smbios(ab); +- if (r) +- ath11k_dbg(ab, ATH11K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); +- + r = ath11k_core_check_dt(ab); + if (r) + ath11k_dbg(ab, ATH11K_DBG_QMI, "DT bdf variant name not set.\n"); diff --git a/package/kernel/mac80211/patches/ath11k/0117-ath11k-Fix-spelling-mistake-detetction-detection.patch b/package/kernel/mac80211/patches/ath11k/0117-ath11k-Fix-spelling-mistake-detetction-detection.patch new file mode 100644 index 00000000000000..751c2b1c925138 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0117-ath11k-Fix-spelling-mistake-detetction-detection.patch @@ -0,0 +1,25 @@ +From c27506cc7733261bafd7a97e7990407eef433d32 Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Tue, 23 Nov 2021 09:04:31 +0000 +Subject: [PATCH] ath11k: Fix spelling mistake "detetction" -> "detection" + +There is a spelling mistake in an ath11k_warn message. Fix it. + +Signed-off-by: Colin Ian King +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211123090431.165103-1-colin.i.king@gmail.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -3511,7 +3511,7 @@ ath11k_wmi_obss_color_collision_event(st + case WMI_BSS_COLOR_FREE_SLOT_AVAILABLE: + break; + default: +- ath11k_warn(ab, "received unknown obss color collision detetction event\n"); ++ ath11k_warn(ab, "received unknown obss color collision detection event\n"); + } + + exit: diff --git a/package/kernel/mac80211/patches/ath11k/0121-ath11k-add-support-for-WCN6855-hw2.1.patch b/package/kernel/mac80211/patches/ath11k/0121-ath11k-add-support-for-WCN6855-hw2.1.patch new file mode 100644 index 00000000000000..79d8b9140e2a45 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0121-ath11k-add-support-for-WCN6855-hw2.1.patch @@ -0,0 +1,152 @@ +From d1147a316b53df9cb0152e415ec41dcb6ea62c1c Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 29 Nov 2021 10:56:12 +0800 +Subject: [PATCH] ath11k: add support for WCN6855 hw2.1 + +Ath11k fails to probe WCN6855 hw2.1 chip: + +[ 6.983821] ath11k_pci 0000:06:00.0: enabling device (0000 -> 0002) +[ 6.983841] ath11k_pci 0000:06:00.0: Unsupported WCN6855 SOC hardware version: 18 17 + +This is caused by the wrong bit mask setting of hardware major version: +for QCA6390/QCN6855, it should be BIT8-11, not BIT8-16, so change the +definition to GENMASK(11, 8). + +Also, add a separate entry for WCN6855 hw2.1 in ath11k_hw_params. + +Please note that currently WCN6855 hw2.1 shares the same firmwares +as hw2.0, so users of this chip need to create a symlink as below: + + ln -s hw2.0 hw2.1 + +Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Fixes: 18ac1665e785 ("ath11k: pci: check TCSR_SOC_HW_VERSION") +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211129025613.21594-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 53 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mhi.c | 1 + + drivers/net/wireless/ath/ath11k/pci.c | 16 +++++++- + 4 files changed, 69 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -301,6 +301,59 @@ static const struct ath11k_hw_params ath + .alloc_cacheable_memory = false, + .wakeup_mhi = true, + }, ++ { ++ .name = "wcn6855 hw2.1", ++ .hw_rev = ATH11K_HW_WCN6855_HW21, ++ .fw = { ++ .dir = "WCN6855/hw2.1", ++ .board_size = 256 * 1024, ++ .cal_offset = 128 * 1024, ++ }, ++ .max_radios = 3, ++ .bdf_addr = 0x4B0C0000, ++ .hw_ops = &wcn6855_ops, ++ .ring_mask = &ath11k_hw_ring_mask_qca6390, ++ .internal_sleep_clock = true, ++ .regs = &wcn6855_regs, ++ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, ++ .host_ce_config = ath11k_host_ce_config_qca6390, ++ .ce_count = 9, ++ .target_ce_config = ath11k_target_ce_config_wlan_qca6390, ++ .target_ce_count = 9, ++ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, ++ .svc_to_ce_map_len = 14, ++ .single_pdev_only = true, ++ .rxdma1_enable = false, ++ .num_rxmda_per_pdev = 2, ++ .rx_mac_buf_ring = true, ++ .vdev_start_delay = true, ++ .htt_peer_map_v2 = false, ++ ++ .spectral = { ++ .fft_sz = 0, ++ .fft_pad_sz = 0, ++ .summary_pad_sz = 0, ++ .fft_hdr_len = 0, ++ .max_fft_bins = 0, ++ }, ++ ++ .interface_modes = BIT(NL80211_IFTYPE_STATION) | ++ BIT(NL80211_IFTYPE_AP), ++ .supports_monitor = false, ++ .supports_shadow_regs = true, ++ .idle_ps = true, ++ .supports_sta_ps = true, ++ .cold_boot_calib = false, ++ .supports_suspend = true, ++ .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), ++ .fix_l1ss = false, ++ .credit_flow = true, ++ .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, ++ .hal_params = &ath11k_hw_hal_params_qca6390, ++ .supports_dynamic_smps_6ghz = false, ++ .alloc_cacheable_memory = false, ++ .wakeup_mhi = true, ++ }, + }; + + int ath11k_core_suspend(struct ath11k_base *ab) +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -117,6 +117,7 @@ enum ath11k_hw_rev { + ATH11K_HW_IPQ6018_HW10, + ATH11K_HW_QCN9074_HW10, + ATH11K_HW_WCN6855_HW20, ++ ATH11K_HW_WCN6855_HW21, + }; + + enum ath11k_firmware_mode { +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -368,6 +368,7 @@ int ath11k_mhi_register(struct ath11k_pc + break; + case ATH11K_HW_QCA6390_HW20: + case ATH11K_HW_WCN6855_HW20: ++ case ATH11K_HW_WCN6855_HW21: + ath11k_mhi_config = &ath11k_mhi_config_qca6390; + break; + default: +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -26,7 +26,7 @@ + #define WINDOW_RANGE_MASK GENMASK(18, 0) + + #define TCSR_SOC_HW_VERSION 0x0224 +-#define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(16, 8) ++#define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8) + #define TCSR_SOC_HW_VERSION_MINOR_MASK GENMASK(7, 0) + + /* BAR0 + 4k is always accessible, and no +@@ -1415,9 +1415,21 @@ static int ath11k_pci_probe(struct pci_d + &soc_hw_version_minor); + switch (soc_hw_version_major) { + case 2: +- ab->hw_rev = ATH11K_HW_WCN6855_HW20; ++ switch (soc_hw_version_minor) { ++ case 0x00: ++ case 0x01: ++ ab->hw_rev = ATH11K_HW_WCN6855_HW20; ++ break; ++ case 0x10: ++ case 0x11: ++ ab->hw_rev = ATH11K_HW_WCN6855_HW21; ++ break; ++ default: ++ goto unsupported_wcn6855_soc; ++ } + break; + default: ++unsupported_wcn6855_soc: + dev_err(&pdev->dev, "Unsupported WCN6855 SOC hardware version: %d %d\n", + soc_hw_version_major, soc_hw_version_minor); + ret = -EOPNOTSUPP; diff --git a/package/kernel/mac80211/patches/ath11k/0122-ath11k-Fix-QMI-file-type-enum-value.patch b/package/kernel/mac80211/patches/ath11k/0122-ath11k-Fix-QMI-file-type-enum-value.patch new file mode 100644 index 00000000000000..3d1b5bfd062712 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0122-ath11k-Fix-QMI-file-type-enum-value.patch @@ -0,0 +1,32 @@ +From 18ae1ab04525507ae5528245a6df004cacd0d39a Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Mon, 29 Nov 2021 16:15:54 +0530 +Subject: [PATCH] ath11k: Fix QMI file type enum value + +bdf_type for caldata in QMI_WLANFW_BDF_DOWNLOAD_REQ_V01 is wrongly +sent as 1. But, expected bdf_type value for caldata and EEPROM is 2 and 3 +respectively. It leads to firmware crash. Fix ath11k_qmi_file_type enum +values. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Fixes: 336e7b53c82f ("ath11k: clean up BDF download functions") +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638182754-18408-1-git-send-email-quic_seevalam@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -41,7 +41,7 @@ struct ath11k_base; + + enum ath11k_qmi_file_type { + ATH11K_QMI_FILE_TYPE_BDF_GOLDEN, +- ATH11K_QMI_FILE_TYPE_CALDATA, ++ ATH11K_QMI_FILE_TYPE_CALDATA = 2, + ATH11K_QMI_FILE_TYPE_EEPROM, + ATH11K_QMI_MAX_FILE_TYPE, + }; diff --git a/package/kernel/mac80211/patches/ath11k/0123-ath11k-change-to-use-dynamic-memory-for-channel-list.patch b/package/kernel/mac80211/patches/ath11k/0123-ath11k-change-to-use-dynamic-memory-for-channel-list.patch new file mode 100644 index 00000000000000..fa508e8a7b25f4 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0123-ath11k-change-to-use-dynamic-memory-for-channel-list.patch @@ -0,0 +1,66 @@ +From cea7f78d85f3f6ba05f43754600426b0e84abbbd Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 29 Nov 2021 06:09:39 -0500 +Subject: [PATCH] ath11k: change to use dynamic memory for channel list of scan + +Currently there are about 60 channels for 6 GHz, then the size of +chan_list in struct scan_req_params which is 40 is not enough to +fill all the channel list of 6 GHz. + +Use dynamic memory to save the channel list of scan. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211129110939.15711-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 10 ++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 3 +-- + 2 files changed, 11 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -3504,6 +3504,14 @@ static int ath11k_mac_op_hw_scan(struct + + if (req->n_channels) { + arg.num_chan = req->n_channels; ++ arg.chan_list = kcalloc(arg.num_chan, sizeof(*arg.chan_list), ++ GFP_KERNEL); ++ ++ if (!arg.chan_list) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ + for (i = 0; i < arg.num_chan; i++) + arg.chan_list[i] = req->channels[i]->center_freq; + } +@@ -3522,6 +3530,8 @@ static int ath11k_mac_op_hw_scan(struct + ATH11K_MAC_SCAN_TIMEOUT_MSECS)); + + exit: ++ kfree(arg.chan_list); ++ + if (req->ie_len) + kfree(arg.extraie.ptr); + +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3082,7 +3082,6 @@ enum scan_dwelltime_adaptive_mode { + + #define WLAN_SCAN_MAX_NUM_SSID 10 + #define WLAN_SCAN_MAX_NUM_BSSID 10 +-#define WLAN_SCAN_MAX_NUM_CHANNELS 40 + + #define WLAN_SSID_MAX_LEN 32 + +@@ -3303,7 +3302,7 @@ struct scan_req_params { + u32 num_bssid; + u32 num_ssids; + u32 n_probes; +- u32 chan_list[WLAN_SCAN_MAX_NUM_CHANNELS]; ++ u32 *chan_list; + u32 notify_scan_events; + struct wlan_ssid ssid[WLAN_SCAN_MAX_NUM_SSID]; + struct wmi_mac_addr bssid_list[WLAN_SCAN_MAX_NUM_BSSID]; diff --git a/package/kernel/mac80211/patches/ath11k/0125-ath11k-add-configure-country-code-for-QCA6390-and-WC.patch b/package/kernel/mac80211/patches/ath11k/0125-ath11k-add-configure-country-code-for-QCA6390-and-WC.patch new file mode 100644 index 00000000000000..3d8a60ca58b216 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0125-ath11k-add-configure-country-code-for-QCA6390-and-WC.patch @@ -0,0 +1,92 @@ +From 0b05ddad8e4bd56bda42b9dc491c1b127720f063 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 7 Dec 2021 17:23:36 +0200 +Subject: [PATCH] ath11k: add configure country code for QCA6390 and WCN6855 + +Add handler to send WMI_SET_CURRENT_COUNTRY_CMDID to firmware which +is used for QCA6390 and WCN6855. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211201071745.17746-3-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 36 +++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 12 +++++++++ + 2 files changed, 48 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -2798,6 +2798,42 @@ out: + return ret; + } + ++int ath11k_wmi_send_set_current_country_cmd(struct ath11k *ar, ++ struct wmi_set_current_country_params *param) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_set_current_country_cmd *cmd; ++ struct sk_buff *skb; ++ int ret; ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_set_current_country_cmd *)skb->data; ++ cmd->tlv_header = ++ FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SET_CURRENT_COUNTRY_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->pdev_id = ar->pdev->pdev_id; ++ memcpy(&cmd->new_alpha2, ¶m->alpha2, 3); ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SET_CURRENT_COUNTRY_CMDID); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "set current country pdev id %d alpha2 %c%c\n", ++ ar->pdev->pdev_id, ++ param->alpha2[0], ++ param->alpha2[1]); ++ ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to send WMI_SET_CURRENT_COUNTRY_CMDID: %d\n", ret); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; ++} ++ + int + ath11k_wmi_send_thermal_mitigation_param_cmd(struct ath11k *ar, + struct thermal_mitigation_params *param) +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3769,6 +3769,16 @@ struct stats_request_params { + u32 pdev_id; + }; + ++struct wmi_set_current_country_params { ++ u8 alpha2[3]; ++}; ++ ++struct wmi_set_current_country_cmd { ++ u32 tlv_header; ++ u32 pdev_id; ++ u32 new_alpha2; ++} __packed; ++ + enum set_init_cc_type { + WMI_COUNTRY_INFO_TYPE_ALPHA, + WMI_COUNTRY_INFO_TYPE_COUNTRY_CODE, +@@ -5432,6 +5442,8 @@ int ath11k_wmi_delba_send(struct ath11k + u32 tid, u32 initiator, u32 reason); + int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, + u32 vdev_id, u32 bcn_ctrl_op); ++int ath11k_wmi_send_set_current_country_cmd(struct ath11k *ar, ++ struct wmi_set_current_country_params *param); + int + ath11k_wmi_send_init_country_cmd(struct ath11k *ar, + struct wmi_init_country_params init_cc_param); diff --git a/package/kernel/mac80211/patches/ath11k/0126-ath11k-add-11d-scan-offload-support.patch b/package/kernel/mac80211/patches/ath11k/0126-ath11k-add-11d-scan-offload-support.patch new file mode 100644 index 00000000000000..72b82111dbee83 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0126-ath11k-add-11d-scan-offload-support.patch @@ -0,0 +1,600 @@ +From 9dcf6808b253a72b2c90eed179863bf5fab7d68c Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 7 Dec 2021 17:23:36 +0200 +Subject: [PATCH] ath11k: add 11d scan offload support + +Add handler for WMI_11D_NEW_COUNTRY_EVENTID, WMI_11D_SCAN_START_CMDID, +WMI_11D_SCAN_STOP_CMDID. + +After vdev create for STATION, send WMI_11D_SCAN_START_CMDID to firmware +and wait firmware complete it, the scan from mac80211 also need to wait +the 11d scan finished, and send WMI_11D_SCAN_STOP_CMDID to firmware +before vdev delete for STATION. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01230-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211201071745.17746-4-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 31 +++++ + drivers/net/wireless/ath/ath11k/core.h | 9 ++ + drivers/net/wireless/ath/ath11k/mac.c | 163 ++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/mac.h | 7 ++ + drivers/net/wireless/ath/ath11k/reg.c | 15 +++ + drivers/net/wireless/ath/ath11k/wmi.c | 109 +++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 27 ++++ + 7 files changed, 360 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1096,6 +1096,7 @@ void ath11k_core_halt(struct ath11k *ar) + ath11k_mac_peer_cleanup_all(ar); + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); ++ cancel_work_sync(&ab->update_11d_work); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); + synchronize_rcu(); +@@ -1103,6 +1104,34 @@ void ath11k_core_halt(struct ath11k *ar) + idr_init(&ar->txmgmt_idr); + } + ++static void ath11k_update_11d(struct work_struct *work) ++{ ++ struct ath11k_base *ab = container_of(work, struct ath11k_base, update_11d_work); ++ struct ath11k *ar; ++ struct ath11k_pdev *pdev; ++ struct wmi_set_current_country_params set_current_param = {}; ++ int ret, i; ++ ++ spin_lock_bh(&ab->base_lock); ++ memcpy(&set_current_param.alpha2, &ab->new_alpha2, 2); ++ spin_unlock_bh(&ab->base_lock); ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, "update 11d new cc %c%c\n", ++ set_current_param.alpha2[0], ++ set_current_param.alpha2[1]); ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ ++ ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "pdev id %d failed set current country code: %d\n", ++ i, ret); ++ } ++} ++ + static void ath11k_core_restart(struct work_struct *work) + { + struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); +@@ -1272,12 +1301,14 @@ struct ath11k_base *ath11k_core_alloc(st + + mutex_init(&ab->core_lock); + spin_lock_init(&ab->base_lock); ++ mutex_init(&ab->vdev_id_11d_lock); + + INIT_LIST_HEAD(&ab->peers); + init_waitqueue_head(&ab->peer_mapping_wq); + init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); + init_waitqueue_head(&ab->qmi.cold_boot_waitq); + INIT_WORK(&ab->restart_work, ath11k_core_restart); ++ INIT_WORK(&ab->update_11d_work, ath11k_update_11d); + timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); + init_completion(&ab->htc_suspend); + init_completion(&ab->wow.wakeup_completed); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -589,6 +589,11 @@ struct ath11k { + #endif + bool dfs_block_radar_events; + struct ath11k_thermal thermal; ++ u32 vdev_id_11d_scan; ++ struct completion finish_11d_scan; ++ struct completion finish_11d_ch_list; ++ bool pending_11d; ++ bool regdom_set_by_user; + }; + + struct ath11k_band_cap { +@@ -762,6 +767,8 @@ struct ath11k_base { + struct completion driver_recovery; + struct workqueue_struct *workqueue; + struct work_struct restart_work; ++ struct work_struct update_11d_work; ++ u8 new_alpha2[3]; + struct { + /* protected by data_lock */ + u32 fw_crash_counter; +@@ -771,6 +778,8 @@ struct ath11k_base { + struct ath11k_dbring_cap *db_caps; + u32 num_db_cap; + ++ /* To synchronize 11d scan vdev id */ ++ struct mutex vdev_id_11d_lock; + struct timer_list mon_reap_timer; + + struct completion htc_suspend; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2681,6 +2681,8 @@ static void ath11k_bss_assoc(struct ieee + if (ret) + ath11k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n", + arvif->vdev_id, ret); ++ ++ ath11k_mac_11d_scan_stop_all(ar->ab); + } + + static void ath11k_bss_disassoc(struct ieee80211_hw *hw, +@@ -3410,6 +3412,7 @@ static int ath11k_start_scan(struct ath1 + struct scan_req_params *arg) + { + int ret; ++ unsigned long timeout = 1 * HZ; + + lockdep_assert_held(&ar->conf_mutex); + +@@ -3420,7 +3423,14 @@ static int ath11k_start_scan(struct ath1 + if (ret) + return ret; + +- ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ); ++ if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) { ++ timeout = 5 * HZ; ++ ++ if (ar->supports_6ghz) ++ timeout += 5 * HZ; ++ } ++ ++ ret = wait_for_completion_timeout(&ar->scan.started, timeout); + if (ret == 0) { + ret = ath11k_scan_stop(ar); + if (ret) +@@ -3477,6 +3487,26 @@ static int ath11k_mac_op_hw_scan(struct + if (ret) + goto exit; + ++ /* Currently the pending_11d=true only happened 1 time while ++ * wlan interface up in ath11k_mac_11d_scan_start(), it is called by ++ * ath11k_mac_op_add_interface(), after wlan interface up, ++ * pending_11d=false always. ++ * If remove below wait, it always happened scan fail and lead connect ++ * fail while wlan interface up, because it has a 11d scan which is running ++ * in firmware, and lead this scan failed. ++ */ ++ if (ar->pending_11d) { ++ long time_left; ++ unsigned long timeout = 5 * HZ; ++ ++ if (ar->supports_6ghz) ++ timeout += 5 * HZ; ++ ++ time_left = wait_for_completion_timeout(&ar->finish_11d_ch_list, timeout); ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac wait 11d channel list time left %ld\n", time_left); ++ } ++ + memset(&arg, 0, sizeof(arg)); + ath11k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; +@@ -5638,6 +5668,7 @@ static void ath11k_mac_op_stop(struct ie + + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); ++ cancel_work_sync(&ar->ab->update_11d_work); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { +@@ -5791,6 +5822,122 @@ static void ath11k_mac_op_update_vif_off + } + } + ++static bool ath11k_mac_vif_ap_active_any(struct ath11k_base *ab) ++{ ++ struct ath11k *ar; ++ struct ath11k_pdev *pdev; ++ struct ath11k_vif *arvif; ++ int i; ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_AP) ++ return true; ++ } ++ } ++ return false; ++} ++ ++void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id, bool wait) ++{ ++ struct wmi_11d_scan_start_params param; ++ int ret; ++ ++ mutex_lock(&ar->ab->vdev_id_11d_lock); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev id for 11d scan %d\n", ++ ar->vdev_id_11d_scan); ++ ++ if (ar->regdom_set_by_user) ++ goto fin; ++ ++ if (ar->vdev_id_11d_scan != ATH11K_11D_INVALID_VDEV_ID) ++ goto fin; ++ ++ if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) ++ goto fin; ++ ++ if (ath11k_mac_vif_ap_active_any(ar->ab)) ++ goto fin; ++ ++ param.vdev_id = vdev_id; ++ param.start_interval_msec = 0; ++ param.scan_period_msec = ATH11K_SCAN_11D_INTERVAL; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac start 11d scan\n"); ++ ++ if (wait) ++ reinit_completion(&ar->finish_11d_scan); ++ ++ ret = ath11k_wmi_send_11d_scan_start_cmd(ar, ¶m); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start 11d scan vdev %d ret: %d\n", ++ vdev_id, ret); ++ } else { ++ ar->vdev_id_11d_scan = vdev_id; ++ if (wait) { ++ ar->pending_11d = true; ++ ret = wait_for_completion_timeout(&ar->finish_11d_scan, ++ 5 * HZ); ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac 11d scan left time %d\n", ret); ++ ++ if (!ret) ++ ar->pending_11d = false; ++ } ++ } ++ ++fin: ++ mutex_unlock(&ar->ab->vdev_id_11d_lock); ++} ++ ++void ath11k_mac_11d_scan_stop(struct ath11k *ar) ++{ ++ int ret; ++ u32 vdev_id; ++ ++ if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) ++ return; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac stop 11d scan\n"); ++ ++ mutex_lock(&ar->ab->vdev_id_11d_lock); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac stop 11d vdev id %d\n", ++ ar->vdev_id_11d_scan); ++ ++ if (ar->vdev_id_11d_scan != ATH11K_11D_INVALID_VDEV_ID) { ++ vdev_id = ar->vdev_id_11d_scan; ++ ++ ret = ath11k_wmi_send_11d_scan_stop_cmd(ar, vdev_id); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "failed to stopt 11d scan vdev %d ret: %d\n", ++ vdev_id, ret); ++ else ++ ar->vdev_id_11d_scan = ATH11K_11D_INVALID_VDEV_ID; ++ } ++ mutex_unlock(&ar->ab->vdev_id_11d_lock); ++} ++ ++void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab) ++{ ++ struct ath11k *ar; ++ struct ath11k_pdev *pdev; ++ int i; ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "mac stop soc 11d scan\n"); ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ ++ ath11k_mac_11d_scan_stop(ar); ++ } ++} ++ + static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) + { +@@ -5924,6 +6071,8 @@ static int ath11k_mac_op_add_interface(s + arvif->vdev_id, ret); + goto err_peer_del; + } ++ ++ ath11k_mac_11d_scan_stop_all(ar->ab); + break; + case WMI_VDEV_TYPE_STA: + param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY; +@@ -5963,6 +6112,9 @@ static int ath11k_mac_op_add_interface(s + arvif->vdev_id, ret); + goto err_peer_del; + } ++ ++ ath11k_mac_11d_scan_start(ar, arvif->vdev_id, true); ++ + break; + case WMI_VDEV_TYPE_MONITOR: + set_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); +@@ -6064,6 +6216,9 @@ static void ath11k_mac_op_remove_interfa + ath11k_dbg(ab, ATH11K_DBG_MAC, "mac remove interface (vdev %d)\n", + arvif->vdev_id); + ++ if (arvif->vdev_type == WMI_VDEV_TYPE_STA) ++ ath11k_mac_11d_scan_stop(ar); ++ + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath11k_peer_delete(ar, arvif->vdev_id, vif->addr); + if (ret) +@@ -6782,6 +6937,9 @@ ath11k_mac_op_unassign_vif_chanctx(struc + ret); + } + ++ if (arvif->vdev_type == WMI_VDEV_TYPE_STA) ++ ath11k_mac_11d_scan_start(ar, arvif->vdev_id, false); ++ + mutex_unlock(&ar->conf_mutex); + } + +@@ -8183,6 +8341,9 @@ int ath11k_mac_allocate(struct ath11k_ba + + ar->monitor_vdev_id = -1; + clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ++ ar->vdev_id_11d_scan = ATH11K_11D_INVALID_VDEV_ID; ++ init_completion(&ar->finish_11d_scan); ++ init_completion(&ar->finish_11d_ch_list); + } + + return 0; +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -127,6 +127,13 @@ struct ath11k_generic_iter { + + extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default; + ++#define ATH11K_SCAN_11D_INTERVAL 600000 ++#define ATH11K_11D_INVALID_VDEV_ID 0xFFFF ++ ++void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id, bool wait); ++void ath11k_mac_11d_scan_stop(struct ath11k *ar); ++void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab); ++ + void ath11k_mac_destroy(struct ath11k_base *ab); + void ath11k_mac_unregister(struct ath11k_base *ab); + int ath11k_mac_register(struct ath11k_base *ab); +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -86,6 +86,9 @@ ath11k_reg_notifier(struct wiphy *wiphy, + if (ret) + ath11k_warn(ar->ab, + "INIT Country code set to fw failed : %d\n", ret); ++ ++ ath11k_mac_11d_scan_stop(ar); ++ ar->regdom_set_by_user = true; + } + + int ath11k_reg_update_chan_list(struct ath11k *ar) +@@ -179,6 +182,11 @@ int ath11k_reg_update_chan_list(struct a + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); + kfree(params); + ++ if (ar->pending_11d) { ++ complete(&ar->finish_11d_ch_list); ++ ar->pending_11d = false; ++ } ++ + return ret; + } + +@@ -244,8 +252,15 @@ int ath11k_regd_update(struct ath11k *ar + goto err; + } + ++ if (ar->pending_11d) ++ complete(&ar->finish_11d_scan); ++ + rtnl_lock(); + wiphy_lock(ar->hw->wiphy); ++ ++ if (ar->pending_11d) ++ reinit_completion(&ar->finish_11d_ch_list); ++ + ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy); + wiphy_unlock(ar->hw->wiphy); + rtnl_unlock(); +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -130,6 +130,8 @@ static const struct wmi_tlv_policy wmi_t + .min_len = sizeof(struct wmi_vdev_delete_resp_event) }, + [WMI_TAG_OBSS_COLOR_COLLISION_EVT] = { + .min_len = sizeof(struct wmi_obss_color_collision_event) }, ++ [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { ++ .min_len = sizeof(struct wmi_11d_new_cc_ev) }, + }; + + #define PRIMAP(_hw_mode_) \ +@@ -2898,6 +2900,75 @@ ath11k_wmi_send_thermal_mitigation_param + return ret; + } + ++int ath11k_wmi_send_11d_scan_start_cmd(struct ath11k *ar, ++ struct wmi_11d_scan_start_params *param) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_11d_scan_start_cmd *cmd; ++ struct sk_buff *skb; ++ int ret; ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_11d_scan_start_cmd *)skb->data; ++ cmd->tlv_header = ++ FIELD_PREP(WMI_TLV_TAG, WMI_TAG_11D_SCAN_START_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = param->vdev_id; ++ cmd->scan_period_msec = param->scan_period_msec; ++ cmd->start_interval_msec = param->start_interval_msec; ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_START_CMDID); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "send 11d scan start vdev id %d period %d ms internal %d ms\n", ++ cmd->vdev_id, ++ cmd->scan_period_msec, ++ cmd->start_interval_msec); ++ ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to send WMI_11D_SCAN_START_CMDID: %d\n", ret); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; ++} ++ ++int ath11k_wmi_send_11d_scan_stop_cmd(struct ath11k *ar, u32 vdev_id) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_11d_scan_stop_cmd *cmd; ++ struct sk_buff *skb; ++ int ret; ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_11d_scan_stop_cmd *)skb->data; ++ cmd->tlv_header = ++ FIELD_PREP(WMI_TLV_TAG, WMI_TAG_11D_SCAN_STOP_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = vdev_id; ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_STOP_CMDID); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "send 11d scan stop vdev id %d\n", ++ cmd->vdev_id); ++ ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to send WMI_11D_SCAN_STOP_CMDID: %d\n", ret); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; ++} ++ + int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter) + { + struct ath11k_pdev_wmi *wmi = ar->wmi; +@@ -5938,6 +6009,41 @@ static void ath11k_wmi_op_ep_tx_credits( + wake_up(&ab->wmi_ab.tx_credits_wq); + } + ++static int ath11k_reg_11d_new_cc_event(struct ath11k_base *ab, struct sk_buff *skb) ++{ ++ const struct wmi_11d_new_cc_ev *ev; ++ const void **tb; ++ int ret; ++ ++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); ++ if (IS_ERR(tb)) { ++ ret = PTR_ERR(tb); ++ ath11k_warn(ab, "failed to parse tlv: %d\n", ret); ++ return ret; ++ } ++ ++ ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT]; ++ if (!ev) { ++ kfree(tb); ++ ath11k_warn(ab, "failed to fetch 11d new cc ev"); ++ return -EPROTO; ++ } ++ ++ spin_lock_bh(&ab->base_lock); ++ memcpy(&ab->new_alpha2, &ev->new_alpha2, 2); ++ spin_unlock_bh(&ab->base_lock); ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi 11d new cc %c%c\n", ++ ab->new_alpha2[0], ++ ab->new_alpha2[1]); ++ ++ kfree(tb); ++ ++ queue_work(ab->workqueue, &ab->update_11d_work); ++ ++ return 0; ++} ++ + static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) + { +@@ -7333,6 +7439,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_WOW_WAKEUP_HOST_EVENTID: + ath11k_wmi_event_wow_wakeup_host(ab, skb); + break; ++ case WMI_11D_NEW_COUNTRY_EVENTID: ++ ath11k_reg_11d_new_cc_event(ab, skb); ++ break; + /* TODO: Add remaining events */ + default: + ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id); +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3812,6 +3812,28 @@ struct wmi_init_country_cmd { + } cc_info; + } __packed; + ++struct wmi_11d_scan_start_params { ++ u32 vdev_id; ++ u32 scan_period_msec; ++ u32 start_interval_msec; ++}; ++ ++struct wmi_11d_scan_start_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 scan_period_msec; ++ u32 start_interval_msec; ++} __packed; ++ ++struct wmi_11d_scan_stop_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++} __packed; ++ ++struct wmi_11d_new_cc_ev { ++ u32 new_alpha2; ++} __packed; ++ + #define THERMAL_LEVELS 1 + struct tt_level_config { + u32 tmplwm; +@@ -5447,6 +5469,11 @@ int ath11k_wmi_send_set_current_country_ + int + ath11k_wmi_send_init_country_cmd(struct ath11k *ar, + struct wmi_init_country_params init_cc_param); ++ ++int ath11k_wmi_send_11d_scan_start_cmd(struct ath11k *ar, ++ struct wmi_11d_scan_start_params *param); ++int ath11k_wmi_send_11d_scan_stop_cmd(struct ath11k *ar, u32 vdev_id); ++ + int + ath11k_wmi_send_thermal_mitigation_param_cmd(struct ath11k *ar, + struct thermal_mitigation_params *param); diff --git a/package/kernel/mac80211/patches/ath11k/0127-ath11k-add-wait-operation-for-tx-management-packets-.patch b/package/kernel/mac80211/patches/ath11k/0127-ath11k-add-wait-operation-for-tx-management-packets-.patch new file mode 100644 index 00000000000000..dffa7acba13ba7 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0127-ath11k-add-wait-operation-for-tx-management-packets-.patch @@ -0,0 +1,230 @@ +From dddaa64d0af37275314a656bd8f8e941799e2d61 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Thu, 2 Dec 2021 01:37:05 -0500 +Subject: [PATCH] ath11k: add wait operation for tx management packets for + flush from mac80211 + +In ath11k, tx of management packet is doing in a work queue. Sometimes +the workqueue does not finish tx immediately, then it lead after the next +step of vdev delete finished, it start to send the management packet to +firmware and lead firmware crash. + +ieee80211_set_disassoc() have logic of ieee80211_flush_queues() after +it send_deauth_disassoc() to ath11k, its purpose is make sure the +deauth was actually sent, so it need to change ath11k to match the +purpose of mac80211. + +To address these issue wait for tx mgmt as well as tx data packets. + +dmesg log of connect/disconnect to AP: +[ 307.522226] wls1: authenticate with 62:66:e4:e9:6a:a9 +[ 307.586565] wls1: send auth to 62:66:e4:e9:6a:a9 (try 1/3) +[ 307.586581] ath11k_pci 0000:05:00.0: mac tx mgmt frame, buf id 0 +[ 307.586922] ath11k_pci 0000:05:00.0: mac tx mgmt frame, vdev_id 0 +[ 307.590179] ath11k_pci 0000:05:00.0: wmi mgmt tx comp pending 0 desc id 0 +[ 307.590181] ath11k_pci 0000:05:00.0: mgmt tx compl ev pdev_id 2, desc_id 0, status 0 +[ 307.598699] wls1: authenticated +[ 307.599483] wls1: associate with 62:66:e4:e9:6a:a9 (try 1/3) +[ 307.599506] ath11k_pci 0000:05:00.0: mac tx mgmt frame, buf id 0 +[ 307.599519] ath11k_pci 0000:05:00.0: mac tx mgmt frame, vdev_id 0 +[ 307.603059] ath11k_pci 0000:05:00.0: wmi mgmt tx comp pending 0 desc id 0 +[ 307.603063] ath11k_pci 0000:05:00.0: mgmt tx compl ev pdev_id 2, desc_id 0, status 0 +[ 307.637105] wls1: associated +[ 317.365239] wls1: deauthenticating from 62:66:e4:e9:6a:a9 by local choice (Reason: 3=DEAUTH_LEAVING) +[ 317.368104] ath11k_pci 0000:05:00.0: mac tx mgmt frame, buf id 0 +[ 317.372622] ath11k_pci 0000:05:00.0: mac tx mgmt frame, vdev_id 0 +[ 317.378320] ath11k_pci 0000:05:00.0: wmi mgmt tx comp pending 0 desc id 0 +[ 317.378330] ath11k_pci 0000:05:00.0: mgmt tx compl ev pdev_id 2, desc_id 0, status 0 +[ 317.378359] ath11k_pci 0000:05:00.0: mac mgmt tx flush mgmt pending 0 +[ 317.421066] ath11k_pci 0000:05:00.0: mac mgmt tx flush mgmt pending 0 +[ 317.421427] ath11k_pci 0000:05:00.0: mac remove interface (vdev 0) + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01230-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211202063705.14321-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 51 +++++++++++++++++++++----- + drivers/net/wireless/ath/ath11k/wmi.c | 12 +++++- + 4 files changed, 55 insertions(+), 10 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1165,6 +1165,7 @@ static void ath11k_core_restart(struct w + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); ++ wake_up(&ar->txmgmt_empty_waitq); + } + + wake_up(&ab->wmi_ab.tx_credits_wq); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -551,6 +551,7 @@ struct ath11k { + /* protects txmgmt_idr data */ + spinlock_t txmgmt_idr_lock; + atomic_t num_pending_mgmt_tx; ++ wait_queue_head_t txmgmt_empty_waitq; + + /* cycle count is reported twice for each visited channel during scan. + * access protected by data_lock +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5268,6 +5268,21 @@ static int __ath11k_set_antenna(struct a + return 0; + } + ++static void ath11k_mgmt_over_wmi_tx_drop(struct ath11k *ar, struct sk_buff *skb) ++{ ++ int num_mgmt; ++ ++ ieee80211_free_txskb(ar->hw, skb); ++ ++ num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); ++ ++ if (num_mgmt < 0) ++ WARN_ON_ONCE(1); ++ ++ if (!num_mgmt) ++ wake_up(&ar->txmgmt_empty_waitq); ++} ++ + static void ath11k_mac_tx_mgmt_free(struct ath11k *ar, int buf_id) + { + struct sk_buff *msdu; +@@ -5286,7 +5301,7 @@ static void ath11k_mac_tx_mgmt_free(stru + info = IEEE80211_SKB_CB(msdu); + memset(&info->status, 0, sizeof(info->status)); + +- ieee80211_free_txskb(ar->hw, msdu); ++ ath11k_mgmt_over_wmi_tx_drop(ar, msdu); + } + + int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx) +@@ -5326,6 +5341,10 @@ static int ath11k_mac_mgmt_tx_wmi(struct + buf_id = idr_alloc(&ar->txmgmt_idr, skb, 0, + ATH11K_TX_MGMT_NUM_PENDING_MAX, GFP_ATOMIC); + spin_unlock_bh(&ar->txmgmt_idr_lock); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac tx mgmt frame, buf id %d\n", buf_id); ++ + if (buf_id < 0) + return -ENOSPC; + +@@ -5372,7 +5391,7 @@ static void ath11k_mgmt_over_wmi_tx_purg + struct sk_buff *skb; + + while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL) +- ieee80211_free_txskb(ar->hw, skb); ++ ath11k_mgmt_over_wmi_tx_drop(ar, skb); + } + + static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work) +@@ -5387,29 +5406,29 @@ static void ath11k_mgmt_over_wmi_tx_work + skb_cb = ATH11K_SKB_CB(skb); + if (!skb_cb->vif) { + ath11k_warn(ar->ab, "no vif found for mgmt frame\n"); +- ieee80211_free_txskb(ar->hw, skb); ++ ath11k_mgmt_over_wmi_tx_drop(ar, skb); + continue; + } + + arvif = ath11k_vif_to_arvif(skb_cb->vif); + if (ar->allocated_vdev_map & (1LL << arvif->vdev_id) && + arvif->is_started) { +- atomic_inc(&ar->num_pending_mgmt_tx); + ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb); + if (ret) { +- if (atomic_dec_if_positive(&ar->num_pending_mgmt_tx) < 0) +- WARN_ON_ONCE(1); +- + ath11k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n", + arvif->vdev_id, ret); +- ieee80211_free_txskb(ar->hw, skb); ++ ath11k_mgmt_over_wmi_tx_drop(ar, skb); ++ } else { ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac tx mgmt frame, vdev_id %d\n", ++ arvif->vdev_id); + } + } else { + ath11k_warn(ar->ab, + "dropping mgmt frame for vdev %d, is_started %d\n", + arvif->vdev_id, + arvif->is_started); +- ieee80211_free_txskb(ar->hw, skb); ++ ath11k_mgmt_over_wmi_tx_drop(ar, skb); + } + } + } +@@ -5440,6 +5459,7 @@ static int ath11k_mac_mgmt_tx(struct ath + } + + skb_queue_tail(q, skb); ++ atomic_inc(&ar->num_pending_mgmt_tx); + ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + + return 0; +@@ -7026,6 +7046,17 @@ static void ath11k_mac_op_flush(struct i + ATH11K_FLUSH_TIMEOUT); + if (time_left == 0) + ath11k_warn(ar->ab, "failed to flush transmit queue %ld\n", time_left); ++ ++ time_left = wait_event_timeout(ar->txmgmt_empty_waitq, ++ (atomic_read(&ar->num_pending_mgmt_tx) == 0), ++ ATH11K_FLUSH_TIMEOUT); ++ if (time_left == 0) ++ ath11k_warn(ar->ab, "failed to flush mgmt transmit queue %ld\n", ++ time_left); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac mgmt tx flush mgmt pending %d\n", ++ atomic_read(&ar->num_pending_mgmt_tx)); + } + + static int +@@ -8264,6 +8295,8 @@ int ath11k_mac_register(struct ath11k_ba + ret = __ath11k_mac_register(ar); + if (ret) + goto err_cleanup; ++ ++ init_waitqueue_head(&ar->txmgmt_empty_waitq); + } + + return 0; +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -4980,6 +4980,7 @@ static int wmi_process_mgmt_tx_comp(stru + struct sk_buff *msdu; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; ++ int num_mgmt; + + spin_lock_bh(&ar->txmgmt_idr_lock); + msdu = idr_find(&ar->txmgmt_idr, desc_id); +@@ -5003,10 +5004,19 @@ static int wmi_process_mgmt_tx_comp(stru + + ieee80211_tx_status_irqsafe(ar->hw, msdu); + ++ num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); ++ + /* WARN when we received this event without doing any mgmt tx */ +- if (atomic_dec_if_positive(&ar->num_pending_mgmt_tx) < 0) ++ if (num_mgmt < 0) + WARN_ON_ONCE(1); + ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi mgmt tx comp pending %d desc id %d\n", ++ num_mgmt, desc_id); ++ ++ if (!num_mgmt) ++ wake_up(&ar->txmgmt_empty_waitq); ++ + return 0; + } + diff --git a/package/kernel/mac80211/patches/ath11k/0129-ath11k-enable-IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS-.patch b/package/kernel/mac80211/patches/ath11k/0129-ath11k-enable-IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS-.patch new file mode 100644 index 00000000000000..bb1336216fe36b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0129-ath11k-enable-IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS-.patch @@ -0,0 +1,42 @@ +From 9f6da09a5f6ab94bca58395af56b883b3a79663a Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 8 Dec 2021 10:43:59 +0200 +Subject: [PATCH] ath11k: enable IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS for + WCN6855 + +Currently mac80211 will send 3 scan request for each scan of WCN6855, +they are 2.4 GHz/5 GHz/6 GHz band scan. Firmware of WCN6855 will +cache the RNR IE(Reduced Neighbor Report element) which exist in the +beacon of 2.4 GHz/5 GHz of the AP which is co-located with 6 GHz, +and then use the cache to scan in 6 GHz band scan if the 6 GHz scan +is in the same scan with the 2.4 GHz/5 GHz band, this will helpful to +search more AP of 6 GHz. Also it will decrease the time cost of scan +because firmware will use dual-band scan for the 2.4 GHz/5 GHz, it +means the 2.4 GHz and 5 GHz scans are doing simultaneously. + +Set the flag IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS for WCN6855 since +it supports 2.4 GHz/5 GHz/6 GHz and it is single pdev which means +all the 2.4 GHz/5 GHz/6 GHz exist in the same wiphy/ieee80211_hw. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Tested-by: Sven Eckelmann +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211129101309.2931-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8122,6 +8122,9 @@ static int __ath11k_mac_register(struct + + ar->hw->wiphy->interface_modes = ab->hw_params.interface_modes; + ++ if (ab->hw_params.single_pdev_only && ar->supports_6ghz) ++ ieee80211_hw_set(ar->hw, SINGLE_SCAN_ON_ALL_BANDS); ++ + ieee80211_hw_set(ar->hw, SIGNAL_DBM); + ieee80211_hw_set(ar->hw, SUPPORTS_PS); + ieee80211_hw_set(ar->hw, SUPPORTS_DYNAMIC_PS); diff --git a/package/kernel/mac80211/patches/ath11k/0130-ath11k-Add-htt-cmd-to-enable-full-monitor-mode.patch b/package/kernel/mac80211/patches/ath11k/0130-ath11k-Add-htt-cmd-to-enable-full-monitor-mode.patch new file mode 100644 index 00000000000000..ccf8b9c1fbedfa --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0130-ath11k-Add-htt-cmd-to-enable-full-monitor-mode.patch @@ -0,0 +1,189 @@ +From 5c1f74d24d92ce62264508df4c8acabb3127cd83 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Wed, 8 Dec 2021 10:44:00 +0200 +Subject: [PATCH] ath11k: Add htt cmd to enable full monitor mode + +A new hw_param full_monitor_mode is added to enable full +monitor support for QCN9074. +HTT_H2T_MSG_TYPE_RX_FULL_MONITOR_MODE cmd is sent to FW +to enable the full monitor mode. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638881695-22155-2-git-send-email-akolli@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 5 +++ + drivers/net/wireless/ath/ath11k/dp.h | 28 +++++++++++++++ + drivers/net/wireless/ath/ath11k/dp_tx.c | 48 +++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/dp_tx.h | 2 ++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 5 files changed, 84 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -74,6 +74,7 @@ static const struct ath11k_hw_params ath + 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, +@@ -128,6 +129,7 @@ static const struct ath11k_hw_params ath + 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, +@@ -181,6 +183,7 @@ static const struct ath11k_hw_params ath + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, ++ .full_monitor_mode = false, + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, +@@ -234,6 +237,7 @@ static const struct ath11k_hw_params ath + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = true, ++ .full_monitor_mode = true, + .supports_shadow_regs = false, + .idle_ps = false, + .supports_sta_ps = false, +@@ -287,6 +291,7 @@ static const struct ath11k_hw_params ath + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, ++ .full_monitor_mode = false, + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -292,6 +292,7 @@ enum htt_h2t_msg_type { + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc, + HTT_H2T_MSG_TYPE_EXT_STATS_CFG = 0x10, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG = 0x11, ++ HTT_H2T_MSG_TYPE_RX_FULL_MONITOR_MODE = 0x17, + }; + + #define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) +@@ -957,6 +958,33 @@ struct htt_rx_ring_tlv_filter { + u32 pkt_filter_flags3; /* DATA */ + }; + ++#define HTT_RX_FULL_MON_MODE_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) ++#define HTT_RX_FULL_MON_MODE_CFG_CMD_INFO0_PDEV_ID GENMASK(15, 8) ++ ++#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_ENABLE BIT(0) ++#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_ZERO_MPDUS_END BIT(1) ++#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_NON_ZERO_MPDUS_END BIT(2) ++#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_RELEASE_RING GENMASK(10, 3) ++ ++/** ++ * Enumeration for full monitor mode destination ring select ++ * 0 - REO destination ring select ++ * 1 - FW destination ring select ++ * 2 - SW destination ring select ++ * 3 - Release destination ring select ++ */ ++enum htt_rx_full_mon_release_ring { ++ HTT_RX_MON_RING_REO, ++ HTT_RX_MON_RING_FW, ++ HTT_RX_MON_RING_SW, ++ HTT_RX_MON_RING_RELEASE, ++}; ++ ++struct htt_rx_full_monitor_mode_cfg_cmd { ++ u32 info0; ++ u32 cfg; ++} __packed; ++ + /* HTT message target->host */ + + enum htt_t2h_msg_type { +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -1033,6 +1033,15 @@ int ath11k_dp_tx_htt_monitor_mode_ring_c + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + int ret = 0, ring_id = 0, i; + ++ if (ab->hw_params.full_monitor_mode) { ++ ret = ath11k_dp_tx_htt_rx_full_mon_setup(ab, ++ dp->mac_id, !reset); ++ if (ret < 0) { ++ ath11k_err(ab, "failed to setup full monitor %d\n", ret); ++ return ret; ++ } ++ } ++ + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; + + if (!reset) { +@@ -1098,3 +1107,42 @@ int ath11k_dp_tx_htt_monitor_mode_ring_c + + return ret; + } ++ ++int ath11k_dp_tx_htt_rx_full_mon_setup(struct ath11k_base *ab, int mac_id, ++ bool config) ++{ ++ struct htt_rx_full_monitor_mode_cfg_cmd *cmd; ++ struct sk_buff *skb; ++ int ret, len = sizeof(*cmd); ++ ++ skb = ath11k_htc_alloc_skb(ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ skb_put(skb, len); ++ cmd = (struct htt_rx_full_monitor_mode_cfg_cmd *)skb->data; ++ memset(cmd, 0, sizeof(*cmd)); ++ cmd->info0 = FIELD_PREP(HTT_RX_FULL_MON_MODE_CFG_CMD_INFO0_MSG_TYPE, ++ HTT_H2T_MSG_TYPE_RX_FULL_MONITOR_MODE); ++ ++ cmd->info0 |= FIELD_PREP(HTT_RX_FULL_MON_MODE_CFG_CMD_INFO0_PDEV_ID, mac_id); ++ ++ cmd->cfg = HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_ENABLE | ++ FIELD_PREP(HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_RELEASE_RING, ++ HTT_RX_MON_RING_SW); ++ if (config) { ++ cmd->cfg |= HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_ZERO_MPDUS_END | ++ HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_NON_ZERO_MPDUS_END; ++ } ++ ++ ret = ath11k_htc_send(&ab->htc, ab->dp.eid, skb); ++ if (ret) ++ goto err_free; ++ ++ return 0; ++ ++err_free: ++ dev_kfree_skb_any(skb); ++ ++ return ret; ++} +--- a/drivers/net/wireless/ath/ath11k/dp_tx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.h +@@ -37,4 +37,6 @@ int ath11k_dp_tx_htt_rx_filter_setup(str + int rx_buf_size, + struct htt_rx_ring_tlv_filter *tlv_filter); + ++int ath11k_dp_tx_htt_rx_full_mon_setup(struct ath11k_base *ab, int mac_id, ++ bool config); + #endif +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -168,6 +168,7 @@ struct ath11k_hw_params { + + u16 interface_modes; + bool supports_monitor; ++ bool full_monitor_mode; + bool supports_shadow_regs; + bool idle_ps; + bool supports_sta_ps; diff --git a/package/kernel/mac80211/patches/ath11k/0131-ath11k-add-software-monitor-ring-descriptor-for-full.patch b/package/kernel/mac80211/patches/ath11k/0131-ath11k-add-software-monitor-ring-descriptor-for-full.patch new file mode 100644 index 00000000000000..6db287bf232d4c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0131-ath11k-add-software-monitor-ring-descriptor-for-full.patch @@ -0,0 +1,131 @@ +From 88ee00d130f744854cdd91ad76a888d9e66996d1 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Wed, 8 Dec 2021 10:44:00 +0200 +Subject: [PATCH] ath11k: add software monitor ring descriptor for full monitor + +In full monitor mode, monitor destination ring is read in +software monitor ring descriptor format instead of +reo_entrance_ring format. Add new sw_mon_ring descriptor. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638881695-22155-3-git-send-email-akolli@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/hal_desc.h | 19 ++++++++++ + drivers/net/wireless/ath/ath11k/hal_rx.c | 44 ++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/hal_rx.h | 17 +++++++++ + 3 files changed, 80 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/hal_desc.h ++++ b/drivers/net/wireless/ath/ath11k/hal_desc.h +@@ -858,6 +858,25 @@ struct hal_reo_entrance_ring { + * this ring has looped around the ring. + */ + ++#define HAL_SW_MON_RING_INFO0_RXDMA_PUSH_REASON GENMASK(1, 0) ++#define HAL_SW_MON_RING_INFO0_RXDMA_ERROR_CODE GENMASK(6, 2) ++#define HAL_SW_MON_RING_INFO0_MPDU_FRAG_NUMBER GENMASK(10, 7) ++#define HAL_SW_MON_RING_INFO0_FRAMELESS_BAR BIT(11) ++#define HAL_SW_MON_RING_INFO0_STATUS_BUF_CNT GENMASK(15, 12) ++#define HAL_SW_MON_RING_INFO0_END_OF_PPDU BIT(16) ++ ++#define HAL_SW_MON_RING_INFO1_PHY_PPDU_ID GENMASK(15, 0) ++#define HAL_SW_MON_RING_INFO1_RING_ID GENMASK(27, 20) ++#define HAL_SW_MON_RING_INFO1_LOOPING_COUNT GENMASK(31, 28) ++ ++struct hal_sw_monitor_ring { ++ struct ath11k_buffer_addr buf_addr_info; ++ struct rx_mpdu_desc rx_mpdu_info; ++ struct ath11k_buffer_addr status_buf_addr_info; ++ u32 info0; ++ u32 info1; ++} __packed; ++ + #define HAL_REO_CMD_HDR_INFO0_CMD_NUMBER GENMASK(15, 0) + #define HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED BIT(16) + +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1186,3 +1186,47 @@ void ath11k_hal_rx_reo_ent_buf_paddr_get + + *pp_buf_addr = (void *)buf_addr_info; + } ++ ++void ++ath11k_hal_rx_sw_mon_ring_buf_paddr_get(void *rx_desc, ++ struct hal_sw_mon_ring_entries *sw_mon_entries) ++{ ++ struct hal_sw_monitor_ring *sw_mon_ring = rx_desc; ++ struct ath11k_buffer_addr *buf_addr_info; ++ struct ath11k_buffer_addr *status_buf_addr_info; ++ struct rx_mpdu_desc *rx_mpdu_desc_info_details; ++ ++ rx_mpdu_desc_info_details = &sw_mon_ring->rx_mpdu_info; ++ ++ sw_mon_entries->msdu_cnt = FIELD_GET(RX_MPDU_DESC_INFO0_MSDU_COUNT, ++ rx_mpdu_desc_info_details->info0); ++ ++ buf_addr_info = &sw_mon_ring->buf_addr_info; ++ status_buf_addr_info = &sw_mon_ring->status_buf_addr_info; ++ ++ sw_mon_entries->mon_dst_paddr = (((u64)FIELD_GET(BUFFER_ADDR_INFO1_ADDR, ++ buf_addr_info->info1)) << 32) | ++ FIELD_GET(BUFFER_ADDR_INFO0_ADDR, ++ buf_addr_info->info0); ++ ++ sw_mon_entries->mon_status_paddr = ++ (((u64)FIELD_GET(BUFFER_ADDR_INFO1_ADDR, ++ status_buf_addr_info->info1)) << 32) | ++ FIELD_GET(BUFFER_ADDR_INFO0_ADDR, ++ status_buf_addr_info->info0); ++ ++ sw_mon_entries->mon_dst_sw_cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, ++ buf_addr_info->info1); ++ ++ sw_mon_entries->mon_status_sw_cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, ++ status_buf_addr_info->info1); ++ ++ sw_mon_entries->status_buf_count = FIELD_GET(HAL_SW_MON_RING_INFO0_STATUS_BUF_CNT, ++ sw_mon_ring->info0); ++ ++ sw_mon_entries->dst_buf_addr_info = buf_addr_info; ++ sw_mon_entries->status_buf_addr_info = status_buf_addr_info; ++ ++ sw_mon_entries->ppdu_id = ++ FIELD_GET(HAL_SW_MON_RING_INFO1_PHY_PPDU_ID, sw_mon_ring->info1); ++} +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -77,6 +77,20 @@ enum hal_rx_mon_status { + HAL_RX_MON_STATUS_BUF_DONE, + }; + ++struct hal_sw_mon_ring_entries { ++ dma_addr_t mon_dst_paddr; ++ dma_addr_t mon_status_paddr; ++ u32 mon_dst_sw_cookie; ++ u32 mon_status_sw_cookie; ++ void *dst_buf_addr_info; ++ void *status_buf_addr_info; ++ u16 ppdu_id; ++ u8 status_buf_count; ++ u8 msdu_cnt; ++ bool end_of_ppdu; ++ bool drop_ppdu; ++}; ++ + struct hal_rx_mon_ppdu_info { + u32 ppdu_id; + u32 ppdu_ts; +@@ -331,6 +345,9 @@ void ath11k_hal_rx_reo_ent_buf_paddr_get + dma_addr_t *paddr, u32 *sw_cookie, + void **pp_buf_addr_info, u8 *rbm, + u32 *msdu_cnt); ++void ++ath11k_hal_rx_sw_mon_ring_buf_paddr_get(void *rx_desc, ++ struct hal_sw_mon_ring_entries *sw_mon_ent); + enum hal_rx_mon_status + ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, diff --git a/package/kernel/mac80211/patches/ath11k/0132-ath11k-Process-full-monitor-mode-rx-support.patch b/package/kernel/mac80211/patches/ath11k/0132-ath11k-Process-full-monitor-mode-rx-support.patch new file mode 100644 index 00000000000000..884a795646cd30 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0132-ath11k-Process-full-monitor-mode-rx-support.patch @@ -0,0 +1,568 @@ +From 7e2ea2e947046834a450295dfd328adb70a9f864 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Wed, 8 Dec 2021 10:44:00 +0200 +Subject: [PATCH] ath11k: Process full monitor mode rx support + +In full monitor mode, monitor destination ring is read before monitor +status ring. mon_dst_ring has ppdu id, reap till the end of PPDU. Add +all the MPDUs to list. Start processing the status ring, if PPDU id in +status ring is lagging behind, reap the status ring, once the PPDU ID +matches, deliver the MSDU to upper layer. If status PPDU id leading, +reap the mon_dst_ring. + +The advantage with full monitor mode is hardware has status buffers +available for all the MPDUs in mon_dst_ring, which makes it possible +to deliver more frames to be seen on sniffer. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638881695-22155-4-git-send-email-akolli@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp.c | 1 + + drivers/net/wireless/ath/ath11k/dp.h | 26 +- + drivers/net/wireless/ath/ath11k/dp_rx.c | 409 +++++++++++++++++++++++- + 3 files changed, 433 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -1051,6 +1051,7 @@ int ath11k_dp_alloc(struct ath11k_base * + + INIT_LIST_HEAD(&dp->reo_cmd_list); + INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list); ++ INIT_LIST_HEAD(&dp->dp_full_mon_mpdu_list); + spin_lock_init(&dp->reo_cmd_lock); + + dp->reo_cmd_cache_flush_count = 0; +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -89,6 +89,19 @@ struct dp_tx_ring { + int tx_status_tail; + }; + ++enum dp_mon_status_buf_state { ++ /* PPDU id matches in dst ring and status ring */ ++ DP_MON_STATUS_MATCH, ++ /* status ring dma is not done */ ++ DP_MON_STATUS_NO_DMA, ++ /* status ring is lagging, reap status ring */ ++ DP_MON_STATUS_LAG, ++ /* status ring is leading, reap dst ring and drop */ ++ DP_MON_STATUS_LEAD, ++ /* replinish monitor status ring */ ++ DP_MON_STATUS_REPLINISH, ++}; ++ + struct ath11k_pdev_mon_stats { + u32 status_ppdu_state; + u32 status_ppdu_start; +@@ -104,6 +117,12 @@ struct ath11k_pdev_mon_stats { + u32 dup_mon_buf_cnt; + }; + ++struct dp_full_mon_mpdu { ++ struct list_head list; ++ struct sk_buff *head; ++ struct sk_buff *tail; ++}; ++ + struct dp_link_desc_bank { + void *vaddr_unaligned; + void *vaddr; +@@ -135,7 +154,11 @@ struct ath11k_mon_data { + u32 mon_last_buf_cookie; + u64 mon_last_linkdesc_paddr; + u16 chan_noise_floor; +- ++ bool hold_mon_dst_ring; ++ enum dp_mon_status_buf_state buf_state; ++ dma_addr_t mon_status_paddr; ++ struct dp_full_mon_mpdu *mon_mpdu; ++ struct hal_sw_mon_ring_entries sw_mon_entries; + struct ath11k_pdev_mon_stats rx_mon_stats; + /* lock for monitor data */ + spinlock_t mon_lock; +@@ -245,6 +268,7 @@ struct ath11k_dp { + struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX]; + struct list_head reo_cmd_list; + struct list_head reo_cmd_cache_flush_list; ++ struct list_head dp_full_mon_mpdu_list; + u32 reo_cmd_cache_flush_count; + /** + * protects access to below fields, +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2942,6 +2942,43 @@ fail_desc_get: + return req_entries - num_remain; + } + ++#define ATH11K_DP_RX_FULL_MON_PPDU_ID_WRAP 32535 ++ ++static void ++ath11k_dp_rx_mon_update_status_buf_state(struct ath11k_mon_data *pmon, ++ struct hal_tlv_hdr *tlv) ++{ ++ struct hal_rx_ppdu_start *ppdu_start; ++ u16 ppdu_id_diff, ppdu_id, tlv_len; ++ u8 *ptr; ++ ++ /* PPDU id is part of second tlv, move ptr to second tlv */ ++ tlv_len = FIELD_GET(HAL_TLV_HDR_LEN, tlv->tl); ++ ptr = (u8 *)tlv; ++ ptr += sizeof(*tlv) + tlv_len; ++ tlv = (struct hal_tlv_hdr *)ptr; ++ ++ if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != HAL_RX_PPDU_START) ++ return; ++ ++ ptr += sizeof(*tlv); ++ ppdu_start = (struct hal_rx_ppdu_start *)ptr; ++ ppdu_id = FIELD_GET(HAL_RX_PPDU_START_INFO0_PPDU_ID, ++ __le32_to_cpu(ppdu_start->info0)); ++ ++ if (pmon->sw_mon_entries.ppdu_id < ppdu_id) { ++ pmon->buf_state = DP_MON_STATUS_LEAD; ++ ppdu_id_diff = ppdu_id - pmon->sw_mon_entries.ppdu_id; ++ if (ppdu_id_diff > ATH11K_DP_RX_FULL_MON_PPDU_ID_WRAP) ++ pmon->buf_state = DP_MON_STATUS_LAG; ++ } else if (pmon->sw_mon_entries.ppdu_id > ppdu_id) { ++ pmon->buf_state = DP_MON_STATUS_LAG; ++ ppdu_id_diff = pmon->sw_mon_entries.ppdu_id - ppdu_id; ++ if (ppdu_id_diff > ATH11K_DP_RX_FULL_MON_PPDU_ID_WRAP) ++ pmon->buf_state = DP_MON_STATUS_LEAD; ++ } ++} ++ + static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, + int *budget, struct sk_buff_head *skb_list) + { +@@ -2949,6 +2986,7 @@ static int ath11k_dp_rx_reap_mon_status_ + const struct ath11k_hw_hal_params *hal_params; + struct ath11k_pdev_dp *dp; + struct dp_rxdma_ring *rx_ring; ++ struct ath11k_mon_data *pmon; + struct hal_srng *srng; + void *rx_mon_status_desc; + struct sk_buff *skb; +@@ -2962,6 +3000,7 @@ static int ath11k_dp_rx_reap_mon_status_ + + ar = ab->pdevs[ath11k_hw_mac_id_to_pdev_id(&ab->hw_params, mac_id)].ar; + dp = &ar->dp; ++ pmon = &dp->mon_data; + srng_id = ath11k_hw_mac_id_to_srng_id(&ab->hw_params, mac_id); + rx_ring = &dp->rx_mon_status_refill_ring[srng_id]; + +@@ -2974,8 +3013,10 @@ static int ath11k_dp_rx_reap_mon_status_ + *budget -= 1; + rx_mon_status_desc = + ath11k_hal_srng_src_peek(ab, srng); +- if (!rx_mon_status_desc) ++ if (!rx_mon_status_desc) { ++ pmon->buf_state = DP_MON_STATUS_REPLINISH; + break; ++ } + + ath11k_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr, + &cookie, &rbm); +@@ -2988,6 +3029,7 @@ static int ath11k_dp_rx_reap_mon_status_ + ath11k_warn(ab, "rx monitor status with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); ++ pmon->buf_state = DP_MON_STATUS_REPLINISH; + goto move_next; + } + +@@ -3007,10 +3049,18 @@ static int ath11k_dp_rx_reap_mon_status_ + FIELD_GET(HAL_TLV_HDR_TAG, + tlv->tl)); + dev_kfree_skb_any(skb); ++ pmon->buf_state = DP_MON_STATUS_NO_DMA; + goto move_next; + } + ++ if (ab->hw_params.full_monitor_mode) { ++ ath11k_dp_rx_mon_update_status_buf_state(pmon, tlv); ++ if (paddr == pmon->mon_status_paddr) ++ pmon->buf_state = DP_MON_STATUS_MATCH; ++ } + __skb_queue_tail(skb_list, skb); ++ } else { ++ pmon->buf_state = DP_MON_STATUS_REPLINISH; + } + move_next: + skb = ath11k_dp_rx_alloc_mon_status_buf(ab, rx_ring, +@@ -5098,6 +5148,357 @@ static void ath11k_dp_rx_mon_status_proc + } + } + ++static u32 ++ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, ++ void *ring_entry, struct sk_buff **head_msdu, ++ struct sk_buff **tail_msdu, ++ struct hal_sw_mon_ring_entries *sw_mon_entries) ++{ ++ struct ath11k_pdev_dp *dp = &ar->dp; ++ struct ath11k_mon_data *pmon = &dp->mon_data; ++ struct dp_rxdma_ring *rx_ring = &dp->rxdma_mon_buf_ring; ++ struct sk_buff *msdu = NULL, *last = NULL; ++ struct hal_sw_monitor_ring *sw_desc = ring_entry; ++ struct hal_rx_msdu_list msdu_list; ++ struct hal_rx_desc *rx_desc; ++ struct ath11k_skb_rxcb *rxcb; ++ void *rx_msdu_link_desc; ++ void *p_buf_addr_info, *p_last_buf_addr_info; ++ int buf_id, i = 0; ++ u32 rx_buf_size, rx_pkt_offset, l2_hdr_offset; ++ u32 rx_bufs_used = 0, msdu_cnt = 0; ++ u32 total_len = 0, frag_len = 0, sw_cookie; ++ u16 num_msdus = 0; ++ u8 rxdma_err, rbm; ++ bool is_frag, is_first_msdu; ++ bool drop_mpdu = false; ++ ++ ath11k_hal_rx_sw_mon_ring_buf_paddr_get(ring_entry, sw_mon_entries); ++ ++ sw_cookie = sw_mon_entries->mon_dst_sw_cookie; ++ sw_mon_entries->end_of_ppdu = false; ++ sw_mon_entries->drop_ppdu = false; ++ p_last_buf_addr_info = sw_mon_entries->dst_buf_addr_info; ++ msdu_cnt = sw_mon_entries->msdu_cnt; ++ ++ sw_mon_entries->end_of_ppdu = ++ FIELD_GET(HAL_SW_MON_RING_INFO0_END_OF_PPDU, sw_desc->info0); ++ if (sw_mon_entries->end_of_ppdu) ++ return rx_bufs_used; ++ ++ if (FIELD_GET(HAL_SW_MON_RING_INFO0_RXDMA_PUSH_REASON, ++ sw_desc->info0) == ++ HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { ++ rxdma_err = ++ FIELD_GET(HAL_SW_MON_RING_INFO0_RXDMA_ERROR_CODE, ++ sw_desc->info0); ++ if (rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR || ++ rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR || ++ rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR) { ++ pmon->rx_mon_stats.dest_mpdu_drop++; ++ drop_mpdu = true; ++ } ++ } ++ ++ is_frag = false; ++ is_first_msdu = true; ++ ++ do { ++ rx_msdu_link_desc = ++ (u8 *)pmon->link_desc_banks[sw_cookie].vaddr + ++ (sw_mon_entries->mon_dst_paddr - ++ pmon->link_desc_banks[sw_cookie].paddr); ++ ++ ath11k_hal_rx_msdu_list_get(ar, rx_msdu_link_desc, &msdu_list, ++ &num_msdus); ++ ++ for (i = 0; i < num_msdus; i++) { ++ buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, ++ msdu_list.sw_cookie[i]); ++ ++ spin_lock_bh(&rx_ring->idr_lock); ++ msdu = idr_find(&rx_ring->bufs_idr, buf_id); ++ if (!msdu) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_DATA, ++ "full mon msdu_pop: invalid buf_id %d\n", ++ buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); ++ break; ++ } ++ idr_remove(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); ++ ++ rxcb = ATH11K_SKB_RXCB(msdu); ++ if (!rxcb->unmapped) { ++ dma_unmap_single(ar->ab->dev, rxcb->paddr, ++ msdu->len + ++ skb_tailroom(msdu), ++ DMA_FROM_DEVICE); ++ rxcb->unmapped = 1; ++ } ++ if (drop_mpdu) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_DATA, ++ "full mon: i %d drop msdu %p *ppdu_id %x\n", ++ i, msdu, sw_mon_entries->ppdu_id); ++ dev_kfree_skb_any(msdu); ++ msdu_cnt--; ++ goto next_msdu; ++ } ++ ++ rx_desc = (struct hal_rx_desc *)msdu->data; ++ ++ rx_pkt_offset = sizeof(struct hal_rx_desc); ++ l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, rx_desc); ++ ++ if (is_first_msdu) { ++ if (!ath11k_dp_rxdesc_mpdu_valid(ar->ab, rx_desc)) { ++ drop_mpdu = true; ++ dev_kfree_skb_any(msdu); ++ msdu = NULL; ++ goto next_msdu; ++ } ++ is_first_msdu = false; ++ } ++ ++ ath11k_dp_mon_get_buf_len(&msdu_list.msdu_info[i], ++ &is_frag, &total_len, ++ &frag_len, &msdu_cnt); ++ ++ rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len; ++ ++ ath11k_dp_pkt_set_pktlen(msdu, rx_buf_size); ++ ++ if (!(*head_msdu)) ++ *head_msdu = msdu; ++ else if (last) ++ last->next = msdu; ++ ++ last = msdu; ++next_msdu: ++ rx_bufs_used++; ++ } ++ ++ ath11k_dp_rx_mon_next_link_desc_get(rx_msdu_link_desc, ++ &sw_mon_entries->mon_dst_paddr, ++ &sw_mon_entries->mon_dst_sw_cookie, ++ &rbm, ++ &p_buf_addr_info); ++ ++ if (ath11k_dp_rx_monitor_link_desc_return(ar, ++ p_last_buf_addr_info, ++ dp->mac_id)) ++ ath11k_dbg(ar->ab, ATH11K_DBG_DATA, ++ "full mon: dp_rx_monitor_link_desc_return failed\n"); ++ ++ p_last_buf_addr_info = p_buf_addr_info; ++ ++ } while (sw_mon_entries->mon_dst_paddr && msdu_cnt); ++ ++ if (last) ++ last->next = NULL; ++ ++ *tail_msdu = msdu; ++ ++ return rx_bufs_used; ++} ++ ++static int ath11k_dp_rx_full_mon_prepare_mpdu(struct ath11k_dp *dp, ++ struct dp_full_mon_mpdu *mon_mpdu, ++ struct sk_buff *head, ++ struct sk_buff *tail) ++{ ++ mon_mpdu = kzalloc(sizeof(*mon_mpdu), GFP_ATOMIC); ++ if (!mon_mpdu) ++ return -ENOMEM; ++ ++ list_add_tail(&mon_mpdu->list, &dp->dp_full_mon_mpdu_list); ++ mon_mpdu->head = head; ++ mon_mpdu->tail = tail; ++ ++ return 0; ++} ++ ++static void ath11k_dp_rx_full_mon_drop_ppdu(struct ath11k_dp *dp, ++ struct dp_full_mon_mpdu *mon_mpdu) ++{ ++ struct dp_full_mon_mpdu *tmp; ++ struct sk_buff *tmp_msdu, *skb_next; ++ ++ if (list_empty(&dp->dp_full_mon_mpdu_list)) ++ return; ++ ++ list_for_each_entry_safe(mon_mpdu, tmp, &dp->dp_full_mon_mpdu_list, list) { ++ list_del(&mon_mpdu->list); ++ ++ tmp_msdu = mon_mpdu->head; ++ while (tmp_msdu) { ++ skb_next = tmp_msdu->next; ++ dev_kfree_skb_any(tmp_msdu); ++ tmp_msdu = skb_next; ++ } ++ ++ kfree(mon_mpdu); ++ } ++} ++ ++static int ath11k_dp_rx_full_mon_deliver_ppdu(struct ath11k *ar, ++ int mac_id, ++ struct ath11k_mon_data *pmon, ++ struct napi_struct *napi) ++{ ++ struct ath11k_pdev_mon_stats *rx_mon_stats; ++ struct dp_full_mon_mpdu *tmp; ++ struct dp_full_mon_mpdu *mon_mpdu = pmon->mon_mpdu; ++ struct sk_buff *head_msdu, *tail_msdu; ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_dp *dp = &ab->dp; ++ int ret; ++ ++ rx_mon_stats = &pmon->rx_mon_stats; ++ ++ list_for_each_entry_safe(mon_mpdu, tmp, &dp->dp_full_mon_mpdu_list, list) { ++ list_del(&mon_mpdu->list); ++ head_msdu = mon_mpdu->head; ++ tail_msdu = mon_mpdu->tail; ++ if (head_msdu && tail_msdu) { ++ ret = ath11k_dp_rx_mon_deliver(ar, mac_id, head_msdu, ++ tail_msdu, napi); ++ rx_mon_stats->dest_mpdu_done++; ++ ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "full mon: deliver ppdu\n"); ++ } ++ kfree(mon_mpdu); ++ } ++ ++ return ret; ++} ++ ++static int ++ath11k_dp_rx_process_full_mon_status_ring(struct ath11k_base *ab, int mac_id, ++ struct napi_struct *napi, int budget) ++{ ++ struct ath11k *ar = ab->pdevs[mac_id].ar; ++ struct ath11k_pdev_dp *dp = &ar->dp; ++ struct ath11k_mon_data *pmon = &dp->mon_data; ++ struct hal_sw_mon_ring_entries *sw_mon_entries; ++ int quota = 0, work = 0, count; ++ ++ sw_mon_entries = &pmon->sw_mon_entries; ++ ++ while (pmon->hold_mon_dst_ring) { ++ quota = ath11k_dp_rx_process_mon_status(ab, mac_id, ++ napi, 1); ++ if (pmon->buf_state == DP_MON_STATUS_MATCH) { ++ count = sw_mon_entries->status_buf_count; ++ if (count > 1) { ++ quota += ath11k_dp_rx_process_mon_status(ab, mac_id, ++ napi, count); ++ } ++ ++ ath11k_dp_rx_full_mon_deliver_ppdu(ar, dp->mac_id, ++ pmon, napi); ++ pmon->hold_mon_dst_ring = false; ++ } else if (!pmon->mon_status_paddr || ++ pmon->buf_state == DP_MON_STATUS_LEAD) { ++ sw_mon_entries->drop_ppdu = true; ++ pmon->hold_mon_dst_ring = false; ++ } ++ ++ if (!quota) ++ break; ++ ++ work += quota; ++ } ++ ++ if (sw_mon_entries->drop_ppdu) ++ ath11k_dp_rx_full_mon_drop_ppdu(&ab->dp, pmon->mon_mpdu); ++ ++ return work; ++} ++ ++static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, ++ struct napi_struct *napi, int budget) ++{ ++ struct ath11k *ar = ab->pdevs[mac_id].ar; ++ struct ath11k_pdev_dp *dp = &ar->dp; ++ struct ath11k_mon_data *pmon = &dp->mon_data; ++ struct hal_sw_mon_ring_entries *sw_mon_entries; ++ struct ath11k_pdev_mon_stats *rx_mon_stats; ++ struct sk_buff *head_msdu, *tail_msdu; ++ void *mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; ++ void *ring_entry; ++ u32 rx_bufs_used = 0, mpdu_rx_bufs_used; ++ int quota = 0, ret; ++ bool break_dst_ring = false; ++ ++ spin_lock_bh(&pmon->mon_lock); ++ ++ sw_mon_entries = &pmon->sw_mon_entries; ++ rx_mon_stats = &pmon->rx_mon_stats; ++ ++ if (pmon->hold_mon_dst_ring) { ++ spin_unlock_bh(&pmon->mon_lock); ++ goto reap_status_ring; ++ } ++ ++ ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); ++ while ((ring_entry = ath11k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { ++ head_msdu = NULL; ++ tail_msdu = NULL; ++ ++ mpdu_rx_bufs_used = ath11k_dp_rx_full_mon_mpdu_pop(ar, ring_entry, ++ &head_msdu, ++ &tail_msdu, ++ sw_mon_entries); ++ rx_bufs_used += mpdu_rx_bufs_used; ++ ++ if (!sw_mon_entries->end_of_ppdu) { ++ if (head_msdu) { ++ ret = ath11k_dp_rx_full_mon_prepare_mpdu(&ab->dp, ++ pmon->mon_mpdu, ++ head_msdu, ++ tail_msdu); ++ if (ret) ++ break_dst_ring = true; ++ } ++ ++ goto next_entry; ++ } else { ++ if (!sw_mon_entries->ppdu_id && ++ !sw_mon_entries->mon_status_paddr) { ++ break_dst_ring = true; ++ goto next_entry; ++ } ++ } ++ ++ rx_mon_stats->dest_ppdu_done++; ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_START; ++ pmon->buf_state = DP_MON_STATUS_LAG; ++ pmon->mon_status_paddr = sw_mon_entries->mon_status_paddr; ++ pmon->hold_mon_dst_ring = true; ++next_entry: ++ ring_entry = ath11k_hal_srng_dst_get_next_entry(ar->ab, ++ mon_dst_srng); ++ if (break_dst_ring) ++ break; ++ } ++ ++ ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); ++ spin_unlock_bh(&pmon->mon_lock); ++ ++ if (rx_bufs_used) { ++ ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, ++ &dp->rxdma_mon_buf_ring, ++ rx_bufs_used, ++ HAL_RX_BUF_RBM_SW3_BM); ++ } ++ ++reap_status_ring: ++ quota = ath11k_dp_rx_process_full_mon_status_ring(ab, mac_id, ++ napi, budget); ++ ++ return quota; ++} ++ + static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) + { +@@ -5120,10 +5521,14 @@ int ath11k_dp_rx_process_mon_rings(struc + struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); + int ret = 0; + +- if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && ++ ab->hw_params.full_monitor_mode) ++ ret = ath11k_dp_full_mon_process_rx(ab, mac_id, napi, budget); ++ else if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) + ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget); + else + ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); ++ + return ret; + } + diff --git a/package/kernel/mac80211/patches/ath11k/0133-ath11k-add-spectral-CFR-buffer-validation-support.patch b/package/kernel/mac80211/patches/ath11k/0133-ath11k-add-spectral-CFR-buffer-validation-support.patch new file mode 100644 index 00000000000000..ac37a3a0b6db68 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0133-ath11k-add-spectral-CFR-buffer-validation-support.patch @@ -0,0 +1,118 @@ +From d3d358efc553de4f9d803c889a2e91523ea90f19 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Wed, 8 Dec 2021 10:44:00 +0200 +Subject: [PATCH] ath11k: add spectral/CFR buffer validation support + +Currently there is no validation on the spectral/CFR report +over the db ring buffers from the hardware. Improper/incomplete +DMA by the target can result in invalid data received by host. +Due to this we may populate incorrect data to user space. + +This buffer validation support fix this issues by filling some +magic value in the buffer during buffer replenish and check for +the magic value in the buffer received by the target. If host +detect magic value in the received buffer it will drop the buffer. + +Tested-on: IPQ8074 WLAN.HK.2.4.0.1-01467-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1637312901-10279-2-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dbring.c | 30 ++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/dbring.h | 2 ++ + drivers/net/wireless/ath/ath11k/spectral.c | 14 ++++++++++ + 3 files changed, 46 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/dbring.c ++++ b/drivers/net/wireless/ath/ath11k/dbring.c +@@ -6,6 +6,35 @@ + #include "core.h" + #include "debug.h" + ++#define ATH11K_DB_MAGIC_VALUE 0xdeadbeaf ++ ++int ath11k_dbring_validate_buffer(struct ath11k *ar, void *buffer, u32 size) ++{ ++ u32 *temp; ++ int idx; ++ ++ size = size >> 2; ++ ++ for (idx = 0, temp = buffer; idx < size; idx++, temp++) { ++ if (*temp == ATH11K_DB_MAGIC_VALUE) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void ath11k_dbring_fill_magic_value(struct ath11k *ar, ++ void *buffer, u32 size) ++{ ++ u32 *temp; ++ int idx; ++ ++ size = size >> 2; ++ ++ for (idx = 0, temp = buffer; idx < size; idx++, temp++) ++ *temp++ = ATH11K_DB_MAGIC_VALUE; ++} ++ + static int ath11k_dbring_bufs_replenish(struct ath11k *ar, + struct ath11k_dbring *ring, + struct ath11k_dbring_element *buff) +@@ -26,6 +55,7 @@ static int ath11k_dbring_bufs_replenish( + + ptr_unaligned = buff->payload; + ptr_aligned = PTR_ALIGN(ptr_unaligned, ring->buf_align); ++ ath11k_dbring_fill_magic_value(ar, ptr_aligned, ring->buf_sz); + paddr = dma_map_single(ab->dev, ptr_aligned, ring->buf_sz, + DMA_FROM_DEVICE); + +--- a/drivers/net/wireless/ath/ath11k/dbring.h ++++ b/drivers/net/wireless/ath/ath11k/dbring.h +@@ -76,4 +76,6 @@ int ath11k_dbring_get_cap(struct ath11k_ + struct ath11k_dbring_cap *db_cap); + void ath11k_dbring_srng_cleanup(struct ath11k *ar, struct ath11k_dbring *ring); + void ath11k_dbring_buf_cleanup(struct ath11k *ar, struct ath11k_dbring *ring); ++int ath11k_dbring_validate_buffer(struct ath11k *ar, void *data, u32 size); ++ + #endif /* ATH11K_DBRING_H */ +--- a/drivers/net/wireless/ath/ath11k/spectral.c ++++ b/drivers/net/wireless/ath/ath11k/spectral.c +@@ -585,6 +585,7 @@ int ath11k_spectral_process_fft(struct a + u16 length, freq; + u8 chan_width_mhz, bin_sz; + int ret; ++ u32 check_length; + + lockdep_assert_held(&ar->spectral.lock); + +@@ -618,6 +619,13 @@ int ath11k_spectral_process_fft(struct a + return -EINVAL; + } + ++ check_length = sizeof(*fft_report) + (num_bins * ab->hw_params.spectral.fft_sz); ++ ret = ath11k_dbring_validate_buffer(ar, data, check_length); ++ if (ret) { ++ ath11k_warn(ar->ab, "found magic value in fft data, dropping\n"); ++ return ret; ++ } ++ + ret = ath11k_spectral_pull_search(ar, data, &search); + if (ret) { + ath11k_warn(ab, "failed to pull search report %d\n", ret); +@@ -751,6 +759,12 @@ static int ath11k_spectral_process_data( + goto err; + } + ++ ret = ath11k_dbring_validate_buffer(ar, data, tlv_len); ++ if (ret) { ++ ath11k_warn(ar->ab, "found magic value in spectral summary, dropping\n"); ++ goto err; ++ } ++ + summary = (struct spectral_summary_fft_report *)tlv; + ath11k_spectral_pull_summary(ar, ¶m->meta, + summary, &summ_rpt); diff --git a/package/kernel/mac80211/patches/ath11k/0136-ath11k-support-MAC-address-randomization-in-scan.patch b/package/kernel/mac80211/patches/ath11k/0136-ath11k-support-MAC-address-randomization-in-scan.patch new file mode 100644 index 00000000000000..90b984b0f2d2ce --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0136-ath11k-support-MAC-address-randomization-in-scan.patch @@ -0,0 +1,137 @@ +From 9cbd7fc9be82a99af690adcebd6f2cdae4c7c193 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Thu, 9 Dec 2021 10:17:49 +0200 +Subject: [PATCH] ath11k: support MAC address randomization in scan + +The driver reports NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR capability +to upper layer based on the service bit firmware reported. Driver +sets the spoofed flag in scan_ctrl_flag to firmware if upper layer +has enabled this feature in scan request. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638948007-9609-1-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 19 +++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 30 +++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 10 ++++++++- + 3 files changed, 58 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -3546,6 +3546,12 @@ static int ath11k_mac_op_hw_scan(struct + arg.chan_list[i] = req->channels[i]->center_freq; + } + ++ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { ++ arg.scan_f_add_spoofed_mac_in_probe = 1; ++ ether_addr_copy(arg.mac_addr.addr, req->mac_addr); ++ ether_addr_copy(arg.mac_mask.addr, req->mac_addr_mask); ++ } ++ + ret = ath11k_start_scan(ar, &arg); + if (ret) { + ath11k_warn(ar->ab, "failed to start hw scan: %d\n", ret); +@@ -5590,6 +5596,14 @@ static int ath11k_mac_op_start(struct ie + goto err; + } + ++ if (test_bit(WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi->wmi_ab->svc_map)) { ++ ret = ath11k_wmi_scan_prob_req_oui(ar, ar->mac_addr); ++ if (ret) { ++ ath11k_err(ab, "failed to set prob req oui: %i\n", ret); ++ goto err; ++ } ++ } ++ + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + 0, pdev->pdev_id); + if (ret) { +@@ -8186,6 +8200,11 @@ static int __ath11k_mac_register(struct + + ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; + ++ if (test_bit(WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi->wmi_ab->svc_map)) { ++ ar->hw->wiphy->features |= ++ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; ++ } ++ + ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; + ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -2181,6 +2181,8 @@ int ath11k_wmi_send_scan_start_cmd(struc + cmd->num_ssids = params->num_ssids; + cmd->ie_len = params->extraie.len; + cmd->n_probes = params->n_probes; ++ ether_addr_copy(cmd->mac_addr.addr, params->mac_addr.addr); ++ ether_addr_copy(cmd->mac_mask.addr, params->mac_mask.addr); + + ptr += sizeof(*cmd); + +@@ -7710,3 +7712,31 @@ int ath11k_wmi_wow_enable(struct ath11k + + return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + } ++ ++int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, ++ const u8 mac_addr[ETH_ALEN]) ++{ ++ struct sk_buff *skb; ++ struct wmi_scan_prob_req_oui_cmd *cmd; ++ u32 prob_req_oui; ++ int len; ++ ++ prob_req_oui = (((u32)mac_addr[0]) << 16) | ++ (((u32)mac_addr[1]) << 8) | mac_addr[2]; ++ ++ len = sizeof(*cmd); ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_scan_prob_req_oui_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_SCAN_PROB_REQ_OUI_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ cmd->prob_req_oui = prob_req_oui; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi scan prob req oui %d\n", ++ prob_req_oui); ++ ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID); ++} +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3313,6 +3313,8 @@ struct scan_req_params { + u32 num_hint_bssid; + struct hint_short_ssid hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID]; + struct hint_bssid hint_bssid[WLAN_SCAN_MAX_HINT_BSSID]; ++ struct wmi_mac_addr mac_addr; ++ struct wmi_mac_addr mac_mask; + }; + + struct wmi_ssid_arg { +@@ -3676,6 +3678,11 @@ struct wmi_scan_chan_list_cmd { + u32 pdev_id; + } __packed; + ++struct wmi_scan_prob_req_oui_cmd { ++ u32 tlv_header; ++ u32 prob_req_oui; ++} __packed; ++ + #define WMI_MGMT_SEND_DOWNLD_LEN 64 + + #define WMI_TX_PARAMS_DWORD0_POWER GENMASK(7, 0) +@@ -5530,5 +5537,6 @@ int ath11k_wmi_set_hw_mode(struct ath11k + enum wmi_host_hw_mode_config_type mode); + int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar); + int ath11k_wmi_wow_enable(struct ath11k *ar); +- ++int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, ++ const u8 mac_addr[ETH_ALEN]); + #endif diff --git a/package/kernel/mac80211/patches/ath11k/0137-ath11k-set-DTIM-policy-to-stick-mode-for-station-int.patch b/package/kernel/mac80211/patches/ath11k/0137-ath11k-set-DTIM-policy-to-stick-mode-for-station-int.patch new file mode 100644 index 00000000000000..a74d67801d4998 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0137-ath11k-set-DTIM-policy-to-stick-mode-for-station-int.patch @@ -0,0 +1,51 @@ +From 55e18e5a76ab5de2286d4bac703a78066278f475 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Thu, 9 Dec 2021 10:17:49 +0200 +Subject: [PATCH] ath11k: set DTIM policy to stick mode for station interface + +Set DTIM policy to DTIM stick mode, so station follows AP DTIM +interval rather than listen interval which is set in peer assoc cmd. +DTIM stick mode is more preferred per firmware team request. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638948694-15582-1-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 7 +++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 7 +++++++ + 2 files changed, 14 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2682,6 +2682,13 @@ static void ath11k_bss_assoc(struct ieee + ath11k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n", + arvif->vdev_id, ret); + ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ WMI_VDEV_PARAM_DTIM_POLICY, ++ WMI_DTIM_POLICY_STICK); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to set vdev %d dtim policy: %d\n", ++ arvif->vdev_id, ret); ++ + ath11k_mac_11d_scan_stop_all(ar->ab); + } + +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -2169,6 +2169,13 @@ enum wmi_nss_ratio { + WMI_NSS_RATIO_2_NSS = 0x3, + }; + ++enum wmi_dtim_policy { ++ WMI_DTIM_POLICY_IGNORE = 1, ++ WMI_DTIM_POLICY_NORMAL = 2, ++ WMI_DTIM_POLICY_STICK = 3, ++ WMI_DTIM_POLICY_AUTO = 4, ++}; ++ + struct wmi_host_pdev_band_to_mac { + u32 pdev_id; + u32 start_freq; diff --git a/package/kernel/mac80211/patches/ath11k/0141-ath11k-add-ab-to-TARGET_NUM_VDEVS-co.patch b/package/kernel/mac80211/patches/ath11k/0141-ath11k-add-ab-to-TARGET_NUM_VDEVS-co.patch new file mode 100644 index 00000000000000..750ec81e333b0f --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0141-ath11k-add-ab-to-TARGET_NUM_VDEVS-co.patch @@ -0,0 +1,130 @@ +From 523aafd0f071c5f9e951a861c30531c1eeb01ad4 Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Mon, 13 Dec 2021 11:53:08 +0200 +Subject: [PATCH] ath11k: add ab to TARGET_NUM_VDEVS & co + +The next patch changes TARGET_NUM_VDEVS to be dynamic and need access to ab. +Add ab separately to keep the next patch simple. + +Compile tested only. + +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211209104351.9811-2-kvalo@kernel.org +--- + drivers/net/wireless/ath/ath11k/core.c | 2 +- + drivers/net/wireless/ath/ath11k/hw.c | 14 +++++++------- + drivers/net/wireless/ath/ath11k/hw.h | 18 +++++++++--------- + drivers/net/wireless/ath/ath11k/mac.c | 10 +++++----- + 4 files changed, 22 insertions(+), 22 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1067,7 +1067,7 @@ static int ath11k_core_reconfigure_on_cr + ath11k_dp_free(ab); + ath11k_hal_srng_deinit(ab); + +- ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; ++ ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS(ab))) - 1; + + ret = ath11k_hal_srng_init(ab); + if (ret) +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -150,18 +150,18 @@ static void ath11k_hw_ipq8074_reo_setup( + static void ath11k_init_wmi_config_ipq8074(struct ath11k_base *ab, + struct target_resource_config *config) + { +- config->num_vdevs = ab->num_radios * TARGET_NUM_VDEVS; ++ config->num_vdevs = ab->num_radios * TARGET_NUM_VDEVS(ab); + + if (ab->num_radios == 2) { +- config->num_peers = TARGET_NUM_PEERS(DBS); +- config->num_tids = TARGET_NUM_TIDS(DBS); ++ config->num_peers = TARGET_NUM_PEERS(ab, DBS); ++ config->num_tids = TARGET_NUM_TIDS(ab, DBS); + } else if (ab->num_radios == 3) { +- config->num_peers = TARGET_NUM_PEERS(DBS_SBS); +- config->num_tids = TARGET_NUM_TIDS(DBS_SBS); ++ config->num_peers = TARGET_NUM_PEERS(ab, DBS_SBS); ++ config->num_tids = TARGET_NUM_TIDS(ab, DBS_SBS); + } else { + /* Control should not reach here */ +- config->num_peers = TARGET_NUM_PEERS(SINGLE); +- config->num_tids = TARGET_NUM_TIDS(SINGLE); ++ config->num_peers = TARGET_NUM_PEERS(ab, SINGLE); ++ config->num_tids = TARGET_NUM_TIDS(ab, SINGLE); + } + config->num_offload_peers = TARGET_NUM_OFFLD_PEERS; + config->num_offload_reorder_buffs = TARGET_NUM_OFFLD_REORDER_BUFFS; +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -12,26 +12,26 @@ + /* Target configuration defines */ + + /* Num VDEVS per radio */ +-#define TARGET_NUM_VDEVS (16 + 1) ++#define TARGET_NUM_VDEVS(ab) (16 + 1) + +-#define TARGET_NUM_PEERS_PDEV (512 + TARGET_NUM_VDEVS) ++#define TARGET_NUM_PEERS_PDEV(ab) (512 + TARGET_NUM_VDEVS(ab)) + + /* Num of peers for Single Radio mode */ +-#define TARGET_NUM_PEERS_SINGLE (TARGET_NUM_PEERS_PDEV) ++#define TARGET_NUM_PEERS_SINGLE(ab) (TARGET_NUM_PEERS_PDEV(ab)) + + /* Num of peers for DBS */ +-#define TARGET_NUM_PEERS_DBS (2 * TARGET_NUM_PEERS_PDEV) ++#define TARGET_NUM_PEERS_DBS(ab) (2 * TARGET_NUM_PEERS_PDEV(ab)) + + /* Num of peers for DBS_SBS */ +-#define TARGET_NUM_PEERS_DBS_SBS (3 * TARGET_NUM_PEERS_PDEV) ++#define TARGET_NUM_PEERS_DBS_SBS(ab) (3 * TARGET_NUM_PEERS_PDEV(ab)) + + /* Max num of stations (per radio) */ +-#define TARGET_NUM_STATIONS 512 ++#define TARGET_NUM_STATIONS(ab) 512 + +-#define TARGET_NUM_PEERS(x) TARGET_NUM_PEERS_##x ++#define TARGET_NUM_PEERS(ab, x) TARGET_NUM_PEERS_##x(ab) + #define TARGET_NUM_PEER_KEYS 2 +-#define TARGET_NUM_TIDS(x) (2 * TARGET_NUM_PEERS(x) + \ +- 4 * TARGET_NUM_VDEVS + 8) ++#define TARGET_NUM_TIDS(ab, x) (2 * TARGET_NUM_PEERS(ab, x) + \ ++ 4 * TARGET_NUM_VDEVS(ab) + 8) + + #define TARGET_AST_SKID_LIMIT 16 + #define TARGET_NUM_OFFLD_PEERS 4 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6004,9 +6004,9 @@ static int ath11k_mac_op_add_interface(s + goto err; + } + +- if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) { ++ if (ar->num_created_vdevs > (TARGET_NUM_VDEVS(ab) - 1)) { + ath11k_warn(ab, "failed to create vdev %u, reached max vdev limit %d\n", +- ar->num_created_vdevs, TARGET_NUM_VDEVS); ++ ar->num_created_vdevs, TARGET_NUM_VDEVS(ab)); + ret = -EBUSY; + goto err; + } +@@ -8202,8 +8202,8 @@ static int __ath11k_mac_register(struct + ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | + NL80211_FEATURE_AP_SCAN; + +- ar->max_num_stations = TARGET_NUM_STATIONS; +- ar->max_num_peers = TARGET_NUM_PEERS_PDEV; ++ ar->max_num_stations = TARGET_NUM_STATIONS(ab); ++ ar->max_num_peers = TARGET_NUM_PEERS_PDEV(ab); + + ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; + +@@ -8306,7 +8306,7 @@ int ath11k_mac_register(struct ath11k_ba + + /* Initialize channel counters frequency value in hertz */ + ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; +- ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; ++ ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS(ab))) - 1; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; diff --git a/package/kernel/mac80211/patches/ath11k/0142-ath11k-Change-qcn9074-fw-to-operate-in-mode-2.patch b/package/kernel/mac80211/patches/ath11k/0142-ath11k-Change-qcn9074-fw-to-operate-in-mode-2.patch new file mode 100644 index 00000000000000..7d9f0cd766b992 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0142-ath11k-Change-qcn9074-fw-to-operate-in-mode-2.patch @@ -0,0 +1,139 @@ +From beefee71336bfb406c3e37a0de8a12881424e78c Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Mon, 13 Dec 2021 11:53:08 +0200 +Subject: [PATCH] ath11k: Change qcn9074 fw to operate in mode-2 + +In mode-2 QCN9074 firmware uses 15MB of host memory and firmware +request 1MB size segements in QMI, whereas in mode-0 firmware +uses 45MB of host memory and each segment is of 2MB size. +In mode-2 firmware operates with reduced number of vdevs and peers. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211209104351.9811-3-kvalo@kernel.org +--- + drivers/net/wireless/ath/ath11k/core.c | 18 ++++++++++++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 9 ++++++--- + drivers/net/wireless/ath/ath11k/qmi.c | 2 +- + drivers/net/wireless/ath/ath11k/qmi.h | 1 - + 4 files changed, 25 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -79,6 +79,9 @@ static const struct ath11k_hw_params ath + .idle_ps = false, + .supports_sta_ps = false, + .cold_boot_calib = true, ++ .fw_mem_mode = 0, ++ .num_vdevs = 16 + 1, ++ .num_peers = 512, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, +@@ -134,6 +137,9 @@ static const struct ath11k_hw_params ath + .idle_ps = false, + .supports_sta_ps = false, + .cold_boot_calib = true, ++ .fw_mem_mode = 0, ++ .num_vdevs = 16 + 1, ++ .num_peers = 512, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, +@@ -188,6 +194,9 @@ static const struct ath11k_hw_params ath + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = false, ++ .fw_mem_mode = 0, ++ .num_vdevs = 16 + 1, ++ .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .fix_l1ss = true, +@@ -242,6 +251,9 @@ static const struct ath11k_hw_params ath + .idle_ps = false, + .supports_sta_ps = false, + .cold_boot_calib = false, ++ .fw_mem_mode = 2, ++ .num_vdevs = 8, ++ .num_peers = 128, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .fix_l1ss = true, +@@ -296,6 +308,9 @@ static const struct ath11k_hw_params ath + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = false, ++ .fw_mem_mode = 0, ++ .num_vdevs = 16 + 1, ++ .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .fix_l1ss = false, +@@ -349,6 +364,9 @@ static const struct ath11k_hw_params ath + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = false, ++ .fw_mem_mode = 0, ++ .num_vdevs = 16 + 1, ++ .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .fix_l1ss = false, +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -12,9 +12,9 @@ + /* Target configuration defines */ + + /* Num VDEVS per radio */ +-#define TARGET_NUM_VDEVS(ab) (16 + 1) ++#define TARGET_NUM_VDEVS(ab) (ab->hw_params.num_vdevs) + +-#define TARGET_NUM_PEERS_PDEV(ab) (512 + TARGET_NUM_VDEVS(ab)) ++#define TARGET_NUM_PEERS_PDEV(ab) (ab->hw_params.num_peers + TARGET_NUM_VDEVS(ab)) + + /* Num of peers for Single Radio mode */ + #define TARGET_NUM_PEERS_SINGLE(ab) (TARGET_NUM_PEERS_PDEV(ab)) +@@ -26,7 +26,7 @@ + #define TARGET_NUM_PEERS_DBS_SBS(ab) (3 * TARGET_NUM_PEERS_PDEV(ab)) + + /* Max num of stations (per radio) */ +-#define TARGET_NUM_STATIONS(ab) 512 ++#define TARGET_NUM_STATIONS(ab) (ab->hw_params.num_peers) + + #define TARGET_NUM_PEERS(ab, x) TARGET_NUM_PEERS_##x(ab) + #define TARGET_NUM_PEER_KEYS 2 +@@ -173,6 +173,9 @@ struct ath11k_hw_params { + bool idle_ps; + bool supports_sta_ps; + bool cold_boot_calib; ++ int fw_mem_mode; ++ u32 num_vdevs; ++ u32 num_peers; + bool supports_suspend; + u32 hal_desc_sz; + bool fix_l1ss; +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2830,7 +2830,7 @@ int ath11k_qmi_init_service(struct ath11 + memset(&ab->qmi.target_mem, 0, sizeof(struct target_mem_chunk)); + ab->qmi.ab = ab; + +- ab->qmi.target_mem_mode = ATH11K_QMI_TARGET_MEM_MODE_DEFAULT; ++ ab->qmi.target_mem_mode = ab->hw_params.fw_mem_mode; + ret = qmi_handle_init(&ab->qmi.handle, ATH11K_QMI_RESP_LEN_MAX, + &ath11k_qmi_ops, ath11k_qmi_msg_handlers); + if (ret < 0) { +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -34,7 +34,6 @@ + + #define QMI_WLANFW_MAX_DATA_SIZE_V01 6144 + #define ATH11K_FIRMWARE_MODE_OFF 4 +-#define ATH11K_QMI_TARGET_MEM_MODE_DEFAULT 0 + #define ATH11K_COLD_BOOT_FW_RESET_DELAY (40 * HZ) + + struct ath11k_base; diff --git a/package/kernel/mac80211/patches/ath11k/0143-ath11k-Use-reserved-host-DDR-addresses-from-DT-for-P.patch b/package/kernel/mac80211/patches/ath11k/0143-ath11k-Use-reserved-host-DDR-addresses-from-DT-for-P.patch new file mode 100644 index 00000000000000..4842e5261872a7 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0143-ath11k-Use-reserved-host-DDR-addresses-from-DT-for-P.patch @@ -0,0 +1,254 @@ +From 6ac04bdc5edb418787ab2040b1f922c23464c750 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Tue, 14 Dec 2021 17:39:43 +0200 +Subject: [PATCH] ath11k: Use reserved host DDR addresses from DT for PCI + devices + +Host DDR memory (contiguous 45 MB in mode-0 or 15 MB in mode-2) +is reserved through DT entries for firmware usage. Send the base +address from DT entries. +If DT entry is available, PCI device will work with +fixed_mem_region else host allocates multiple segments. + +IPQ8074 on HK10 board supports multiple PCI devices. +IPQ8074 + QCN9074 is tested with this patch. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01838-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638789319-2950-2-git-send-email-akolli@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mhi.c | 34 ++++++++++++- + drivers/net/wireless/ath/ath11k/pci.c | 11 ++++- + drivers/net/wireless/ath/ath11k/qmi.c | 67 ++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/qmi.h | 1 + + 5 files changed, 101 insertions(+), 13 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -203,6 +203,7 @@ enum ath11k_dev_flags { + ATH11K_FLAG_HTC_SUSPEND_COMPLETE, + ATH11K_FLAG_CE_IRQ_ENABLED, + ATH11K_FLAG_EXT_IRQ_ENABLED, ++ ATH11K_FLAG_FIXED_MEM_RGN, + }; + + enum ath11k_monitor_flags { +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -3,6 +3,9 @@ + + #include + #include ++#include ++#include ++#include + + #include "core.h" + #include "debug.h" +@@ -318,6 +321,26 @@ static void ath11k_mhi_op_write_reg(stru + writel(val, addr); + } + ++static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl) ++{ ++ struct device_node *np; ++ struct resource res; ++ int ret; ++ ++ np = of_find_node_by_type(NULL, "memory"); ++ if (!np) ++ return -ENOENT; ++ ++ ret = of_address_to_resource(np, 0, &res); ++ if (ret) ++ return ret; ++ ++ mhi_ctrl->iova_start = res.start + 0x1000000; ++ mhi_ctrl->iova_stop = res.end; ++ ++ return 0; ++} ++ + int ath11k_mhi_register(struct ath11k_pci *ab_pci) + { + struct ath11k_base *ab = ab_pci->ab; +@@ -351,8 +374,15 @@ int ath11k_mhi_register(struct ath11k_pc + if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) + mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; + +- mhi_ctrl->iova_start = 0; +- mhi_ctrl->iova_stop = 0xffffffff; ++ if (test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { ++ ret = ath11k_mhi_read_addr_from_dt(mhi_ctrl); ++ if (ret < 0) ++ return ret; ++ } else { ++ mhi_ctrl->iova_start = 0; ++ mhi_ctrl->iova_stop = 0xFFFFFFFF; ++ } ++ + mhi_ctrl->sbl_size = SZ_512K; + mhi_ctrl->seg_len = SZ_512K; + mhi_ctrl->fbc_download = true; +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + + #include "pci.h" + #include "core.h" +@@ -1353,7 +1354,7 @@ static int ath11k_pci_probe(struct pci_d + { + struct ath11k_base *ab; + struct ath11k_pci *ab_pci; +- u32 soc_hw_version_major, soc_hw_version_minor; ++ u32 soc_hw_version_major, soc_hw_version_minor, addr; + int ret; + + ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI, +@@ -1373,6 +1374,14 @@ static int ath11k_pci_probe(struct pci_d + pci_set_drvdata(pdev, ab); + spin_lock_init(&ab_pci->window_lock); + ++ /* Set fixed_mem_region to true for platforms support reserved memory ++ * from DT. If memory is reserved from DT for FW, ath11k driver need not ++ * allocate memory. ++ */ ++ ret = of_property_read_u32(ab->dev->of_node, "memory-region", &addr); ++ if (!ret) ++ set_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags); ++ + ret = ath11k_pci_claim(ab_pci, pdev); + if (ret) { + ath11k_err(ab, "failed to claim device: %d\n", ret); +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -9,6 +9,8 @@ + #include "core.h" + #include "debug.h" + #include ++#include ++#include + #include + + #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 +@@ -1751,7 +1753,9 @@ static int ath11k_qmi_respond_fw_mem_req + * failure to FW and FW will then request mulitple blocks of small + * chunk size memory. + */ +- if (!ab->bus_params.fixed_mem_region && ab->qmi.target_mem_delayed) { ++ if (!(ab->bus_params.fixed_mem_region || ++ test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && ++ ab->qmi.target_mem_delayed) { + delayed = true; + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi delays mem_request %d\n", + ab->qmi.mem_seg_count); +@@ -1818,10 +1822,12 @@ static void ath11k_qmi_free_target_mem_c + { + int i; + +- if (ab->bus_params.fixed_mem_region) +- return; +- + for (i = 0; i < ab->qmi.mem_seg_count; i++) { ++ if ((ab->bus_params.fixed_mem_region || ++ test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && ++ ab->qmi.target_mem[i].iaddr) ++ iounmap(ab->qmi.target_mem[i].iaddr); ++ + if (!ab->qmi.target_mem[i].vaddr) + continue; + +@@ -1869,10 +1875,44 @@ static int ath11k_qmi_alloc_target_mem_c + + static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) + { +- int i, idx; ++ struct device *dev = ab->dev; ++ struct device_node *hremote_node = NULL; ++ struct resource res; ++ u32 host_ddr_sz; ++ int i, idx, ret; + + for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { + switch (ab->qmi.target_mem[i].type) { ++ case HOST_DDR_REGION_TYPE: ++ hremote_node = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (!hremote_node) { ++ ath11k_dbg(ab, ATH11K_DBG_QMI, ++ "qmi fail to get hremote_node\n"); ++ return ret; ++ } ++ ++ ret = of_address_to_resource(hremote_node, 0, &res); ++ if (ret) { ++ ath11k_dbg(ab, ATH11K_DBG_QMI, ++ "qmi fail to get reg from hremote\n"); ++ return ret; ++ } ++ ++ if (res.end - res.start + 1 < ab->qmi.target_mem[i].size) { ++ ath11k_dbg(ab, ATH11K_DBG_QMI, ++ "qmi fail to assign memory of sz\n"); ++ return -EINVAL; ++ } ++ ++ ab->qmi.target_mem[idx].paddr = res.start; ++ ab->qmi.target_mem[idx].iaddr = ++ ioremap(ab->qmi.target_mem[idx].paddr, ++ ab->qmi.target_mem[i].size); ++ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ++ host_ddr_sz = ab->qmi.target_mem[i].size; ++ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; ++ idx++; ++ break; + case BDF_MEM_REGION_TYPE: + ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; + ab->qmi.target_mem[idx].vaddr = NULL; +@@ -1887,10 +1927,16 @@ static int ath11k_qmi_assign_target_mem_ + } + + if (ath11k_cold_boot_cal && ab->hw_params.cold_boot_calib) { +- ab->qmi.target_mem[idx].paddr = +- ATH11K_QMI_CALDB_ADDRESS; +- ab->qmi.target_mem[idx].vaddr = +- (void *)ATH11K_QMI_CALDB_ADDRESS; ++ if (hremote_node) { ++ ab->qmi.target_mem[idx].paddr = ++ res.start + host_ddr_sz; ++ ab->qmi.target_mem[idx].iaddr = ++ ioremap(ab->qmi.target_mem[idx].paddr, ++ ab->qmi.target_mem[i].size); ++ } else { ++ ab->qmi.target_mem[idx].paddr = ++ ATH11K_QMI_CALDB_ADDRESS; ++ } + } else { + ab->qmi.target_mem[idx].paddr = 0; + ab->qmi.target_mem[idx].vaddr = NULL; +@@ -2621,7 +2667,8 @@ static void ath11k_qmi_msg_mem_request_c + msg->mem_seg[i].type, msg->mem_seg[i].size); + } + +- if (ab->bus_params.fixed_mem_region) { ++ if (ab->bus_params.fixed_mem_region || ++ test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { + ret = ath11k_qmi_assign_target_mem_chunk(ab); + if (ret) { + ath11k_warn(ab, "failed to assign qmi target memory: %d\n", +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -94,6 +94,7 @@ struct target_mem_chunk { + u32 type; + dma_addr_t paddr; + u32 *vaddr; ++ void __iomem *iaddr; + }; + + struct target_info { diff --git a/package/kernel/mac80211/patches/ath11k/0144-ath11k-report-rssi-of-each-chain-to-mac80211-for-QCA.patch b/package/kernel/mac80211/patches/ath11k/0144-ath11k-report-rssi-of-each-chain-to-mac80211-for-QCA.patch new file mode 100644 index 00000000000000..aff101f5f74b4f --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0144-ath11k-report-rssi-of-each-chain-to-mac80211-for-QCA.patch @@ -0,0 +1,654 @@ +From b488c766442f7d9c07ea0708aa2a1a6bff1baef5 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Fri, 17 Dec 2021 20:27:21 +0200 +Subject: [PATCH] ath11k: report rssi of each chain to mac80211 for + QCA6390/WCN6855 + +Command "iw wls1 station dump" does not show each chain's rssi currently. + +If the rssi of each chain from mon status which parsed in function +ath11k_hal_rx_parse_mon_status_tlv() is invalid, then ath11k send +wmi cmd WMI_REQUEST_STATS_CMDID with flag WMI_REQUEST_RSSI_PER_CHAIN_STAT +to firmware, and parse the rssi of chain in wmi WMI_UPDATE_STATS_EVENTID, +then report them to mac80211. + +WMI_REQUEST_STATS_CMDID is only sent when CONFIG_ATH11K_DEBUGFS is set, +it is only called by ath11k_mac_op_sta_statistics(). It does not effect +performance and power consumption. Because after STATION connected to +AP, it is only called every 6 seconds by NetworkManager in below stack. + +[ 797.005587] CPU: 0 PID: 701 Comm: NetworkManager Tainted: G W OE 5.13.0-rc6-wt-ath+ #2 +[ 797.005596] Hardware name: LENOVO 418065C/418065C, BIOS 83ET63WW (1.33 ) 07/29/2011 +[ 797.005600] RIP: 0010:ath11k_mac_op_sta_statistics+0x2f/0x1b0 [ath11k] +[ 797.005644] Code: 41 56 41 55 4c 8d aa 58 01 00 00 41 54 55 48 89 d5 53 48 8b 82 58 01 00 00 48 89 cb 4c 8b 70 20 49 8b 06 4c 8b a0 90 08 00 00 <0f> 0b 48 8b 82 b8 01 00 00 48 ba 00 00 00 00 01 00 00 00 48 89 81 +[ 797.005651] RSP: 0018:ffffb1fc80a4b890 EFLAGS: 00010282 +[ 797.005658] RAX: ffff8a5726200000 RBX: ffffb1fc80a4b958 RCX: ffffb1fc80a4b958 +[ 797.005664] RDX: ffff8a5726a609f0 RSI: ffff8a581247f598 RDI: ffff8a5702878800 +[ 797.005668] RBP: ffff8a5726a609f0 R08: 0000000000000000 R09: 0000000000000000 +[ 797.005672] R10: 0000000000000000 R11: 0000000000000007 R12: 02dd68024f75f480 +[ 797.005676] R13: ffff8a5726a60b48 R14: ffff8a5702879f40 R15: ffff8a5726a60000 +[ 797.005681] FS: 00007f632c52a380(0000) GS:ffff8a583a200000(0000) knlGS:0000000000000000 +[ 797.005687] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 797.005692] CR2: 00007fb025d69000 CR3: 00000001124f6005 CR4: 00000000000606f0 +[ 797.005698] Call Trace: +[ 797.005710] sta_set_sinfo+0xa7/0xb80 [mac80211] +[ 797.005820] ieee80211_get_station+0x50/0x70 [mac80211] +[ 797.005925] nl80211_get_station+0xd1/0x200 [cfg80211] +[ 797.006045] genl_family_rcv_msg_doit.isra.15+0x111/0x140 +[ 797.006059] genl_rcv_msg+0xe6/0x1e0 +[ 797.006065] ? nl80211_dump_station+0x220/0x220 [cfg80211] +[ 797.006223] ? nl80211_send_station.isra.72+0xf50/0xf50 [cfg80211] +[ 797.006348] ? genl_family_rcv_msg_doit.isra.15+0x140/0x140 +[ 797.006355] netlink_rcv_skb+0xb9/0xf0 +[ 797.006363] genl_rcv+0x24/0x40 +[ 797.006369] netlink_unicast+0x18e/0x290 +[ 797.006375] netlink_sendmsg+0x30f/0x450 +[ 797.006382] sock_sendmsg+0x5b/0x60 +[ 797.006393] ____sys_sendmsg+0x219/0x240 +[ 797.006403] ? copy_msghdr_from_user+0x5c/0x90 +[ 797.006413] ? ____sys_recvmsg+0xf5/0x190 +[ 797.006422] ___sys_sendmsg+0x88/0xd0 +[ 797.006432] ? copy_msghdr_from_user+0x5c/0x90 +[ 797.006443] ? ___sys_recvmsg+0x9e/0xd0 +[ 797.006454] ? __fget_files+0x58/0x90 +[ 797.006461] ? __fget_light+0x2d/0x70 +[ 797.006466] ? do_epoll_wait+0xce/0x720 +[ 797.006476] ? __sys_sendmsg+0x63/0xa0 +[ 797.006485] __sys_sendmsg+0x63/0xa0 +[ 797.006497] do_syscall_64+0x3c/0xb0 +[ 797.006509] entry_SYSCALL_64_after_hwframe+0x44/0xae +[ 797.006519] RIP: 0033:0x7f632d99912d +[ 797.006526] Code: 28 89 54 24 1c 48 89 74 24 10 89 7c 24 08 e8 ca ee ff ff 8b 54 24 1c 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 2e 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 2f 44 89 c7 48 89 44 24 08 e8 fe ee ff ff 48 +[ 797.006533] RSP: 002b:00007ffd80808c00 EFLAGS: 00000293 ORIG_RAX: 000000000000002e +[ 797.006540] RAX: ffffffffffffffda RBX: 0000563dab99d840 RCX: 00007f632d99912d +[ 797.006545] RDX: 0000000000000000 RSI: 00007ffd80808c50 RDI: 000000000000000b +[ 797.006549] RBP: 00007ffd80808c50 R08: 0000000000000000 R09: 0000000000001000 +[ 797.006552] R10: 0000563dab96f010 R11: 0000000000000293 R12: 0000563dab99d840 +[ 797.006556] R13: 0000563dabbb28c0 R14: 00007f632dad4280 R15: 0000563dabab11c0 +[ 797.006563] ---[ end trace c9dcf08920c9945c ]--- + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01230-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-02892.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211215090944.19729-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 6 + + drivers/net/wireless/ath/ath11k/core.h | 5 + + drivers/net/wireless/ath/ath11k/debugfs.c | 37 +++++ + drivers/net/wireless/ath/ath11k/debugfs.h | 8 + + drivers/net/wireless/ath/ath11k/dp_rx.c | 8 + + drivers/net/wireless/ath/ath11k/hal_rx.c | 11 ++ + drivers/net/wireless/ath/ath11k/hal_rx.h | 12 +- + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 40 +++++ + drivers/net/wireless/ath/ath11k/wmi.c | 179 ++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/wmi.h | 11 ++ + 11 files changed, 284 insertions(+), 34 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -91,6 +91,7 @@ static const struct ath11k_hw_params ath + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, + .wakeup_mhi = false, ++ .supports_rssi_stats = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -149,6 +150,7 @@ static const struct ath11k_hw_params ath + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, + .wakeup_mhi = false, ++ .supports_rssi_stats = false, + }, + { + .name = "qca6390 hw2.0", +@@ -206,6 +208,7 @@ static const struct ath11k_hw_params ath + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .wakeup_mhi = true, ++ .supports_rssi_stats = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -263,6 +266,7 @@ static const struct ath11k_hw_params ath + .supports_dynamic_smps_6ghz = true, + .alloc_cacheable_memory = true, + .wakeup_mhi = false, ++ .supports_rssi_stats = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -320,6 +324,7 @@ static const struct ath11k_hw_params ath + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .wakeup_mhi = true, ++ .supports_rssi_stats = true, + }, + { + .name = "wcn6855 hw2.1", +@@ -376,6 +381,7 @@ static const struct ath11k_hw_params ath + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .wakeup_mhi = true, ++ .supports_rssi_stats = true, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -387,6 +387,7 @@ struct ath11k_sta { + u64 rx_duration; + u64 tx_duration; + u8 rssi_comb; ++ s8 chain_signal[IEEE80211_MAX_CHAINS]; + struct ath11k_htt_tx_stats *tx_stats; + struct ath11k_rx_peer_stats *rx_stats; + +@@ -417,6 +418,10 @@ enum ath11k_state { + /* Antenna noise floor */ + #define ATH11K_DEFAULT_NOISE_FLOOR -95 + ++#define ATH11K_INVALID_RSSI_FULL -1 ++ ++#define ATH11K_INVALID_RSSI_EMPTY -128 ++ + struct ath11k_fw_stats { + struct dentry *debugfs_fwstats; + u32 pdev_id; +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -126,6 +126,11 @@ void ath11k_debugfs_fw_stats_process(str + goto complete; + } + ++ if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) { ++ ar->debug.fw_stats_done = true; ++ goto complete; ++ } ++ + if (stats.stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats.vdevs)) { + ath11k_warn(ab, "empty vdev stats"); +@@ -229,6 +234,38 @@ static int ath11k_debugfs_fw_stats_reque + return 0; + } + ++int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id, ++ u32 vdev_id, u32 stats_id) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct stats_request_params req_param; ++ int ret; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ if (ar->state != ATH11K_STATE_ON) { ++ ret = -ENETDOWN; ++ goto err_unlock; ++ } ++ ++ req_param.pdev_id = pdev_id; ++ req_param.vdev_id = vdev_id; ++ req_param.stats_id = stats_id; ++ ++ ret = ath11k_debugfs_fw_stats_request(ar, &req_param); ++ if (ret) ++ ath11k_warn(ab, "failed to request fw stats: %d\n", ret); ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n", ++ pdev_id, vdev_id, stats_id); ++ ++err_unlock: ++ mutex_unlock(&ar->conf_mutex); ++ ++ return ret; ++} ++ + static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) + { + struct ath11k *ar = inode->i_private; +--- a/drivers/net/wireless/ath/ath11k/debugfs.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs.h +@@ -117,6 +117,8 @@ void ath11k_debugfs_unregister(struct at + void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb); + + void ath11k_debugfs_fw_stats_init(struct ath11k *ar); ++int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id, ++ u32 vdev_id, u32 stats_id); + + static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar) + { +@@ -215,6 +217,12 @@ static inline int ath11k_debugfs_rx_filt + { + return 0; + } ++ ++static inline int ath11k_debugfs_get_fw_stats(struct ath11k *ar, ++ u32 pdev_id, u32 vdev_id, u32 stats_id) ++{ ++ return 0; ++} + + #endif /* CPTCFG_MAC80211_DEBUGFS*/ + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2768,6 +2768,7 @@ static void ath11k_dp_rx_update_peer_sta + { + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; + u32 num_msdu; ++ int i; + + if (!rx_stats) + return; +@@ -2829,6 +2830,13 @@ static void ath11k_dp_rx_update_peer_sta + rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; + + arsta->rssi_comb = ppdu_info->rssi_comb; ++ ++ BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > ++ ARRAY_SIZE(ppdu_info->rssi_chain_pri20)); ++ ++ for (i = 0; i < ARRAY_SIZE(arsta->chain_signal); i++) ++ arsta->chain_signal[i] = ppdu_info->rssi_chain_pri20[i]; ++ + rx_stats->rx_duration += ppdu_info->rx_duration; + arsta->rx_duration = rx_stats->rx_duration; + } +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1080,6 +1080,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + break; + } + case HAL_PHYRX_RSSI_LEGACY: { ++ int i; ++ bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, ++ ab->wmi_ab.svc_map); + struct hal_rx_phyrx_rssi_legacy_info *rssi = + (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data; + +@@ -1090,6 +1093,14 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + ppdu_info->rssi_comb = + FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB, + __le32_to_cpu(rssi->info0)); ++ ++ if (db2dbm) { ++ for (i = 0; i < ARRAY_SIZE(rssi->preamble); i++) { ++ ppdu_info->rssi_chain_pri20[i] = ++ le32_get_bits(rssi->preamble[i].rssi_2040, ++ HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20); ++ } ++ } + break; + } + case HAL_RX_MPDU_START: { +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -112,6 +112,7 @@ struct hal_rx_mon_ppdu_info { + u8 ldpc; + u8 beamformed; + u8 rssi_comb; ++ u8 rssi_chain_pri20[HAL_RX_MAX_NSS]; + u8 tid; + u8 dcm; + u8 ru_alloc; +@@ -262,8 +263,17 @@ struct hal_rx_he_sig_b2_ofdma_info { + + #define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB GENMASK(15, 8) + ++#define HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20 GENMASK(7, 0) ++ ++struct hal_rx_phyrx_chain_rssi { ++ __le32 rssi_2040; ++ __le32 rssi_80; ++} __packed; ++ + struct hal_rx_phyrx_rssi_legacy_info { +- __le32 rsvd[35]; ++ __le32 rsvd[3]; ++ struct hal_rx_phyrx_chain_rssi pre_rssi[HAL_RX_MAX_NSS]; ++ struct hal_rx_phyrx_chain_rssi preamble[HAL_RX_MAX_NSS]; + __le32 info0; + } __packed; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -185,6 +185,7 @@ struct ath11k_hw_params { + bool supports_dynamic_smps_6ghz; + bool alloc_cacheable_memory; + bool wakeup_mhi; ++ bool supports_rssi_stats; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7775,12 +7775,42 @@ exit: + return ret; + } + ++static void ath11k_mac_put_chain_rssi(struct station_info *sinfo, ++ struct ath11k_sta *arsta, ++ char *pre, ++ bool clear) ++{ ++ struct ath11k *ar = arsta->arvif->ar; ++ int i; ++ s8 rssi; ++ ++ for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { ++ sinfo->chains &= ~BIT(i); ++ rssi = arsta->chain_signal[i]; ++ if (clear) ++ arsta->chain_signal[i] = ATH11K_INVALID_RSSI_FULL; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac sta statistics %s rssi[%d] %d\n", pre, i, rssi); ++ ++ if (rssi != ATH11K_DEFAULT_NOISE_FLOOR && ++ rssi != ATH11K_INVALID_RSSI_FULL && ++ rssi != ATH11K_INVALID_RSSI_EMPTY && ++ rssi != 0) { ++ sinfo->chain_signal[i] = rssi; ++ sinfo->chains |= BIT(i); ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); ++ } ++ } ++} ++ + static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) + { + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; ++ struct ath11k *ar = arsta->arvif->ar; + + sinfo->rx_duration = arsta->rx_duration; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); +@@ -7803,6 +7833,16 @@ static void ath11k_mac_op_sta_statistics + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + } + ++ ath11k_mac_put_chain_rssi(sinfo, arsta, "ppdu", false); ++ ++ if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) && ++ arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA && ++ ar->ab->hw_params.supports_rssi_stats && ++ !ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0, ++ WMI_REQUEST_RSSI_PER_CHAIN_STAT)) { ++ ath11k_mac_put_chain_rssi(sinfo, arsta, "fw stats", true); ++ } ++ + /* TODO: Use real NF instead of default one. */ + sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -73,6 +73,14 @@ struct wmi_tlv_dma_buf_release_parse { + bool meta_data_done; + }; + ++struct wmi_tlv_fw_stats_parse { ++ const struct wmi_stats_event *ev; ++ const struct wmi_per_chain_rssi_stats *rssi; ++ struct ath11k_fw_stats *stats; ++ int rssi_num; ++ bool chain_rssi_done; ++}; ++ + static const struct wmi_tlv_policy wmi_tlv_policies[] = { + [WMI_TAG_ARRAY_BYTE] + = { .min_len = 0 }, +@@ -132,6 +140,8 @@ static const struct wmi_tlv_policy wmi_t + .min_len = sizeof(struct wmi_obss_color_collision_event) }, + [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { + .min_len = sizeof(struct wmi_11d_new_cc_ev) }, ++ [WMI_TAG_PER_CHAIN_RSSI_STATS] = { ++ .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, + }; + + #define PRIMAP(_hw_mode_) \ +@@ -5554,37 +5564,89 @@ ath11k_wmi_pull_bcn_stats(const struct w + dst->tx_bcn_outage_cnt = src->tx_bcn_outage_cnt; + } + +-int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, +- struct ath11k_fw_stats *stats) +-{ +- const void **tb; +- const struct wmi_stats_event *ev; +- const void *data; +- int i, ret; +- u32 len = skb->len; ++static int ath11k_wmi_tlv_rssi_chain_parse(struct ath11k_base *ab, ++ u16 tag, u16 len, ++ const void *ptr, void *data) ++{ ++ struct wmi_tlv_fw_stats_parse *parse = data; ++ const struct wmi_stats_event *ev = parse->ev; ++ struct ath11k_fw_stats *stats = parse->stats; ++ struct ath11k *ar; ++ struct ath11k_vif *arvif; ++ struct ieee80211_sta *sta; ++ struct ath11k_sta *arsta; ++ const struct wmi_rssi_stats *stats_rssi = (const struct wmi_rssi_stats *)ptr; ++ int j, ret = 0; + +- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC); +- if (IS_ERR(tb)) { +- ret = PTR_ERR(tb); +- ath11k_warn(ab, "failed to parse tlv: %d\n", ret); +- return ret; ++ if (tag != WMI_TAG_RSSI_STATS) ++ return -EPROTO; ++ ++ rcu_read_lock(); ++ ++ ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); ++ stats->stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT; ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wmi stats vdev id %d mac %pM\n", ++ stats_rssi->vdev_id, stats_rssi->peer_macaddr.addr); ++ ++ arvif = ath11k_mac_get_arvif(ar, stats_rssi->vdev_id); ++ if (!arvif) { ++ ath11k_warn(ab, "not found vif for vdev id %d\n", ++ stats_rssi->vdev_id); ++ ret = -EPROTO; ++ goto exit; + } + +- ev = tb[WMI_TAG_STATS_EVENT]; +- data = tb[WMI_TAG_ARRAY_BYTE]; +- if (!ev || !data) { ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wmi stats bssid %pM vif %pK\n", ++ arvif->bssid, arvif->vif); ++ ++ sta = ieee80211_find_sta_by_ifaddr(ar->hw, ++ arvif->bssid, ++ NULL); ++ if (!sta) { ++ ath11k_warn(ab, "not found station for bssid %pM\n", ++ arvif->bssid); ++ ret = -EPROTO; ++ goto exit; ++ } ++ ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ ++ BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > ++ ARRAY_SIZE(stats_rssi->rssi_avg_beacon)); ++ ++ for (j = 0; j < ARRAY_SIZE(arsta->chain_signal); j++) { ++ arsta->chain_signal[j] = stats_rssi->rssi_avg_beacon[j]; ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wmi stats beacon rssi[%d] %d data rssi[%d] %d\n", ++ j, ++ stats_rssi->rssi_avg_beacon[j], ++ j, ++ stats_rssi->rssi_avg_data[j]); ++ } ++ ++exit: ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static int ath11k_wmi_tlv_fw_stats_data_parse(struct ath11k_base *ab, ++ struct wmi_tlv_fw_stats_parse *parse, ++ const void *ptr, ++ u16 len) ++{ ++ struct ath11k_fw_stats *stats = parse->stats; ++ const struct wmi_stats_event *ev = parse->ev; ++ int i; ++ const void *data = ptr; ++ ++ if (!ev) { + ath11k_warn(ab, "failed to fetch update stats ev"); +- kfree(tb); + return -EPROTO; + } + +- ath11k_dbg(ab, ATH11K_DBG_WMI, +- "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n", +- ev->pdev_id, +- ev->num_pdev_stats, ev->num_vdev_stats, +- ev->num_bcn_stats); +- +- stats->pdev_id = ev->pdev_id; + stats->stats_id = 0; + + for (i = 0; i < ev->num_pdev_stats; i++) { +@@ -5592,10 +5654,8 @@ int ath11k_wmi_pull_fw_stats(struct ath1 + struct ath11k_fw_stats_pdev *dst; + + src = data; +- if (len < sizeof(*src)) { +- kfree(tb); ++ if (len < sizeof(*src)) + return -EPROTO; +- } + + stats->stats_id = WMI_REQUEST_PDEV_STAT; + +@@ -5617,10 +5677,8 @@ int ath11k_wmi_pull_fw_stats(struct ath1 + struct ath11k_fw_stats_vdev *dst; + + src = data; +- if (len < sizeof(*src)) { +- kfree(tb); ++ if (len < sizeof(*src)) + return -EPROTO; +- } + + stats->stats_id = WMI_REQUEST_VDEV_STAT; + +@@ -5640,10 +5698,8 @@ int ath11k_wmi_pull_fw_stats(struct ath1 + struct ath11k_fw_stats_bcn *dst; + + src = data; +- if (len < sizeof(*src)) { +- kfree(tb); ++ if (len < sizeof(*src)) + return -EPROTO; +- } + + stats->stats_id = WMI_REQUEST_BCN_STAT; + +@@ -5658,10 +5714,67 @@ int ath11k_wmi_pull_fw_stats(struct ath1 + list_add_tail(&dst->list, &stats->bcn); + } + +- kfree(tb); + return 0; + } + ++static int ath11k_wmi_tlv_fw_stats_parse(struct ath11k_base *ab, ++ u16 tag, u16 len, ++ const void *ptr, void *data) ++{ ++ struct wmi_tlv_fw_stats_parse *parse = data; ++ int ret = 0; ++ ++ switch (tag) { ++ case WMI_TAG_STATS_EVENT: ++ parse->ev = (struct wmi_stats_event *)ptr; ++ parse->stats->pdev_id = parse->ev->pdev_id; ++ break; ++ case WMI_TAG_ARRAY_BYTE: ++ ret = ath11k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); ++ break; ++ case WMI_TAG_PER_CHAIN_RSSI_STATS: ++ parse->rssi = (struct wmi_per_chain_rssi_stats *)ptr; ++ ++ if (parse->ev->stats_id & WMI_REQUEST_RSSI_PER_CHAIN_STAT) ++ parse->rssi_num = parse->rssi->num_per_chain_rssi_stats; ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wmi stats id 0x%x num chain %d\n", ++ parse->ev->stats_id, ++ parse->rssi_num); ++ break; ++ case WMI_TAG_ARRAY_STRUCT: ++ if (parse->rssi_num && !parse->chain_rssi_done) { ++ ret = ath11k_wmi_tlv_iter(ab, ptr, len, ++ ath11k_wmi_tlv_rssi_chain_parse, ++ parse); ++ if (ret) { ++ ath11k_warn(ab, "failed to parse rssi chain %d\n", ++ ret); ++ return ret; ++ } ++ parse->chain_rssi_done = true; ++ } ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, ++ struct ath11k_fw_stats *stats) ++{ ++ struct wmi_tlv_fw_stats_parse parse = { }; ++ ++ stats->stats_id = 0; ++ parse.stats = stats; ++ ++ return ath11k_wmi_tlv_iter(ab, skb->data, skb->len, ++ ath11k_wmi_tlv_fw_stats_parse, ++ &parse); ++} ++ + size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head) + { + struct ath11k_fw_stats_vdev *i; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -4439,6 +4439,17 @@ struct wmi_stats_event { + u32 num_peer_extd2_stats; + } __packed; + ++struct wmi_rssi_stats { ++ u32 vdev_id; ++ u32 rssi_avg_beacon[WMI_MAX_CHAINS]; ++ u32 rssi_avg_data[WMI_MAX_CHAINS]; ++ struct wmi_mac_addr peer_macaddr; ++} __packed; ++ ++struct wmi_per_chain_rssi_stats { ++ u32 num_per_chain_rssi_stats; ++} __packed; ++ + struct wmi_pdev_ctl_failsafe_chk_event { + u32 pdev_id; + u32 ctl_failsafe_status; diff --git a/package/kernel/mac80211/patches/ath11k/0145-ath11k-add-signal-report-to-mac80211-for-QCA6390-and.patch b/package/kernel/mac80211/patches/ath11k/0145-ath11k-add-signal-report-to-mac80211-for-QCA6390-and.patch new file mode 100644 index 00000000000000..f06c7313f40178 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0145-ath11k-add-signal-report-to-mac80211-for-QCA6390-and.patch @@ -0,0 +1,170 @@ +From c3b39553fc7712a9621a19d9670d6f250943d50e Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Fri, 17 Dec 2021 20:27:21 +0200 +Subject: [PATCH] ath11k: add signal report to mac80211 for QCA6390 and WCN6855 + +IEEE80211_HW_USES_RSS is set in ath11k, then the device uses RSS and +thus requires parallel RX which implies using per-CPU station statistics +in sta_get_last_rx_stats() of mac80211. Currently signal is only set in +ath11k_mgmt_rx_event(), and not set for RX data packet, then it show +signal as 0 for iw command easily. + +Change to get signal from firmware and report to mac80211. + +For QCA6390 and WCN6855, the rssi value is already in dbm unit, so +don't need to convert it again. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211216070535.31732-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 22 ++++++++++-- + drivers/net/wireless/ath/ath11k/wmi.c | 49 +++++++++++++++++++++----- + 3 files changed, 61 insertions(+), 11 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -387,6 +387,7 @@ struct ath11k_sta { + u64 rx_duration; + u64 tx_duration; + u8 rssi_comb; ++ s8 rssi_beacon; + s8 chain_signal[IEEE80211_MAX_CHAINS]; + struct ath11k_htt_tx_stats *tx_stats; + struct ath11k_rx_peer_stats *rx_stats; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7811,6 +7811,9 @@ static void ath11k_mac_op_sta_statistics + { + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; ++ s8 signal; ++ bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, ++ ar->ab->wmi_ab.svc_map); + + sinfo->rx_duration = arsta->rx_duration; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); +@@ -7843,9 +7846,22 @@ static void ath11k_mac_op_sta_statistics + ath11k_mac_put_chain_rssi(sinfo, arsta, "fw stats", true); + } + +- /* TODO: Use real NF instead of default one. */ +- sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; +- sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); ++ signal = arsta->rssi_comb; ++ if (!signal && ++ arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA && ++ ar->ab->hw_params.supports_rssi_stats && ++ !(ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0, ++ WMI_REQUEST_VDEV_STAT))) ++ signal = arsta->rssi_beacon; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "mac sta statistics db2dbm %u rssi comb %d rssi beacon %d\n", ++ db2dbm, arsta->rssi_comb, arsta->rssi_beacon); ++ ++ if (signal) { ++ sinfo->signal = db2dbm ? signal : signal + ATH11K_DEFAULT_NOISE_FLOOR; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); ++ } + } + + static const struct ieee80211_ops ath11k_ops = { +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -5639,7 +5639,11 @@ static int ath11k_wmi_tlv_fw_stats_data_ + { + struct ath11k_fw_stats *stats = parse->stats; + const struct wmi_stats_event *ev = parse->ev; +- int i; ++ struct ath11k *ar; ++ struct ath11k_vif *arvif; ++ struct ieee80211_sta *sta; ++ struct ath11k_sta *arsta; ++ int i, ret = 0; + const void *data = ptr; + + if (!ev) { +@@ -5649,13 +5653,19 @@ static int ath11k_wmi_tlv_fw_stats_data_ + + stats->stats_id = 0; + ++ rcu_read_lock(); ++ ++ ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); ++ + for (i = 0; i < ev->num_pdev_stats; i++) { + const struct wmi_pdev_stats *src; + struct ath11k_fw_stats_pdev *dst; + + src = data; +- if (len < sizeof(*src)) +- return -EPROTO; ++ if (len < sizeof(*src)) { ++ ret = -EPROTO; ++ goto exit; ++ } + + stats->stats_id = WMI_REQUEST_PDEV_STAT; + +@@ -5677,11 +5687,30 @@ static int ath11k_wmi_tlv_fw_stats_data_ + struct ath11k_fw_stats_vdev *dst; + + src = data; +- if (len < sizeof(*src)) +- return -EPROTO; ++ if (len < sizeof(*src)) { ++ ret = -EPROTO; ++ goto exit; ++ } + + stats->stats_id = WMI_REQUEST_VDEV_STAT; + ++ arvif = ath11k_mac_get_arvif(ar, src->vdev_id); ++ if (arvif) { ++ sta = ieee80211_find_sta_by_ifaddr(ar->hw, ++ arvif->bssid, ++ NULL); ++ if (sta) { ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ arsta->rssi_beacon = src->beacon_snr; ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wmi stats vdev id %d snr %d\n", ++ src->vdev_id, src->beacon_snr); ++ } else { ++ ath11k_warn(ab, "not found station for bssid %pM\n", ++ arvif->bssid); ++ } ++ } ++ + data += sizeof(*src); + len -= sizeof(*src); + +@@ -5698,8 +5727,10 @@ static int ath11k_wmi_tlv_fw_stats_data_ + struct ath11k_fw_stats_bcn *dst; + + src = data; +- if (len < sizeof(*src)) +- return -EPROTO; ++ if (len < sizeof(*src)) { ++ ret = -EPROTO; ++ goto exit; ++ } + + stats->stats_id = WMI_REQUEST_BCN_STAT; + +@@ -5714,7 +5745,9 @@ static int ath11k_wmi_tlv_fw_stats_data_ + list_add_tail(&dst->list, &stats->bcn); + } + +- return 0; ++exit: ++ rcu_read_unlock(); ++ return ret; + } + + static int ath11k_wmi_tlv_fw_stats_parse(struct ath11k_base *ab, diff --git a/package/kernel/mac80211/patches/ath11k/0146-ath11k-fix-warning-of-RCU-usage-for-ath11k_mac_get_a.patch b/package/kernel/mac80211/patches/ath11k/0146-ath11k-fix-warning-of-RCU-usage-for-ath11k_mac_get_a.patch new file mode 100644 index 00000000000000..3e94f2fa7dc90c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0146-ath11k-fix-warning-of-RCU-usage-for-ath11k_mac_get_a.patch @@ -0,0 +1,124 @@ +From 01e782c891083f1847c0b62902bfe3c2812566c6 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Fri, 17 Dec 2021 20:27:21 +0200 +Subject: [PATCH] ath11k: fix warning of RCU usage for + ath11k_mac_get_arvif_by_vdev_id() + +When enable more debug config, it happen below warning. It is because +the caller does not add rcu_read_lock()/rcu_read_unlock() to wrap the +rcu_dereference(). + +Add rcu_read_lock()/rcu_read_unlock() to wrap rcu_dereference(), then +fixed it. + +[ 180.716604] ============================= +[ 180.716670] WARNING: suspicious RCU usage +[ 180.716734] 5.16.0-rc4-wt-ath+ #542 Not tainted +[ 180.716895] ----------------------------- +[ 180.716957] drivers/net/wireless/ath/ath11k/mac.c:506 suspicious rcu_dereference_check() usage! +[ 180.717023] + other info that might help us debug this: + +[ 180.717087] + rcu_scheduler_active = 2, debug_locks = 1 +[ 180.717151] no locks held by swapper/0/0. +[ 180.717215] + stack backtrace: +[ 180.717279] CPU: 0 PID: 0 Comm: swapper/0 Kdump: loaded Not tainted 5.16.0-rc4-wt-ath+ #542 +[ 180.717346] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0067.2021.0528.1339 05/28/2021 +[ 180.717411] Call Trace: +[ 180.717475] +[ 180.717541] dump_stack_lvl+0x57/0x7d +[ 180.717610] ath11k_mac_get_arvif_by_vdev_id+0x1ab/0x2d0 [ath11k] +[ 180.717694] ? ath11k_mac_get_arvif+0x140/0x140 [ath11k] +[ 180.717798] ? ath11k_wmi_tlv_op_rx+0xc1b/0x2520 [ath11k] +[ 180.717888] ? kfree+0xe8/0x2c0 +[ 180.717959] ath11k_wmi_tlv_op_rx+0xc27/0x2520 [ath11k] +[ 180.718038] ? ath11k_mgmt_rx_event+0xda0/0xda0 [ath11k] +[ 180.718113] ? __lock_acquire+0xb72/0x1870 +[ 180.718182] ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370 +[ 180.718250] ? sched_clock_cpu+0x15/0x1b0 +[ 180.718314] ? find_held_lock+0x33/0x110 +[ 180.718381] ? __lock_release+0x4bd/0x9f0 +[ 180.718447] ? lock_downgrade+0x130/0x130 +[ 180.718517] ath11k_htc_rx_completion_handler+0x38f/0x5b0 [ath11k] +[ 180.718596] ? __local_bh_enable_ip+0xa0/0x110 +[ 180.718662] ath11k_ce_recv_process_cb+0x5ac/0x920 [ath11k] +[ 180.718783] ? __lock_acquired+0x205/0x890 +[ 180.718864] ? ath11k_ce_rx_post_pipe+0x970/0x970 [ath11k] +[ 180.718949] ? __wake_up_bit+0x100/0x100 +[ 180.719020] ath11k_pci_ce_tasklet+0x5f/0xf0 [ath11k_pci] +[ 180.719085] ? tasklet_clear_sched+0x42/0xe0 +[ 180.719148] tasklet_action_common.constprop.0+0x204/0x2f0 +[ 180.719217] __do_softirq+0x276/0x86a +[ 180.719281] ? __common_interrupt+0x92/0x1d0 +[ 180.719350] __irq_exit_rcu+0x11c/0x180 +[ 180.719418] irq_exit_rcu+0x5/0x20 +[ 180.719482] common_interrupt+0xa4/0xc0 +[ 180.719547] +[ 180.719609] +[ 180.719671] asm_common_interrupt+0x1e/0x40 +[ 180.719772] RIP: 0010:cpuidle_enter_state+0x1f3/0x8d0 +[ 180.719838] Code: 00 41 8b 77 04 bf ff ff ff ff e8 78 f1 ff ff 31 ff e8 81 fa 52 fe 80 7c 24 08 00 0f 85 9e 01 00 00 e8 11 13 78 fe fb 45 85 e4 <0f> 88 8c 02 00 00 49 63 ec 48 8d 44 6d 00 48 8d 44 85 00 48 8d 7c +[ 180.719909] RSP: 0018:ffffffffa4607dd0 EFLAGS: 00000202 +[ 180.719982] RAX: 00000000002aea91 RBX: ffffffffa4a5fec0 RCX: 1ffffffff49ca501 +[ 180.720047] RDX: 0000000000000000 RSI: ffffffffa3c6e4e0 RDI: ffffffffa3dcf2a0 +[ 180.720110] RBP: 0000000000000002 R08: 0000000000000001 R09: ffffffffa4e54d17 +[ 180.720173] R10: fffffbfff49ca9a2 R11: 0000000000000001 R12: 0000000000000002 +[ 180.720236] R13: ffff8881169ccc04 R14: 0000002a13899598 R15: ffff8881169ccc00 +[ 180.720321] cpuidle_enter+0x45/0xa0 +[ 180.720413] cpuidle_idle_call+0x274/0x3f0 +[ 180.720503] ? arch_cpu_idle_exit+0x30/0x30 +[ 180.720869] ? tsc_verify_tsc_adjust+0x97/0x2e0 +[ 180.720935] ? lockdep_hardirqs_off+0x90/0xd0 +[ 180.721002] do_idle+0xe0/0x150 +[ 180.721069] cpu_startup_entry+0x14/0x20 +[ 180.721134] start_kernel+0x3a2/0x3c2 +[ 180.721200] secondary_startup_64_no_verify+0xb0/0xbb +[ 180.721274] + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-02892.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211217064132.30911-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -3605,6 +3605,8 @@ ath11k_wmi_obss_color_collision_event(st + return; + } + ++ rcu_read_lock(); ++ + ev = tb[WMI_TAG_OBSS_COLOR_COLLISION_EVT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch obss color collision ev"); +@@ -3635,6 +3637,7 @@ ath11k_wmi_obss_color_collision_event(st + + exit: + kfree(tb); ++ rcu_read_unlock(); + } + + static void +@@ -6541,13 +6544,16 @@ static void ath11k_bcn_tx_status_event(s + return; + } + ++ rcu_read_lock(); + arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_id); + if (!arvif) { + ath11k_warn(ab, "invalid vdev id %d in bcn_tx_status", + vdev_id); ++ rcu_read_unlock(); + return; + } + ath11k_mac_bcn_tx_event(arvif); ++ rcu_read_unlock(); + } + + static void ath11k_vdev_stopped_event(struct ath11k_base *ab, struct sk_buff *skb) diff --git a/package/kernel/mac80211/patches/ath11k/0147-ath11k-report-tx-bitrate-for-iw-wlan-station-dump.patch b/package/kernel/mac80211/patches/ath11k/0147-ath11k-report-tx-bitrate-for-iw-wlan-station-dump.patch new file mode 100644 index 00000000000000..0b396428bb2ead --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0147-ath11k-report-tx-bitrate-for-iw-wlan-station-dump.patch @@ -0,0 +1,533 @@ +From 1b8bb94c0612cf32e418e90ae93cf37214d84669 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 20 Dec 2021 18:11:09 +0200 +Subject: [PATCH] ath11k: report tx bitrate for iw wlan station dump + +HTT_T2H_MSG_TYPE_PPDU_STATS_IND is a message which include the ppdu +info, currently it is not report from firmware for ath11k, then the +tx bitrate of "iw wlan0 station dump" always show an invalid value +"tx bitrate: 6.0 MBit/s". + +To address the issue, this is to parse the info of tx complete report +from firmware and indicate the tx rate to mac80211. + +After that, "iw wlan0 station dump" show the correct tx bit rate such +as: +tx bitrate: 78.0 MBit/s MCS 12 +tx bitrate: 144.4 MBit/s VHT-MCS 7 short GI VHT-NSS 2 +tx bitrate: 286.7 MBit/s HE-MCS 11 HE-NSS 2 HE-GI 0 HE-DCM 0 +tx bitrate: 1921.5 MBit/s 160MHz HE-MCS 9 HE-NSS 2 HE-GI 0 HE-DCM 0 + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211217093722.5739-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/debugfs_sta.c | 78 +------------ + drivers/net/wireless/ath/ath11k/debugfs_sta.h | 2 - + drivers/net/wireless/ath/ath11k/dp_rx.c | 28 +---- + drivers/net/wireless/ath/ath11k/dp_tx.c | 106 +++++++++++++++++- + drivers/net/wireless/ath/ath11k/dp_tx.h | 1 + + drivers/net/wireless/ath/ath11k/hal_rx.c | 4 +- + drivers/net/wireless/ath/ath11k/hal_rx.h | 27 ----- + drivers/net/wireless/ath/ath11k/mac.c | 93 +++++++++++++++ + drivers/net/wireless/ath/ath11k/mac.h | 3 + + 10 files changed, 210 insertions(+), 133 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -383,6 +383,7 @@ struct ath11k_sta { + struct work_struct update_wk; + struct work_struct set_4addr_wk; + struct rate_info txrate; ++ u32 peer_nss; + struct rate_info last_txrate; + u64 rx_duration; + u64 tx_duration; +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -126,85 +126,9 @@ void ath11k_debugfs_sta_add_tx_stats(str + } + + void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, +- struct sk_buff *msdu, + struct hal_tx_status *ts) + { +- struct ath11k_base *ab = ar->ab; +- struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; +- enum hal_tx_rate_stats_pkt_type pkt_type; +- enum hal_tx_rate_stats_sgi sgi; +- enum hal_tx_rate_stats_bw bw; +- struct ath11k_peer *peer; +- struct ath11k_sta *arsta; +- struct ieee80211_sta *sta; +- u16 rate; +- u8 rate_idx = 0; +- int ret; +- u8 mcs; +- +- rcu_read_lock(); +- spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, ts->peer_id); +- if (!peer || !peer->sta) { +- ath11k_warn(ab, "failed to find the peer\n"); +- spin_unlock_bh(&ab->base_lock); +- rcu_read_unlock(); +- return; +- } +- +- sta = peer->sta; +- arsta = (struct ath11k_sta *)sta->drv_priv; +- +- memset(&arsta->txrate, 0, sizeof(arsta->txrate)); +- pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, +- ts->rate_stats); +- mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, +- ts->rate_stats); +- sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, +- ts->rate_stats); +- bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats); +- +- if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || +- pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { +- ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, +- pkt_type, +- &rate_idx, +- &rate); +- if (ret < 0) +- goto err_out; +- arsta->txrate.legacy = rate; +- } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { +- if (mcs > 7) { +- ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs); +- goto err_out; +- } +- +- arsta->txrate.mcs = mcs + 8 * (arsta->last_txrate.nss - 1); +- arsta->txrate.flags = RATE_INFO_FLAGS_MCS; +- if (sgi) +- arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; +- } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { +- if (mcs > 9) { +- ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs); +- goto err_out; +- } +- +- arsta->txrate.mcs = mcs; +- arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; +- if (sgi) +- arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; +- } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { +- /* TODO */ +- } +- +- arsta->txrate.nss = arsta->last_txrate.nss; +- arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); +- +- ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); +- +-err_out: +- spin_unlock_bh(&ab->base_lock); +- rcu_read_unlock(); ++ ath11k_dp_tx_update_txcompl(ar, ts); + } + + static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file, +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.h +@@ -19,7 +19,6 @@ void ath11k_debugfs_sta_add_tx_stats(str + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx); + void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, +- struct sk_buff *msdu, + struct hal_tx_status *ts); + + #else /* CPTCFG_ATH11K_DEBUGFS */ +@@ -34,7 +33,6 @@ ath11k_debugfs_sta_add_tx_stats(struct a + } + + static inline void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, +- struct sk_buff *msdu, + struct hal_tx_status *ts) + { + } +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1360,25 +1360,6 @@ int ath11k_dp_htt_tlv_iter(struct ath11k + return 0; + } + +-static inline u32 ath11k_he_gi_to_nl80211_he_gi(u8 sgi) +-{ +- u32 ret = 0; +- +- switch (sgi) { +- case RX_MSDU_START_SGI_0_8_US: +- ret = NL80211_RATE_INFO_HE_GI_0_8; +- break; +- case RX_MSDU_START_SGI_1_6_US: +- ret = NL80211_RATE_INFO_HE_GI_1_6; +- break; +- case RX_MSDU_START_SGI_3_2_US: +- ret = NL80211_RATE_INFO_HE_GI_3_2; +- break; +- } +- +- return ret; +-} +- + static void + ath11k_update_per_peer_tx_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats, u8 user) +@@ -1497,14 +1478,15 @@ ath11k_update_per_peer_tx_stats(struct a + arsta->txrate.mcs = mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS; + arsta->txrate.he_dcm = dcm; +- arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi); +- arsta->txrate.he_ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc( +- (user_rate->ru_end - ++ arsta->txrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi); ++ arsta->txrate.he_ru_alloc = ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc ++ ((user_rate->ru_end - + user_rate->ru_start) + 1); + break; + } + + arsta->txrate.nss = nss; ++ + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); + arsta->tx_duration += tx_duration; + memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info)); +@@ -2384,7 +2366,7 @@ static void ath11k_dp_rx_h_rate(struct a + } + rx_status->encoding = RX_ENC_HE; + rx_status->nss = nss; +- rx_status->he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi); ++ rx_status->he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi); + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + break; + } +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -415,6 +415,105 @@ static void ath11k_dp_tx_cache_peer_stat + } + } + ++void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; ++ enum hal_tx_rate_stats_pkt_type pkt_type; ++ enum hal_tx_rate_stats_sgi sgi; ++ enum hal_tx_rate_stats_bw bw; ++ struct ath11k_peer *peer; ++ struct ath11k_sta *arsta; ++ struct ieee80211_sta *sta; ++ u16 rate, ru_tones; ++ u8 mcs, rate_idx, ofdma; ++ int ret; ++ ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, ts->peer_id); ++ if (!peer || !peer->sta) { ++ ath11k_dbg(ab, ATH11K_DBG_DP_TX, ++ "failed to find the peer by id %u\n", ts->peer_id); ++ goto err_out; ++ } ++ ++ sta = peer->sta; ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ ++ memset(&arsta->txrate, 0, sizeof(arsta->txrate)); ++ pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, ++ ts->rate_stats); ++ mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, ++ ts->rate_stats); ++ sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, ++ ts->rate_stats); ++ bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats); ++ ru_tones = FIELD_GET(HAL_TX_RATE_STATS_INFO0_TONES_IN_RU, ts->rate_stats); ++ ofdma = FIELD_GET(HAL_TX_RATE_STATS_INFO0_OFDMA_TX, ts->rate_stats); ++ ++ /* This is to prefer choose the real NSS value arsta->last_txrate.nss, ++ * if it is invalid, then choose the NSS value while assoc. ++ */ ++ if (arsta->last_txrate.nss) ++ arsta->txrate.nss = arsta->last_txrate.nss; ++ else ++ arsta->txrate.nss = arsta->peer_nss; ++ ++ if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || ++ pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { ++ ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, ++ pkt_type, ++ &rate_idx, ++ &rate); ++ if (ret < 0) ++ goto err_out; ++ arsta->txrate.legacy = rate; ++ } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { ++ if (mcs > 7) { ++ ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs); ++ goto err_out; ++ } ++ ++ if (arsta->txrate.nss != 0) ++ arsta->txrate.mcs = mcs + 8 * (arsta->txrate.nss - 1); ++ arsta->txrate.flags = RATE_INFO_FLAGS_MCS; ++ if (sgi) ++ arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; ++ } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { ++ if (mcs > 9) { ++ ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs); ++ goto err_out; ++ } ++ ++ arsta->txrate.mcs = mcs; ++ arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; ++ if (sgi) ++ arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; ++ } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { ++ if (mcs > 11) { ++ ath11k_warn(ab, "Invalid HE mcs index %d\n", mcs); ++ goto err_out; ++ } ++ ++ arsta->txrate.mcs = mcs; ++ arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS; ++ arsta->txrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi); ++ } ++ ++ arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); ++ if (ofdma && pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { ++ arsta->txrate.bw = RATE_INFO_BW_HE_RU; ++ arsta->txrate.he_ru_alloc = ++ ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); ++ } ++ ++ if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) ++ ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); ++ ++err_out: ++ spin_unlock_bh(&ab->base_lock); ++} ++ + static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +@@ -460,7 +559,8 @@ static void ath11k_dp_tx_complete_msdu(s + (info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + +- if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar))) { ++ if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar)) || ++ ab->hw_params.single_pdev_only) { + if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) { + if (ar->last_ppdu_id == 0) { + ar->last_ppdu_id = ts->ppdu_id; +@@ -468,12 +568,12 @@ static void ath11k_dp_tx_complete_msdu(s + ar->cached_ppdu_id == ar->last_ppdu_id) { + ar->cached_ppdu_id = ar->last_ppdu_id; + ar->cached_stats.is_ampdu = true; +- ath11k_debugfs_sta_update_txcompl(ar, msdu, ts); ++ ath11k_dp_tx_update_txcompl(ar, ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } else { + ar->cached_stats.is_ampdu = false; +- ath11k_debugfs_sta_update_txcompl(ar, msdu, ts); ++ ath11k_dp_tx_update_txcompl(ar, ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } +--- a/drivers/net/wireless/ath/ath11k/dp_tx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.h +@@ -15,6 +15,7 @@ struct ath11k_dp_htt_wbm_tx_status { + int ack_rssi; + }; + ++void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts); + int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab); + int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, + struct ath11k_sta *arsta, struct sk_buff *skb); +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1039,7 +1039,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + + ru_tones = FIELD_GET(HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION, + info0); +- ppdu_info->ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); ++ ppdu_info->ru_alloc = ++ ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(ru_tones); ++ + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -363,33 +363,6 @@ ath11k_hal_rx_parse_mon_status(struct at + struct hal_rx_mon_ppdu_info *ppdu_info, + struct sk_buff *skb); + +-static inline u32 ath11k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) +-{ +- u32 ret = 0; +- +- switch (ru_tones) { +- case RU_26: +- ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; +- break; +- case RU_52: +- ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; +- break; +- case RU_106: +- ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; +- break; +- case RU_242: +- ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; +- break; +- case RU_484: +- ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; +- break; +- case RU_996: +- ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; +- break; +- } +- return ret; +-} +- + #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0 0xDDBEEF + #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1 0xADBEEF + #define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2 0xBDBEEF +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -246,6 +246,93 @@ static const u32 ath11k_smps_map[] = { + static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + ++enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy) ++{ ++ enum nl80211_he_ru_alloc ret; ++ ++ switch (ru_phy) { ++ case RU_26: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; ++ break; ++ case RU_52: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; ++ break; ++ case RU_106: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; ++ break; ++ case RU_242: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; ++ break; ++ case RU_484: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; ++ break; ++ case RU_996: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; ++ break; ++ default: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; ++ break; ++ } ++ ++ return ret; ++} ++ ++enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) ++{ ++ enum nl80211_he_ru_alloc ret; ++ ++ switch (ru_tones) { ++ case 26: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; ++ break; ++ case 52: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; ++ break; ++ case 106: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; ++ break; ++ case 242: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; ++ break; ++ case 484: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; ++ break; ++ case 996: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; ++ break; ++ case (996 * 2): ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; ++ break; ++ default: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; ++ break; ++ } ++ ++ return ret; ++} ++ ++enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi) ++{ ++ enum nl80211_he_gi ret; ++ ++ switch (sgi) { ++ case RX_MSDU_START_SGI_0_8_US: ++ ret = NL80211_RATE_INFO_HE_GI_0_8; ++ break; ++ case RX_MSDU_START_SGI_1_6_US: ++ ret = NL80211_RATE_INFO_HE_GI_1_6; ++ break; ++ case RX_MSDU_START_SGI_3_2_US: ++ ret = NL80211_RATE_INFO_HE_GI_3_2; ++ break; ++ default: ++ ret = NL80211_RATE_INFO_HE_GI_0_8; ++ break; ++ } ++ ++ return ret; ++} ++ + u8 ath11k_mac_bw_to_mac80211_bw(u8 bw) + { + u8 ret = 0; +@@ -2541,8 +2628,12 @@ static void ath11k_peer_assoc_prepare(st + struct peer_assoc_params *arg, + bool reassoc) + { ++ struct ath11k_sta *arsta; ++ + lockdep_assert_held(&ar->conf_mutex); + ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ + memset(arg, 0, sizeof(*arg)); + + reinit_completion(&ar->peer_assoc_done); +@@ -2559,6 +2650,8 @@ static void ath11k_peer_assoc_prepare(st + ath11k_peer_assoc_h_qos(ar, vif, sta, arg); + ath11k_peer_assoc_h_smps(sta, arg); + ++ arsta->peer_nss = arg->peer_nss; ++ + /* TODO: amsdu_disable req? */ + } + +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -162,6 +162,9 @@ void ath11k_mac_drain_tx(struct ath11k * + void ath11k_mac_peer_cleanup_all(struct ath11k *ar); + int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); + u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); ++u32 ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); ++enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy); ++enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones); + enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw); + enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher); + void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb); diff --git a/package/kernel/mac80211/patches/ath11k/0148-ath11k-add-support-for-hardware-rfkill-for-QCA6390.patch b/package/kernel/mac80211/patches/ath11k/0148-ath11k-add-support-for-hardware-rfkill-for-QCA6390.patch new file mode 100644 index 00000000000000..ceb4c6638b109a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0148-ath11k-add-support-for-hardware-rfkill-for-QCA6390.patch @@ -0,0 +1,395 @@ +From ec038c6127fa772d2c5604e329f22371830d5fa6 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 20 Dec 2021 18:11:09 +0200 +Subject: [PATCH] ath11k: add support for hardware rfkill for QCA6390 + +When hardware rfkill is enabled in the firmware it will report the +capability via using WMI_SYS_CAP_INFO_RFKILL bit in the WMI_SERVICE_READY +event to the host. ath11k will check the capability, and if it is enabled then +ath11k will set the GPIO information to firmware using WMI_PDEV_SET_PARAM. When +the firmware detects hardware rfkill is enabled by the user, it will report it +via WMI_RFKILL_STATE_CHANGE_EVENTID. Once ath11k receives the event it will +send wmi command WMI_PDEV_SET_PARAM to the firmware and also notifies cfg80211. + +This only enable rfkill feature for QCA6390, rfkill_pin is all initialized to 0 +for other chips in ath11k_hw_params. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211217102334.14907-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 70 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 4 ++ + drivers/net/wireless/ath/ath11k/hw.h | 3 ++ + drivers/net/wireless/ath/ath11k/mac.c | 58 +++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/mac.h | 2 + + drivers/net/wireless/ath/ath11k/wmi.c | 41 +++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 25 +++++++++ + 7 files changed, 203 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -52,6 +52,9 @@ static const struct ath11k_hw_params ath + .target_ce_count = 11, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq8074, + .svc_to_ce_map_len = 21, ++ .rfkill_pin = 0, ++ .rfkill_cfg = 0, ++ .rfkill_on_level = 0, + .single_pdev_only = false, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, +@@ -114,6 +117,9 @@ static const struct ath11k_hw_params ath + .target_ce_count = 11, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq6018, + .svc_to_ce_map_len = 19, ++ .rfkill_pin = 0, ++ .rfkill_cfg = 0, ++ .rfkill_on_level = 0, + .single_pdev_only = false, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, +@@ -173,6 +179,9 @@ static const struct ath11k_hw_params ath + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, ++ .rfkill_pin = 48, ++ .rfkill_cfg = 0, ++ .rfkill_on_level = 1, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, +@@ -231,6 +240,9 @@ static const struct ath11k_hw_params ath + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qcn9074, + .svc_to_ce_map_len = 18, ++ .rfkill_pin = 0, ++ .rfkill_cfg = 0, ++ .rfkill_on_level = 0, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, + .rx_mac_buf_ring = false, +@@ -289,6 +301,9 @@ static const struct ath11k_hw_params ath + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, ++ .rfkill_pin = 0, ++ .rfkill_cfg = 0, ++ .rfkill_on_level = 0, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, +@@ -347,6 +362,9 @@ static const struct ath11k_hw_params ath + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, ++ .rfkill_pin = 0, ++ .rfkill_cfg = 0, ++ .rfkill_on_level = 0, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, +@@ -1015,6 +1033,27 @@ err_firmware_stop: + return ret; + } + ++static int ath11k_core_rfkill_config(struct ath11k_base *ab) ++{ ++ struct ath11k *ar; ++ int ret = 0, i; ++ ++ if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL)) ++ return 0; ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ ar = ab->pdevs[i].ar; ++ ++ ret = ath11k_mac_rfkill_config(ar); ++ if (ret && ret != -EOPNOTSUPP) { ++ ath11k_warn(ab, "failed to configure rfkill: %d", ret); ++ return ret; ++ } ++ } ++ ++ return ret; ++} ++ + int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) + { + int ret; +@@ -1061,6 +1100,13 @@ int ath11k_core_qmi_firmware_ready(struc + goto err_core_stop; + } + ath11k_hif_irq_enable(ab); ++ ++ ret = ath11k_core_rfkill_config(ab); ++ if (ret && ret != -EOPNOTSUPP) { ++ ath11k_err(ab, "failed to config rfkill: %d\n", ret); ++ goto err_core_stop; ++ } ++ + mutex_unlock(&ab->core_lock); + + return 0; +@@ -1126,6 +1172,7 @@ void ath11k_core_halt(struct ath11k *ar) + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); + cancel_work_sync(&ab->update_11d_work); ++ cancel_work_sync(&ab->rfkill_work); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); + synchronize_rcu(); +@@ -1133,6 +1180,28 @@ void ath11k_core_halt(struct ath11k *ar) + idr_init(&ar->txmgmt_idr); + } + ++static void ath11k_rfkill_work(struct work_struct *work) ++{ ++ struct ath11k_base *ab = container_of(work, struct ath11k_base, rfkill_work); ++ struct ath11k *ar; ++ bool rfkill_radio_on; ++ int i; ++ ++ spin_lock_bh(&ab->base_lock); ++ rfkill_radio_on = ab->rfkill_radio_on; ++ spin_unlock_bh(&ab->base_lock); ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ ar = ab->pdevs[i].ar; ++ if (!ar) ++ continue; ++ ++ /* notify cfg80211 radio state change */ ++ ath11k_mac_rfkill_enable_radio(ar, rfkill_radio_on); ++ wiphy_rfkill_set_hw_state(ar->hw->wiphy, !rfkill_radio_on); ++ } ++} ++ + static void ath11k_update_11d(struct work_struct *work) + { + struct ath11k_base *ab = container_of(work, struct ath11k_base, update_11d_work); +@@ -1339,6 +1408,7 @@ struct ath11k_base *ath11k_core_alloc(st + init_waitqueue_head(&ab->qmi.cold_boot_waitq); + INIT_WORK(&ab->restart_work, ath11k_core_restart); + INIT_WORK(&ab->update_11d_work, ath11k_update_11d); ++ INIT_WORK(&ab->rfkill_work, ath11k_rfkill_work); + timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); + init_completion(&ab->htc_suspend); + init_completion(&ab->wow.wakeup_completed); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -786,6 +786,10 @@ struct ath11k_base { + + struct ath11k_dbring_cap *db_caps; + u32 num_db_cap; ++ struct work_struct rfkill_work; ++ ++ /* true means radio is on */ ++ bool rfkill_radio_on; + + /* To synchronize 11d scan vdev id */ + struct mutex vdev_id_11d_lock; +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -151,6 +151,9 @@ struct ath11k_hw_params { + u32 svc_to_ce_map_len; + + bool single_pdev_only; ++ u32 rfkill_pin; ++ u32 rfkill_cfg; ++ u32 rfkill_on_level; + + bool rxdma1_enable; + int num_rxmda_per_pdev; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5571,6 +5571,63 @@ static int ath11k_mac_mgmt_tx(struct ath + return 0; + } + ++int ath11k_mac_rfkill_config(struct ath11k *ar) ++{ ++ struct ath11k_base *ab = ar->ab; ++ u32 param; ++ int ret; ++ ++ if (ab->hw_params.rfkill_pin == 0) ++ return -EOPNOTSUPP; ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, ++ "mac rfkill_pin %d rfkill_cfg %d rfkill_on_level %d", ++ ab->hw_params.rfkill_pin, ab->hw_params.rfkill_cfg, ++ ab->hw_params.rfkill_on_level); ++ ++ param = FIELD_PREP(WMI_RFKILL_CFG_RADIO_LEVEL, ++ ab->hw_params.rfkill_on_level) | ++ FIELD_PREP(WMI_RFKILL_CFG_GPIO_PIN_NUM, ++ ab->hw_params.rfkill_pin) | ++ FIELD_PREP(WMI_RFKILL_CFG_PIN_AS_GPIO, ++ ab->hw_params.rfkill_cfg); ++ ++ ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_HW_RFKILL_CONFIG, ++ param, ar->pdev->pdev_id); ++ if (ret) { ++ ath11k_warn(ab, ++ "failed to set rfkill config 0x%x: %d\n", ++ param, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int ath11k_mac_rfkill_enable_radio(struct ath11k *ar, bool enable) ++{ ++ enum wmi_rfkill_enable_radio param; ++ int ret; ++ ++ if (enable) ++ param = WMI_RFKILL_ENABLE_RADIO_ON; ++ else ++ param = WMI_RFKILL_ENABLE_RADIO_OFF; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac %d rfkill enable %d", ++ ar->pdev_idx, param); ++ ++ ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_RFKILL_ENABLE, ++ param, ar->pdev->pdev_id); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set rfkill enable param %d: %d\n", ++ param, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ + static void ath11k_mac_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +@@ -5803,6 +5860,7 @@ static void ath11k_mac_op_stop(struct ie + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); + cancel_work_sync(&ar->ab->update_11d_work); ++ cancel_work_sync(&ar->ab->rfkill_work); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -147,6 +147,8 @@ u8 ath11k_mac_hw_rate_to_idx(const struc + + void __ath11k_mac_scan_finish(struct ath11k *ar); + void ath11k_mac_scan_finish(struct ath11k *ar); ++int ath11k_mac_rfkill_enable_radio(struct ath11k *ar, bool enable); ++int ath11k_mac_rfkill_config(struct ath11k *ar); + + struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id); + struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab, +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -128,6 +128,8 @@ static const struct wmi_tlv_policy wmi_t + = { .min_len = sizeof(struct wmi_peer_assoc_conf_event) }, + [WMI_TAG_STATS_EVENT] + = { .min_len = sizeof(struct wmi_stats_event) }, ++ [WMI_TAG_RFKILL_EVENT] = { ++ .min_len = sizeof(struct wmi_rfkill_state_change_ev) }, + [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT] + = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) }, + [WMI_TAG_HOST_SWFDA_EVENT] = { +@@ -524,6 +526,8 @@ static int ath11k_pull_service_ready_tlv + cap->default_dbs_hw_mode_index = ev->default_dbs_hw_mode_index; + cap->num_msdu_desc = ev->num_msdu_desc; + ++ ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi sys cap info 0x%x\n", cap->sys_cap_info); ++ + return 0; + } + +@@ -7334,6 +7338,40 @@ exit: + kfree(tb); + } + ++static void ath11k_rfkill_state_change_event(struct ath11k_base *ab, ++ struct sk_buff *skb) ++{ ++ const struct wmi_rfkill_state_change_ev *ev; ++ const void **tb; ++ int ret; ++ ++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); ++ if (IS_ERR(tb)) { ++ ret = PTR_ERR(tb); ++ ath11k_warn(ab, "failed to parse tlv: %d\n", ret); ++ return; ++ } ++ ++ ev = tb[WMI_TAG_RFKILL_EVENT]; ++ if (!ev) { ++ kfree(tb); ++ return; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, ++ "wmi tlv rfkill state change gpio %d type %d radio_state %d\n", ++ ev->gpio_pin_num, ++ ev->int_type, ++ ev->radio_state); ++ ++ spin_lock_bh(&ab->base_lock); ++ ab->rfkill_radio_on = (ev->radio_state == WMI_RFKILL_RADIO_STATE_ON); ++ spin_unlock_bh(&ab->base_lock); ++ ++ queue_work(ab->workqueue, &ab->rfkill_work); ++ kfree(tb); ++} ++ + static void + ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab, + struct sk_buff *skb) +@@ -7606,6 +7644,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_11D_NEW_COUNTRY_EVENTID: + ath11k_reg_11d_new_cc_event(ab, skb); + break; ++ case WMI_RFKILL_STATE_CHANGE_EVENTID: ++ ath11k_rfkill_state_change_event(ab, skb); ++ break; + /* TODO: Add remaining events */ + default: + ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id); +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5215,6 +5215,31 @@ struct target_resource_config { + u32 twt_ap_sta_count; + }; + ++enum wmi_sys_cap_info_flags { ++ WMI_SYS_CAP_INFO_RXTX_LED = BIT(0), ++ WMI_SYS_CAP_INFO_RFKILL = BIT(1), ++}; ++ ++#define WMI_RFKILL_CFG_GPIO_PIN_NUM GENMASK(5, 0) ++#define WMI_RFKILL_CFG_RADIO_LEVEL BIT(6) ++#define WMI_RFKILL_CFG_PIN_AS_GPIO GENMASK(10, 7) ++ ++enum wmi_rfkill_enable_radio { ++ WMI_RFKILL_ENABLE_RADIO_ON = 0, ++ WMI_RFKILL_ENABLE_RADIO_OFF = 1, ++}; ++ ++enum wmi_rfkill_radio_state { ++ WMI_RFKILL_RADIO_STATE_OFF = 1, ++ WMI_RFKILL_RADIO_STATE_ON = 2, ++}; ++ ++struct wmi_rfkill_state_change_ev { ++ u32 gpio_pin_num; ++ u32 int_type; ++ u32 radio_state; ++} __packed; ++ + #define WMI_MAX_MEM_REQS 32 + + #define MAX_RADIOS 3 diff --git a/package/kernel/mac80211/patches/ath11k/0149-ath11k-add-regdb.bin-download-for-regdb-offload.patch b/package/kernel/mac80211/patches/ath11k/0149-ath11k-add-regdb.bin-download-for-regdb-offload.patch new file mode 100644 index 00000000000000..a53892acab29e5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0149-ath11k-add-regdb.bin-download-for-regdb-offload.patch @@ -0,0 +1,256 @@ +From 01417e57939faffebfdeb2aef1f4388e95cf9271 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 20 Dec 2021 01:23:55 -0500 +Subject: [PATCH] ath11k: add regdb.bin download for regdb offload + +The regdomain is self-managed type for ath11k, the regdomain info is +reported from firmware, it is not from wireless regdb. Firmware fetch +the regdomain info from board data file before. Currently most of the +regdomain info has moved to another file regdb.bin from board data +file for some chips such as QCA6390 and WCN6855, so the regdomain info +left in board data file is not enough to support the feature which need +more regdomain info. + +After download regdb.bin, firmware will fetch the regdomain info from +regdb.bin instead of board data file and report to ath11k. If it does +not have the file regdb.bin, it also can initialize wlan success and +firmware then fetch regdomain info from board data file. + +Add download the regdb.bin before download board data for some specific +chip which support supports_regdb in hardware parameters. + +download regdb.bin log: +[430082.334162] ath11k_pci 0000:05:00.0: chip_id 0x2 chip_family 0xb board_id 0x106 soc_id 0x400c0200 +[430082.334169] ath11k_pci 0000:05:00.0: fw_version 0x110c8b4c fw_build_timestamp 2021-10-25 07:41 fw_build_id QC_IMAGE_VERSION_STRING=WLAN.HSP.1.1-02892-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 +[430082.334414] ath11k_pci 0000:05:00.0: boot firmware request ath11k/WCN6855/hw2.0/regdb.bin size 24310 + +output of "iw reg get" +global +country US: DFS-FCC + (2402 - 2472 @ 40), (N/A, 30), (N/A) + (5170 - 5250 @ 80), (N/A, 23), (N/A), AUTO-BW + (5250 - 5330 @ 80), (N/A, 23), (0 ms), DFS, AUTO-BW + (5490 - 5730 @ 160), (N/A, 23), (0 ms), DFS + (5735 - 5835 @ 80), (N/A, 30), (N/A) + (57240 - 63720 @ 2160), (N/A, 40), (N/A) + +phy#0 (self-managed) +country US: DFS-FCC + (2402 - 2472 @ 40), (6, 30), (N/A) + (5170 - 5250 @ 80), (N/A, 24), (N/A), AUTO-BW + (5250 - 5330 @ 80), (N/A, 24), (0 ms), DFS, AUTO-BW + (5490 - 5730 @ 160), (N/A, 24), (0 ms), DFS, AUTO-BW + (5735 - 5895 @ 160), (N/A, 30), (N/A), AUTO-BW + (5945 - 7125 @ 160), (N/A, 24), (N/A), NO-OUTDOOR, AUTO-BW + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211220062355.17021-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 28 ++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/core.h | 4 ++++ + drivers/net/wireless/ath/ath11k/hw.h | 2 ++ + drivers/net/wireless/ath/ath11k/qmi.c | 30 ++++++++++++++++++-------- + drivers/net/wireless/ath/ath11k/qmi.h | 1 + + 5 files changed, 52 insertions(+), 13 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -87,6 +87,7 @@ static const struct ath11k_hw_params ath + .num_peers = 512, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), ++ .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, +@@ -149,6 +150,7 @@ static const struct ath11k_hw_params ath + .num_peers = 512, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), ++ .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, +@@ -210,6 +212,7 @@ static const struct ath11k_hw_params ath + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), ++ .supports_regdb = true, + .fix_l1ss = true, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, +@@ -271,6 +274,7 @@ static const struct ath11k_hw_params ath + .num_peers = 128, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), ++ .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, +@@ -332,6 +336,7 @@ static const struct ath11k_hw_params ath + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), ++ .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, +@@ -392,6 +397,7 @@ static const struct ath11k_hw_params ath + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), ++ .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, +@@ -760,10 +766,12 @@ err: + return ret; + } + +-static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, +- struct ath11k_board_data *bd) ++int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, ++ struct ath11k_board_data *bd, ++ const char *name) + { +- bd->fw = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_BOARD_FILE); ++ bd->fw = ath11k_core_firmware_request(ab, name); ++ + if (IS_ERR(bd->fw)) + return PTR_ERR(bd->fw); + +@@ -791,7 +799,7 @@ int ath11k_core_fetch_bdf(struct ath11k_ + goto success; + + ab->bd_api = 1; +- ret = ath11k_core_fetch_board_data_api_1(ab, bd); ++ ret = ath11k_core_fetch_board_data_api_1(ab, bd, ATH11K_DEFAULT_BOARD_FILE); + if (ret) { + ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", + ab->hw_params.fw.dir); +@@ -803,6 +811,18 @@ success: + return 0; + } + ++int ath11k_core_fetch_regdb(struct ath11k_base *ab, struct ath11k_board_data *bd) ++{ ++ int ret; ++ ++ ret = ath11k_core_fetch_board_data_api_1(ab, bd, ATH11K_REGDB_FILE_NAME); ++ if (ret) ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "failed to fetch %s from %s\n", ++ ATH11K_REGDB_FILE_NAME, ab->hw_params.fw.dir); ++ ++ return ret; ++} ++ + static void ath11k_core_stop(struct ath11k_base *ab) + { + if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -980,6 +980,10 @@ struct ath11k_base *ath11k_core_alloc(st + void ath11k_core_free(struct ath11k_base *ath11k); + int ath11k_core_fetch_bdf(struct ath11k_base *ath11k, + struct ath11k_board_data *bd); ++int ath11k_core_fetch_regdb(struct ath11k_base *ab, struct ath11k_board_data *bd); ++int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, ++ struct ath11k_board_data *bd, ++ const char *name); + void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); + int ath11k_core_check_dt(struct ath11k_base *ath11k); + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -77,6 +77,7 @@ + #define ATH11K_DEFAULT_CAL_FILE "caldata.bin" + #define ATH11K_AMSS_FILE "amss.bin" + #define ATH11K_M3_FILE "m3.bin" ++#define ATH11K_REGDB_FILE_NAME "regdb.bin" + + enum ath11k_hw_rate_cck { + ATH11K_HW_RATE_CCK_LP_11M = 0, +@@ -181,6 +182,7 @@ struct ath11k_hw_params { + u32 num_peers; + bool supports_suspend; + u32 hal_desc_sz; ++ bool supports_regdb; + bool fix_l1ss; + bool credit_flow; + u8 max_tx_ring; +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2161,7 +2161,8 @@ err_free_req: + return ret; + } + +-static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) ++static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab, ++ bool regdb) + { + struct device *dev = ab->dev; + char filename[ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE]; +@@ -2172,13 +2173,21 @@ static int ath11k_qmi_load_bdf_qmi(struc + const u8 *tmp; + + memset(&bd, 0, sizeof(bd)); +- ret = ath11k_core_fetch_bdf(ab, &bd); +- if (ret) { +- ath11k_warn(ab, "qmi failed to fetch board file: %d\n", ret); +- goto out; ++ ++ if (regdb) { ++ ret = ath11k_core_fetch_regdb(ab, &bd); ++ } else { ++ ret = ath11k_core_fetch_bdf(ab, &bd); ++ if (ret) ++ ath11k_warn(ab, "qmi failed to fetch board file: %d\n", ret); + } + +- if (bd.len >= SELFMAG && memcmp(bd.data, ELFMAG, SELFMAG) == 0) ++ if (ret) ++ goto out; ++ ++ if (regdb) ++ bdf_type = ATH11K_QMI_BDF_TYPE_REGDB; ++ else if (bd.len >= SELFMAG && memcmp(bd.data, ELFMAG, SELFMAG) == 0) + bdf_type = ATH11K_QMI_BDF_TYPE_ELF; + else + bdf_type = ATH11K_QMI_BDF_TYPE_BIN; +@@ -2193,8 +2202,8 @@ static int ath11k_qmi_load_bdf_qmi(struc + goto out; + } + +- /* QCA6390 does not support cal data, skip it */ +- if (bdf_type == ATH11K_QMI_BDF_TYPE_ELF) ++ /* QCA6390/WCN6855 does not support cal data, skip it */ ++ if (bdf_type == ATH11K_QMI_BDF_TYPE_ELF || bdf_type == ATH11K_QMI_BDF_TYPE_REGDB) + goto out; + + if (ab->qmi.target.eeprom_caldata) { +@@ -2626,7 +2635,10 @@ static int ath11k_qmi_event_load_bdf(str + return ret; + } + +- ret = ath11k_qmi_load_bdf_qmi(ab); ++ if (ab->hw_params.supports_regdb) ++ ath11k_qmi_load_bdf_qmi(ab, true); ++ ++ ret = ath11k_qmi_load_bdf_qmi(ab, false); + if (ret < 0) { + ath11k_warn(ab, "failed to load board data file: %d\n", ret); + return ret; +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -48,6 +48,7 @@ enum ath11k_qmi_file_type { + enum ath11k_qmi_bdf_type { + ATH11K_QMI_BDF_TYPE_BIN = 0, + ATH11K_QMI_BDF_TYPE_ELF = 1, ++ ATH11K_QMI_BDF_TYPE_REGDB = 4, + }; + + enum ath11k_qmi_event_type { diff --git a/package/kernel/mac80211/patches/ath11k/0151-ath11k-add-support-of-firmware-logging-for-WCN6855.patch b/package/kernel/mac80211/patches/ath11k/0151-ath11k-add-support-of-firmware-logging-for-WCN6855.patch new file mode 100644 index 00000000000000..61378616b2c752 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0151-ath11k-add-support-of-firmware-logging-for-WCN6855.patch @@ -0,0 +1,295 @@ +From 42da1cc7bd537ea63ddebf88aedcdafcccb00131 Mon Sep 17 00:00:00 2001 +From: Cheng Wang +Date: Mon, 20 Dec 2021 20:10:53 +0800 +Subject: [PATCH] ath11k: add support of firmware logging for WCN6855 + +Host enables WMI firmware logging feature via QMI message. +Host receives firmware logging messages on WMI_DIAG_EVENTID, then +sends logging messages to user space via event tracing infrastructure. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Cheng Wang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211220121053.357087-1-quic_chengwan@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 6 ++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/qmi.c | 94 +++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/qmi.h | 12 ++++ + drivers/net/wireless/ath/ath11k/trace.h | 28 ++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 10 +++ + 6 files changed, 151 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -96,6 +96,7 @@ static const struct ath11k_hw_params ath + .alloc_cacheable_memory = true, + .wakeup_mhi = false, + .supports_rssi_stats = false, ++ .fw_wmi_diag_event = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -159,6 +160,7 @@ static const struct ath11k_hw_params ath + .alloc_cacheable_memory = true, + .wakeup_mhi = false, + .supports_rssi_stats = false, ++ .fw_wmi_diag_event = false, + }, + { + .name = "qca6390 hw2.0", +@@ -221,6 +223,7 @@ static const struct ath11k_hw_params ath + .alloc_cacheable_memory = false, + .wakeup_mhi = true, + .supports_rssi_stats = true, ++ .fw_wmi_diag_event = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -283,6 +286,7 @@ static const struct ath11k_hw_params ath + .alloc_cacheable_memory = true, + .wakeup_mhi = false, + .supports_rssi_stats = false, ++ .fw_wmi_diag_event = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -345,6 +349,7 @@ static const struct ath11k_hw_params ath + .alloc_cacheable_memory = false, + .wakeup_mhi = true, + .supports_rssi_stats = true, ++ .fw_wmi_diag_event = true, + }, + { + .name = "wcn6855 hw2.1", +@@ -406,6 +411,7 @@ static const struct ath11k_hw_params ath + .alloc_cacheable_memory = false, + .wakeup_mhi = true, + .supports_rssi_stats = true, ++ .fw_wmi_diag_event = true, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -191,6 +191,7 @@ struct ath11k_hw_params { + bool alloc_cacheable_memory; + bool wakeup_mhi; + bool supports_rssi_stats; ++ bool fw_wmi_diag_event; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1584,6 +1584,50 @@ static struct qmi_elem_info qmi_wlanfw_c + }, + }; + ++static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { ++ { ++ .data_type = QMI_OPT_FLAG, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x10, ++ .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, ++ enablefwlog_valid), ++ }, ++ { ++ .data_type = QMI_UNSIGNED_1_BYTE, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x10, ++ .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, ++ enablefwlog), ++ }, ++ { ++ .data_type = QMI_EOTI, ++ .array_type = NO_ARRAY, ++ .tlv_type = QMI_COMMON_TLV_TYPE, ++ }, ++}; ++ ++static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { ++ { ++ .data_type = QMI_STRUCT, ++ .elem_len = 1, ++ .elem_size = sizeof(struct qmi_response_type_v01), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x02, ++ .offset = offsetof(struct qmi_wlanfw_wlan_ini_resp_msg_v01, ++ resp), ++ .ei_array = qmi_response_type_v01_ei, ++ }, ++ { ++ .data_type = QMI_EOTI, ++ .array_type = NO_ARRAY, ++ .tlv_type = QMI_COMMON_TLV_TYPE, ++ }, ++}; ++ + static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) + { + struct qmi_wlanfw_host_cap_req_msg_v01 req; +@@ -2504,6 +2548,48 @@ out: + return ret; + } + ++static int ath11k_qmi_wlanfw_wlan_ini_send(struct ath11k_base *ab, bool enable) ++{ ++ int ret; ++ struct qmi_txn txn; ++ struct qmi_wlanfw_wlan_ini_req_msg_v01 req = {}; ++ struct qmi_wlanfw_wlan_ini_resp_msg_v01 resp = {}; ++ ++ req.enablefwlog_valid = true; ++ req.enablefwlog = enable ? 1 : 0; ++ ++ ret = qmi_txn_init(&ab->qmi.handle, &txn, ++ qmi_wlanfw_wlan_ini_resp_msg_v01_ei, &resp); ++ if (ret < 0) ++ goto out; ++ ++ ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, ++ QMI_WLANFW_WLAN_INI_REQ_V01, ++ QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN, ++ qmi_wlanfw_wlan_ini_req_msg_v01_ei, &req); ++ if (ret < 0) { ++ ath11k_warn(ab, "qmi failed to send wlan ini request, err = %d\n", ++ ret); ++ qmi_txn_cancel(&txn); ++ goto out; ++ } ++ ++ ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); ++ if (ret < 0) { ++ ath11k_warn(ab, "qmi failed wlan ini request, err = %d\n", ret); ++ goto out; ++ } ++ ++ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ++ ath11k_warn(ab, "qmi wlan ini request failed, result: %d, err: %d\n", ++ resp.resp.result, resp.resp.error); ++ ret = -EINVAL; ++ } ++ ++out: ++ return ret; ++} ++ + void ath11k_qmi_firmware_stop(struct ath11k_base *ab) + { + int ret; +@@ -2524,6 +2610,14 @@ int ath11k_qmi_firmware_start(struct ath + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware start\n"); + ++ if (ab->hw_params.fw_wmi_diag_event) { ++ ret = ath11k_qmi_wlanfw_wlan_ini_send(ab, true); ++ if (ret < 0) { ++ ath11k_warn(ab, "qmi failed to send wlan fw ini:%d\n", ret); ++ return ret; ++ } ++ } ++ + ret = ath11k_qmi_wlanfw_wlan_cfg_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan cfg: %d\n", ret); +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -428,10 +428,12 @@ struct qmi_wlanfw_m3_info_resp_msg_v01 { + #define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7 + #define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803 + #define QMI_WLANFW_WLAN_CFG_RESP_MSG_V01_MAX_LEN 7 ++#define QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN 4 + #define QMI_WLANFW_WLAN_MODE_REQ_V01 0x0022 + #define QMI_WLANFW_WLAN_MODE_RESP_V01 0x0022 + #define QMI_WLANFW_WLAN_CFG_REQ_V01 0x0023 + #define QMI_WLANFW_WLAN_CFG_RESP_V01 0x0023 ++#define QMI_WLANFW_WLAN_INI_REQ_V01 0x002F + #define QMI_WLANFW_MAX_STR_LEN_V01 16 + #define QMI_WLANFW_MAX_NUM_CE_V01 12 + #define QMI_WLANFW_MAX_NUM_SVC_V01 24 +@@ -473,6 +475,16 @@ struct qmi_wlanfw_wlan_cfg_resp_msg_v01 + struct qmi_response_type_v01 resp; + }; + ++struct qmi_wlanfw_wlan_ini_req_msg_v01 { ++ /* Must be set to true if enablefwlog is being passed */ ++ u8 enablefwlog_valid; ++ u8 enablefwlog; ++}; ++ ++struct qmi_wlanfw_wlan_ini_resp_msg_v01 { ++ struct qmi_response_type_v01 resp; ++}; ++ + int ath11k_qmi_firmware_start(struct ath11k_base *ab, + u32 mode); + void ath11k_qmi_firmware_stop(struct ath11k_base *ab); +--- a/drivers/net/wireless/ath/ath11k/trace.h ++++ b/drivers/net/wireless/ath/ath11k/trace.h +@@ -280,6 +280,34 @@ TRACE_EVENT(ath11k_log_dbg_dump, + __get_str(msg) + ) + ); ++ ++TRACE_EVENT(ath11k_wmi_diag, ++ TP_PROTO(struct ath11k_base *ab, const void *data, size_t len), ++ ++ TP_ARGS(ab, data, len), ++ ++ TP_STRUCT__entry( ++ __string(device, dev_name(ab->dev)) ++ __string(driver, dev_driver_string(ab->dev)) ++ __field(u16, len) ++ __dynamic_array(u8, data, len) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(device, dev_name(ab->dev)); ++ __assign_str(driver, dev_driver_string(ab->dev)); ++ __entry->len = len; ++ memcpy(__get_dynamic_array(data), data, len); ++ ), ++ ++ TP_printk( ++ "%s %s tlv diag len %d", ++ __get_str(driver), ++ __get_str(device), ++ __entry->len ++ ) ++); ++ + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ + + /* we don't want to use include/trace/events */ +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7525,6 +7525,13 @@ static void ath11k_wmi_event_wow_wakeup_ + complete(&ab->wow.wakeup_completed); + } + ++static void ++ath11k_wmi_diag_event(struct ath11k_base *ab, ++ struct sk_buff *skb) ++{ ++ trace_ath11k_wmi_diag(ab, skb->data, skb->len); ++} ++ + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) + { + struct wmi_cmd_hdr *cmd_hdr; +@@ -7647,6 +7654,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_RFKILL_STATE_CHANGE_EVENTID: + ath11k_rfkill_state_change_event(ab, skb); + break; ++ case WMI_DIAG_EVENTID: ++ ath11k_wmi_diag_event(ab, skb); ++ break; + /* TODO: Add remaining events */ + default: + ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id); diff --git a/package/kernel/mac80211/patches/ath11k/0152-ath11k-Fix-unexpected-return-buffer-manager-error-fo.patch b/package/kernel/mac80211/patches/ath11k/0152-ath11k-Fix-unexpected-return-buffer-manager-error-fo.patch new file mode 100644 index 00000000000000..0e12b0519c467b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0152-ath11k-Fix-unexpected-return-buffer-manager-error-fo.patch @@ -0,0 +1,58 @@ +From 71c748b5e01e3e28838a8e26a8966fb5adb03df7 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Wed, 22 Dec 2021 09:35:35 +0800 +Subject: [PATCH] ath11k: Fix unexpected return buffer manager error for + QCA6390 + +We are seeing below error on QCA6390: +... +[70211.671189] ath11k_pci 0000:72:00.0: failed to parse rx error in wbm_rel ring desc -22 +[70212.696154] ath11k_pci 0000:72:00.0: failed to parse rx error in wbm_rel ring desc -22 +[70213.092941] ath11k_pci 0000:72:00.0: failed to parse rx error in wbm_rel ring desc -22 +... + +The reason is that, with commit 734223d78428 ("ath11k: change return +buffer manager for QCA6390"), ath11k expects the return buffer manager +(RBM) field of descriptor configured as HAL_RX_BUF_RBM_SW1_BM when +parsing error frames from WBM2SW3_RELEASE ring. This is a wrong change +cause the RBM field is set as HAL_RX_BUF_RBM_SW3_BM. + +The same issue also applies to REO2TCL ring though we have not got any +error reported. + +Fix it by changing RBM from HAL_RX_BUF_RBM_SW1_BM to HAL_RX_BUF_RBM_SW3_BM +for these two rings. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Fixes: 734223d78428 ("ath11k: change return buffer manager for QCA6390") +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211222013536.582527-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +- + drivers/net/wireless/ath/ath11k/hal_rx.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -3837,7 +3837,7 @@ int ath11k_dp_process_rx_err(struct ath1 + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, msdu_cookies, + &rbm); + if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && +- rbm != ab->hw_params.hal_params->rx_buf_rbm) { ++ rbm != HAL_RX_BUF_RBM_SW3_BM) { + ab->soc_stats.invalid_rbm++; + ath11k_warn(ab, "invalid return buffer manager %d\n", rbm); + ath11k_dp_rx_link_desc_return(ab, desc, +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -374,7 +374,7 @@ int ath11k_hal_wbm_desc_parse_err(struct + + ret_buf_mgr = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + wbm_desc->buf_addr_info.info1); +- if (ret_buf_mgr != ab->hw_params.hal_params->rx_buf_rbm) { ++ if (ret_buf_mgr != HAL_RX_BUF_RBM_SW3_BM) { + ab->soc_stats.invalid_rbm++; + return -EINVAL; + } diff --git a/package/kernel/mac80211/patches/ath11k/0153-codel-remove-unnecessary-sock.h-include.patch b/package/kernel/mac80211/patches/ath11k/0153-codel-remove-unnecessary-sock.h-include.patch new file mode 100644 index 00000000000000..cbe5476bb8ef5d --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0153-codel-remove-unnecessary-sock.h-include.patch @@ -0,0 +1,41 @@ +From 15fcb1031178f2a42425c2993b2ec7bb894c04d6 Mon Sep 17 00:00:00 2001 +From: Jakub Kicinski +Date: Tue, 21 Dec 2021 11:39:40 -0800 +Subject: [PATCH] codel: remove unnecessary sock.h include + +Since sock.h is modified relatively often (60 times in the last +12 months) it seems worthwhile to decrease the incremental build +work. + +CoDel's header includes net/inet_ecn.h which in turn includes net/sock.h. +codel.h is itself included by mac80211 which is included by much of +the WiFi stack and drivers. Removing the net/inet_ecn.h include from +CoDel breaks the dependecy between WiFi and sock.h. + +Commit d068ca2ae2e6 ("codel: split into multiple files") moved all +the code which actually needs ECN helpers out to net/codel_impl.h, +the include can be moved there as well. + +This decreases the incremental build size after touching sock.h +from 4999 objects to 4051 objects. + +Fix unmasked missing includes in WiFi drivers. + +Acked-by: Kalle Valo +Link: https://lore.kernel.org/r/20211221193941.3805147-1-kuba@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/wireless/ath/ath11k/debugfs.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -3,6 +3,8 @@ + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + */ + ++#include ++ + #include "debugfs.h" + + #include "core.h" diff --git a/package/kernel/mac80211/patches/ath11k/0154-codel-remove-unnecessary-pkt_sched.h-include.patch b/package/kernel/mac80211/patches/ath11k/0154-codel-remove-unnecessary-pkt_sched.h-include.patch new file mode 100644 index 00000000000000..bb833ba7a1b9e7 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0154-codel-remove-unnecessary-pkt_sched.h-include.patch @@ -0,0 +1,37 @@ +From e6e5904455815626b711c7d48cacd253f4d72f84 Mon Sep 17 00:00:00 2001 +From: Jakub Kicinski +Date: Tue, 21 Dec 2021 11:39:41 -0800 +Subject: [PATCH] codel: remove unnecessary pkt_sched.h include + +Commit d068ca2ae2e6 ("codel: split into multiple files") moved all +Qdisc-related code to codel_qdisc.h, move the include of pkt_sched.h +as well. + +This is similar to the previous commit, although we don't care as +much about incremental builds after pkt_sched.h was touched itself +it is included by net/sch_generic.h which is modified ~20 times +a year. + +This decreases the incremental build size after touching pkt_sched.h +from 1592 to 617 objects. + +Fix unmasked missing includes in WiFi drivers. + +Acked-by: Kalle Valo +Link: https://lore.kernel.org/r/20211221193941.3805147-2-kuba@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/wireless/ath/ath11k/reg.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -2,6 +2,8 @@ + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ ++#include ++ + #include "core.h" + #include "debug.h" + diff --git a/package/kernel/mac80211/patches/ath11k/0155-ath11k-add-missing-of_node_put-to-avoid-leak.patch b/package/kernel/mac80211/patches/ath11k/0155-ath11k-add-missing-of_node_put-to-avoid-leak.patch new file mode 100644 index 00000000000000..5e348f23eea677 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0155-ath11k-add-missing-of_node_put-to-avoid-leak.patch @@ -0,0 +1,39 @@ +From 3d38faef0de1756994b3d95e47b2302842f729e2 Mon Sep 17 00:00:00 2001 +From: Yang Yingliang +Date: Mon, 10 Jan 2022 16:24:13 +0200 +Subject: [PATCH] ath11k: add missing of_node_put() to avoid leak + +The node pointer is returned by of_find_node_by_type() +or of_parse_phandle() with refcount incremented. Calling +of_node_put() to aovid the refcount leak. + +Fixes: 6ac04bdc5edb ("ath11k: Use reserved host DDR addresses from DT for PCI devices") +Reported-by: Hulk Robot +Signed-off-by: Yang Yingliang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211221114003.335557-1-yangyingliang@huawei.com +--- + drivers/net/wireless/ath/ath11k/mhi.c | 1 + + drivers/net/wireless/ath/ath11k/qmi.c | 1 + + 2 files changed, 2 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -332,6 +332,7 @@ static int ath11k_mhi_read_addr_from_dt( + return -ENOENT; + + ret = of_address_to_resource(np, 0, &res); ++ of_node_put(np); + if (ret) + return ret; + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1936,6 +1936,7 @@ static int ath11k_qmi_assign_target_mem_ + } + + ret = of_address_to_resource(hremote_node, 0, &res); ++ of_node_put(hremote_node); + if (ret) { + ath11k_dbg(ab, ATH11K_DBG_QMI, + "qmi fail to get reg from hremote\n"); diff --git a/package/kernel/mac80211/patches/ath11k/0156-ath11k-fix-workqueue-not-getting-destroyed-after-rmm.patch b/package/kernel/mac80211/patches/ath11k/0156-ath11k-fix-workqueue-not-getting-destroyed-after-rmm.patch new file mode 100644 index 00000000000000..42a87109559658 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0156-ath11k-fix-workqueue-not-getting-destroyed-after-rmm.patch @@ -0,0 +1,43 @@ +From 9f4ecacf2fa47b8aadd9bca2e88cde01856de028 Mon Sep 17 00:00:00 2001 +From: Aditya Kumar Singh +Date: Mon, 10 Jan 2022 16:24:14 +0200 +Subject: [PATCH] ath11k: fix workqueue not getting destroyed after rmmod + +Currently, ath11k_core_alloc() creates a single thread workqueue. +This workqueue is not detroyed during clean up when ath11k modules +are unloaded from the kernel and is left as it is. +If workqueue is not destroyed, it could lead to kernel +memory scarcity in a longer run. This could affect self and +other drivers workability as well. + +Add destroy workqueue in ath11k_core_free(). + +Tested on: IPQ8074 WLAN.HK.2.4.0.1-01746-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Aditya Kumar Singh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1640231787-17408-1-git-send-email-quic_adisi@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -1404,6 +1405,9 @@ EXPORT_SYMBOL(ath11k_core_deinit); + + void ath11k_core_free(struct ath11k_base *ab) + { ++ flush_workqueue(ab->workqueue); ++ destroy_workqueue(ab->workqueue); ++ + kfree(ab); + } + EXPORT_SYMBOL(ath11k_core_free); diff --git a/package/kernel/mac80211/patches/ath11k/0157-ath11k-Refactor-the-fallback-routine-when-peer-creat.patch b/package/kernel/mac80211/patches/ath11k/0157-ath11k-Refactor-the-fallback-routine-when-peer-creat.patch new file mode 100644 index 00000000000000..16c9cf3f3df5fb --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0157-ath11k-Refactor-the-fallback-routine-when-peer-creat.patch @@ -0,0 +1,82 @@ +From fbed57d897f6ea065c45806959337a6f28d2a94d Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Mon, 10 Jan 2022 16:24:14 +0200 +Subject: [PATCH] ath11k: Refactor the fallback routine when peer create fails + +When there is an error in peer create process from +ath11k_peer_find(), the code attempts to handle a fallback +for peer create. When this fallback fails, the driver returns +the fallback return code rather than actual error code +(-ENOENT). So refactor the fallback routine to return +the actual error code. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01067-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1640244819-21183-1-git-send-email-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/peer.c | 40 +++++++++++++++----------- + 1 file changed, 23 insertions(+), 17 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -252,7 +252,7 @@ int ath11k_peer_create(struct ath11k *ar + { + struct ath11k_peer *peer; + struct ath11k_sta *arsta; +- int ret; ++ int ret, fbret; + + lockdep_assert_held(&ar->conf_mutex); + +@@ -291,22 +291,8 @@ int ath11k_peer_create(struct ath11k *ar + ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", + param->peer_addr, param->vdev_id); + +- reinit_completion(&ar->peer_delete_done); +- +- ret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, +- param->vdev_id); +- if (ret) { +- ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", +- param->vdev_id, param->peer_addr); +- return ret; +- } +- +- ret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, +- param->peer_addr); +- if (ret) +- return ret; +- +- return -ENOENT; ++ ret = -ENOENT; ++ goto cleanup; + } + + peer->pdev_idx = ar->pdev_idx; +@@ -335,4 +321,24 @@ int ath11k_peer_create(struct ath11k *ar + spin_unlock_bh(&ar->ab->base_lock); + + return 0; ++ ++cleanup: ++ reinit_completion(&ar->peer_delete_done); ++ ++ fbret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, ++ param->vdev_id); ++ if (fbret) { ++ ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", ++ param->vdev_id, param->peer_addr); ++ goto exit; ++ } ++ ++ fbret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, ++ param->peer_addr); ++ if (fbret) ++ ath11k_warn(ar->ab, "failed wait for peer %pM delete done id %d fallback ret %d\n", ++ param->peer_addr, param->vdev_id, fbret); ++ ++exit: ++ return ret; + } diff --git a/package/kernel/mac80211/patches/ath11k/0158-ath11k-add-LDPC-FEC-type-in-802.11-radiotap-header.patch b/package/kernel/mac80211/patches/ath11k/0158-ath11k-add-LDPC-FEC-type-in-802.11-radiotap-header.patch new file mode 100644 index 00000000000000..572a9e2c6f2b95 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0158-ath11k-add-LDPC-FEC-type-in-802.11-radiotap-header.patch @@ -0,0 +1,126 @@ +From b3febdccde3eb086b892485c923cd6b73b2ce9d7 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Tue, 11 Jan 2022 16:42:52 +0200 +Subject: [PATCH] ath11k: add LDPC FEC type in 802.11 radiotap header + +LDPC is one the FEC type advertised in msdu_start info2 for HT packet +type. Hence, add hardware specific callback for fetching LDPC +support from msdu start and enable RX_ENC_FLAG_LDPC flag while passing +rx status to mac80211. + +Tested-on: IPQ8074 WLAN.HK.2.4.0.1-01467-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1638294648-844-3-git-send-email-quic_ppranees@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 12 +++++++++++- + drivers/net/wireless/ath/ath11k/hw.c | 16 ++++++++++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 3 files changed, 28 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -43,6 +43,13 @@ static inline u8 ath11k_dp_rx_h_msdu_sta + } + + static inline ++bool ath11k_dp_rx_h_msdu_start_ldpc_support(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) ++{ ++ return ab->hw_params.hw_ops->rx_desc_get_ldpc_support(desc); ++} ++ ++static inline + u8 ath11k_dp_rx_h_msdu_start_mesh_ctl_present(struct ath11k_base *ab, + struct hal_rx_desc *desc) + { +@@ -2313,7 +2320,7 @@ static void ath11k_dp_rx_h_rate(struct a + u8 bw; + u8 rate_mcs, nss; + u8 sgi; +- bool is_cck; ++ bool is_cck, is_ldpc; + + pkt_type = ath11k_dp_rx_h_msdu_start_pkt_type(ar->ab, rx_desc); + bw = ath11k_dp_rx_h_msdu_start_rx_bw(ar->ab, rx_desc); +@@ -2355,6 +2362,9 @@ static void ath11k_dp_rx_h_rate(struct a + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); ++ is_ldpc = ath11k_dp_rx_h_msdu_start_ldpc_support(ar->ab, rx_desc); ++ if (is_ldpc) ++ rx_status->enc_flags |= RX_ENC_FLAG_LDPC; + break; + case RX_MSDU_START_PKT_TYPE_11AX: + rx_status->rate_idx = rate_mcs; +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -273,6 +273,12 @@ static u8 ath11k_hw_ipq8074_rx_desc_get_ + __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); + } + ++static bool ath11k_hw_ipq8074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) ++{ ++ return FIELD_GET(RX_MSDU_START_INFO2_LDPC, ++ __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); ++} ++ + static bool ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) + { + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID, +@@ -444,6 +450,12 @@ static u8 ath11k_hw_qcn9074_rx_desc_get_ + __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); + } + ++static bool ath11k_hw_qcn9074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) ++{ ++ return FIELD_GET(RX_MSDU_START_INFO2_LDPC, ++ __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); ++} ++ + static bool ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) + { + return !!FIELD_GET(RX_MPDU_START_INFO11_MPDU_SEQ_CTRL_VALID, +@@ -815,6 +827,7 @@ const struct ath11k_hw_ops ipq8074_ops = + .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, +@@ -853,6 +866,7 @@ const struct ath11k_hw_ops ipq6018_ops = + .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, +@@ -891,6 +905,7 @@ const struct ath11k_hw_ops qca6390_ops = + .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath11k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, +@@ -929,6 +944,7 @@ const struct ath11k_hw_ops qcn9074_ops = + .rx_desc_get_encrypt_type = ath11k_hw_qcn9074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_qcn9074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_qcn9074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ldpc_support = ath11k_hw_qcn9074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath11k_hw_qcn9074_rx_desc_get_mpdu_start_seq_no, +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -210,6 +210,7 @@ struct ath11k_hw_ops { + u32 (*rx_desc_get_encrypt_type)(struct hal_rx_desc *desc); + u8 (*rx_desc_get_decap_type)(struct hal_rx_desc *desc); + u8 (*rx_desc_get_mesh_ctl)(struct hal_rx_desc *desc); ++ bool (*rx_desc_get_ldpc_support)(struct hal_rx_desc *desc); + bool (*rx_desc_get_mpdu_seq_ctl_vld)(struct hal_rx_desc *desc); + bool (*rx_desc_get_mpdu_fc_valid)(struct hal_rx_desc *desc); + u16 (*rx_desc_get_mpdu_start_seq_no)(struct hal_rx_desc *desc); diff --git a/package/kernel/mac80211/patches/ath11k/0159-ath11k-free-peer-for-station-when-disconnect-from-AP.patch b/package/kernel/mac80211/patches/ath11k/0159-ath11k-free-peer-for-station-when-disconnect-from-AP.patch new file mode 100644 index 00000000000000..6bfe0a821c2089 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0159-ath11k-free-peer-for-station-when-disconnect-from-AP.patch @@ -0,0 +1,244 @@ +From 212ad7cb7d7592669c067125949e0a8e31ce6a0b Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 11 Jan 2022 16:42:52 +0200 +Subject: [PATCH] ath11k: free peer for station when disconnect from AP for + QCA6390/WCN6855 + +Commit b4a0f54156ac ("ath11k: move peer delete after vdev stop of station +for QCA6390 and WCN6855") is to fix firmware crash by changing the WMI +command sequence, but actually skip all the peer delete operation, then +it lead commit 58595c9874c6 ("ath11k: Fixing dangling pointer issue upon +peer delete failure") not take effect, and then happened a use-after-free +warning from KASAN. because the peer->sta is not set to NULL and then used +later. + +Change to only skip the WMI_PEER_DELETE_CMDID for QCA6390/WCN6855. + +log of user-after-free: + +[ 534.888665] BUG: KASAN: use-after-free in ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] +[ 534.888696] Read of size 8 at addr ffff8881396bb1b8 by task rtcwake/2860 + +[ 534.888705] CPU: 4 PID: 2860 Comm: rtcwake Kdump: loaded Tainted: G W 5.15.0-wt-ath+ #523 +[ 534.888712] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0067.2021.0528.1339 05/28/2021 +[ 534.888716] Call Trace: +[ 534.888720] +[ 534.888726] dump_stack_lvl+0x57/0x7d +[ 534.888736] print_address_description.constprop.0+0x1f/0x170 +[ 534.888745] ? ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] +[ 534.888771] kasan_report.cold+0x83/0xdf +[ 534.888783] ? ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] +[ 534.888810] ath11k_dp_rx_update_peer_stats+0x912/0xc10 [ath11k] +[ 534.888840] ath11k_dp_rx_process_mon_status+0x529/0xa70 [ath11k] +[ 534.888874] ? ath11k_dp_rx_mon_status_bufs_replenish+0x3f0/0x3f0 [ath11k] +[ 534.888897] ? check_prev_add+0x20f0/0x20f0 +[ 534.888922] ? __lock_acquire+0xb72/0x1870 +[ 534.888937] ? find_held_lock+0x33/0x110 +[ 534.888954] ath11k_dp_rx_process_mon_rings+0x297/0x520 [ath11k] +[ 534.888981] ? rcu_read_unlock+0x40/0x40 +[ 534.888990] ? ath11k_dp_rx_pdev_alloc+0xd90/0xd90 [ath11k] +[ 534.889026] ath11k_dp_service_mon_ring+0x67/0xe0 [ath11k] +[ 534.889053] ? ath11k_dp_rx_process_mon_rings+0x520/0x520 [ath11k] +[ 534.889075] call_timer_fn+0x167/0x4a0 +[ 534.889084] ? add_timer_on+0x3b0/0x3b0 +[ 534.889103] ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370 +[ 534.889117] __run_timers.part.0+0x539/0x8b0 +[ 534.889123] ? ath11k_dp_rx_process_mon_rings+0x520/0x520 [ath11k] +[ 534.889157] ? call_timer_fn+0x4a0/0x4a0 +[ 534.889164] ? mark_lock_irq+0x1c30/0x1c30 +[ 534.889173] ? clockevents_program_event+0xdd/0x280 +[ 534.889189] ? mark_held_locks+0xa5/0xe0 +[ 534.889203] run_timer_softirq+0x97/0x180 +[ 534.889213] __do_softirq+0x276/0x86a +[ 534.889230] __irq_exit_rcu+0x11c/0x180 +[ 534.889238] irq_exit_rcu+0x5/0x20 +[ 534.889244] sysvec_apic_timer_interrupt+0x8e/0xc0 +[ 534.889251] +[ 534.889254] +[ 534.889259] asm_sysvec_apic_timer_interrupt+0x12/0x20 +[ 534.889265] RIP: 0010:_raw_spin_unlock_irqrestore+0x38/0x70 +[ 534.889271] Code: 74 24 10 e8 ea c2 bf fd 48 89 ef e8 12 53 c0 fd 81 e3 00 02 00 00 75 25 9c 58 f6 c4 02 75 2d 48 85 db 74 01 fb bf 01 00 00 00 13 a7 b5 fd 65 8b 05 cc d9 9c 5e 85 c0 74 0a 5b 5d c3 e8 a0 ee +[ 534.889276] RSP: 0018:ffffc90002e5f880 EFLAGS: 00000206 +[ 534.889284] RAX: 0000000000000006 RBX: 0000000000000200 RCX: ffffffff9f256f10 +[ 534.889289] RDX: 0000000000000000 RSI: ffffffffa1c6e420 RDI: 0000000000000001 +[ 534.889293] RBP: ffff8881095e6200 R08: 0000000000000001 R09: ffffffffa40d2b8f +[ 534.889298] R10: fffffbfff481a571 R11: 0000000000000001 R12: ffff8881095e6e68 +[ 534.889302] R13: ffffc90002e5f908 R14: 0000000000000246 R15: 0000000000000000 +[ 534.889316] ? mark_lock+0xd0/0x14a0 +[ 534.889332] klist_next+0x1d4/0x450 +[ 534.889340] ? dpm_wait_for_subordinate+0x2d0/0x2d0 +[ 534.889350] device_for_each_child+0xa8/0x140 +[ 534.889360] ? device_remove_class_symlinks+0x1b0/0x1b0 +[ 534.889370] ? __lock_release+0x4bd/0x9f0 +[ 534.889378] ? dpm_suspend+0x26b/0x3f0 +[ 534.889390] dpm_wait_for_subordinate+0x82/0x2d0 +[ 534.889400] ? dpm_for_each_dev+0xa0/0xa0 +[ 534.889410] ? dpm_suspend+0x233/0x3f0 +[ 534.889427] __device_suspend+0xd4/0x10c0 +[ 534.889440] ? wait_for_completion_io+0x270/0x270 +[ 534.889456] ? async_suspend_late+0xe0/0xe0 +[ 534.889463] ? async_schedule_node_domain+0x468/0x640 +[ 534.889482] dpm_suspend+0x25a/0x3f0 +[ 534.889491] ? dpm_suspend_end+0x1a0/0x1a0 +[ 534.889497] ? ktime_get+0x214/0x2f0 +[ 534.889502] ? lockdep_hardirqs_on+0x79/0x100 +[ 534.889509] ? recalibrate_cpu_khz+0x10/0x10 +[ 534.889516] ? ktime_get+0x119/0x2f0 +[ 534.889528] dpm_suspend_start+0xab/0xc0 +[ 534.889538] suspend_devices_and_enter+0x1ca/0x350 +[ 534.889546] ? suspend_enter+0x850/0x850 +[ 534.889566] enter_state+0x27c/0x3d7 +[ 534.889575] pm_suspend.cold+0x42/0x189 +[ 534.889583] state_store+0xab/0x160 +[ 534.889595] ? sysfs_file_ops+0x160/0x160 +[ 534.889601] kernfs_fop_write_iter+0x2b5/0x450 +[ 534.889615] new_sync_write+0x36a/0x600 +[ 534.889625] ? new_sync_read+0x600/0x600 +[ 534.889639] ? rcu_read_unlock+0x40/0x40 +[ 534.889668] vfs_write+0x619/0x910 +[ 534.889681] ksys_write+0xf4/0x1d0 +[ 534.889689] ? __ia32_sys_read+0xa0/0xa0 +[ 534.889699] ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370 +[ 534.889707] ? syscall_enter_from_user_mode+0x1d/0x50 +[ 534.889719] do_syscall_64+0x3b/0x90 +[ 534.889725] entry_SYSCALL_64_after_hwframe+0x44/0xae +[ 534.889731] RIP: 0033:0x7f0b9bc931e7 +[ 534.889736] Code: 64 89 02 48 c7 c0 ff ff ff ff eb bb 0f 1f 80 00 00 00 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 +[ 534.889741] RSP: 002b:00007ffd9d34cc88 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 +[ 534.889749] RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007f0b9bc931e7 +[ 534.889753] RDX: 0000000000000004 RSI: 0000561cd023c5f0 RDI: 0000000000000004 +[ 534.889757] RBP: 0000561cd023c5f0 R08: 0000000000000000 R09: 0000000000000004 +[ 534.889761] R10: 0000561ccef842a6 R11: 0000000000000246 R12: 0000000000000004 +[ 534.889765] R13: 0000561cd0239590 R14: 00007f0b9bd6f4a0 R15: 00007f0b9bd6e8a0 +[ 534.889789] + +[ 534.889796] Allocated by task 2711: +[ 534.889800] kasan_save_stack+0x1b/0x40 +[ 534.889805] __kasan_kmalloc+0x7c/0x90 +[ 534.889810] sta_info_alloc+0x98/0x1ef0 [mac80211] +[ 534.889874] ieee80211_prep_connection+0x30b/0x11e0 [mac80211] +[ 534.889950] ieee80211_mgd_auth+0x529/0xe00 [mac80211] +[ 534.890024] cfg80211_mlme_auth+0x332/0x6f0 [cfg80211] +[ 534.890090] nl80211_authenticate+0x839/0xcf0 [cfg80211] +[ 534.890147] genl_family_rcv_msg_doit+0x1f4/0x2f0 +[ 534.890154] genl_rcv_msg+0x280/0x500 +[ 534.890160] netlink_rcv_skb+0x11c/0x340 +[ 534.890165] genl_rcv+0x1f/0x30 +[ 534.890170] netlink_unicast+0x42b/0x700 +[ 534.890176] netlink_sendmsg+0x71b/0xc60 +[ 534.890181] sock_sendmsg+0xdf/0x110 +[ 534.890187] ____sys_sendmsg+0x5c0/0x850 +[ 534.890192] ___sys_sendmsg+0xe4/0x160 +[ 534.890197] __sys_sendmsg+0xb2/0x140 +[ 534.890202] do_syscall_64+0x3b/0x90 +[ 534.890207] entry_SYSCALL_64_after_hwframe+0x44/0xae + +[ 534.890215] Freed by task 2825: +[ 534.890218] kasan_save_stack+0x1b/0x40 +[ 534.890223] kasan_set_track+0x1c/0x30 +[ 534.890227] kasan_set_free_info+0x20/0x30 +[ 534.890232] __kasan_slab_free+0xce/0x100 +[ 534.890237] slab_free_freelist_hook+0xf0/0x1a0 +[ 534.890242] kfree+0xe5/0x370 +[ 534.890248] __sta_info_flush+0x333/0x4b0 [mac80211] +[ 534.890308] ieee80211_set_disassoc+0x324/0xd20 [mac80211] +[ 534.890382] ieee80211_mgd_deauth+0x537/0xee0 [mac80211] +[ 534.890472] cfg80211_mlme_deauth+0x349/0x810 [cfg80211] +[ 534.890526] cfg80211_mlme_down+0x1ce/0x270 [cfg80211] +[ 534.890578] cfg80211_disconnect+0x4f5/0x7b0 [cfg80211] +[ 534.890631] cfg80211_leave+0x24/0x40 [cfg80211] +[ 534.890677] wiphy_suspend+0x23d/0x2f0 [cfg80211] +[ 534.890723] dpm_run_callback+0xf4/0x1b0 +[ 534.890728] __device_suspend+0x648/0x10c0 +[ 534.890733] async_suspend+0x16/0xe0 +[ 534.890737] async_run_entry_fn+0x90/0x4f0 +[ 534.890741] process_one_work+0x866/0x1490 +[ 534.890747] worker_thread+0x596/0x1010 +[ 534.890751] kthread+0x35d/0x420 +[ 534.890756] ret_from_fork+0x22/0x30 + +[ 534.890763] The buggy address belongs to the object at ffff8881396ba000 + which belongs to the cache kmalloc-8k of size 8192 +[ 534.890767] The buggy address is located 4536 bytes inside of + 8192-byte region [ffff8881396ba000, ffff8881396bc000) +[ 534.890772] The buggy address belongs to the page: +[ 534.890775] page:ffffea0004e5ae00 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1396b8 +[ 534.890780] head:ffffea0004e5ae00 order:3 compound_mapcount:0 compound_pincount:0 +[ 534.890784] flags: 0x200000000010200(slab|head|node=0|zone=2) +[ 534.890791] raw: 0200000000010200 ffffea000562be08 ffffea0004b04c08 ffff88810004e340 +[ 534.890795] raw: 0000000000000000 0000000000010001 00000001ffffffff 0000000000000000 +[ 534.890798] page dumped because: kasan: bad access detected + +[ 534.890804] Memory state around the buggy address: +[ 534.890807] ffff8881396bb080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 534.890811] ffff8881396bb100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 534.890814] >ffff8881396bb180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 534.890817] ^ +[ 534.890821] ffff8881396bb200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 534.890824] ffff8881396bb280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 534.890827] ================================================================== +[ 534.890830] Disabling lock debugging due to kernel taint + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Fixes: b4a0f54156ac ("ath11k: move peer delete after vdev stop of station for QCA6390 and WCN6855") +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211222070431.29595-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 31 ++++++++++++++++----------- + 1 file changed, 18 insertions(+), 13 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4504,24 +4504,30 @@ static int ath11k_mac_op_sta_state(struc + sta->addr, arvif->vdev_id); + } else if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { +- ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); ++ bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && ++ vif->type == NL80211_IFTYPE_STATION; + +- if (ar->ab->hw_params.vdev_start_delay && +- vif->type == NL80211_IFTYPE_STATION) +- goto free; ++ ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + +- ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); +- if (ret) +- ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- else +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); ++ if (!skip_peer_delete) { ++ ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "Failed to delete peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ else ++ ath11k_dbg(ar->ab, ++ ATH11K_DBG_MAC, ++ "Removed peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ } + + ath11k_mac_dec_num_stations(arvif, sta); + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); +- if (peer && peer->sta == sta) { ++ if (skip_peer_delete && peer) { ++ peer->sta = NULL; ++ } else if (peer && peer->sta == sta) { + ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", + vif->addr, arvif->vdev_id); + peer->sta = NULL; +@@ -4531,7 +4537,6 @@ static int ath11k_mac_op_sta_state(struc + } + spin_unlock_bh(&ar->ab->base_lock); + +-free: + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; + diff --git a/package/kernel/mac80211/patches/ath11k/0160-ath11k-enable-RX-PPDU-stats-in-monitor-co-exist-mode.patch b/package/kernel/mac80211/patches/ath11k/0160-ath11k-enable-RX-PPDU-stats-in-monitor-co-exist-mode.patch new file mode 100644 index 00000000000000..571efbe9bced6c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0160-ath11k-enable-RX-PPDU-stats-in-monitor-co-exist-mode.patch @@ -0,0 +1,222 @@ +From 67a9d399fcb03177152a8797a855cbd4c995c2de Mon Sep 17 00:00:00 2001 +From: Miles Hu +Date: Wed, 12 Jan 2022 10:15:10 +0200 +Subject: [PATCH] ath11k: enable RX PPDU stats in monitor co-exist mode + +RX PPDU statistics collection is missing when monitor mode co-exists +with other modes. This commit combines the processing of the destination +ring with the status ring to fix the issue. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01179-QCAHKSWPL_SILICONZ-1 +Signed-off-by: Miles Hu +Signed-off-by: Aloka Dixit +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220111032224.14093-1-quic_alokad@quicinc.com +--- + drivers/net/wireless/ath/ath11k/debugfs.c | 6 ++ + drivers/net/wireless/ath/ath11k/dp_rx.c | 84 +++++++---------------- + drivers/net/wireless/ath/ath11k/hal_rx.h | 8 +-- + 3 files changed, 34 insertions(+), 64 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -666,6 +666,12 @@ static ssize_t ath11k_write_extd_rx_stat + goto exit; + } + ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) { ++ ar->debug.extd_rx_stats = enable; ++ ret = count; ++ goto exit; ++ } ++ + if (enable) { + rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -20,6 +20,9 @@ + + #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) + ++static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, ++ u32 quota, struct napi_struct *napi); ++ + static inline + u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc) + { +@@ -3097,12 +3100,14 @@ int ath11k_dp_rx_process_mon_status(stru + enum hal_rx_mon_status hal_status; + struct sk_buff *skb; + struct sk_buff_head skb_list; +- struct hal_rx_mon_ppdu_info ppdu_info; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + int num_buffs_reaped = 0; + u32 rx_buf_sz; + u16 log_type = 0; ++ struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; ++ struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; ++ struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; + + __skb_queue_head_init(&skb_list); + +@@ -3111,8 +3116,8 @@ int ath11k_dp_rx_process_mon_status(stru + if (!num_buffs_reaped) + goto exit; + +- memset(&ppdu_info, 0, sizeof(ppdu_info)); +- ppdu_info.peer_id = HAL_INVALID_PEERID; ++ memset(ppdu_info, 0, sizeof(*ppdu_info)); ++ ppdu_info->peer_id = HAL_INVALID_PEERID; + + while ((skb = __skb_dequeue(&skb_list))) { + if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { +@@ -3126,9 +3131,18 @@ int ath11k_dp_rx_process_mon_status(stru + if (log_type) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + +- hal_status = ath11k_hal_rx_parse_mon_status(ab, &ppdu_info, skb); ++ hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); + +- if (ppdu_info.peer_id == HAL_INVALID_PEERID || ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && ++ pmon->mon_ppdu_status == DP_PPDU_STATUS_START && ++ hal_status == HAL_TLV_STATUS_PPDU_DONE) { ++ rx_mon_stats->status_ppdu_done++; ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; ++ ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_START; ++ } ++ ++ if (ppdu_info->peer_id == HAL_INVALID_PEERID || + hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + dev_kfree_skb_any(skb); + continue; +@@ -3136,17 +3150,17 @@ int ath11k_dp_rx_process_mon_status(stru + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, ppdu_info.peer_id); ++ peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); + + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "failed to find the peer with peer_id %d\n", +- ppdu_info.peer_id); ++ ppdu_info->peer_id); + goto next_skb; + } + + arsta = (struct ath11k_sta *)peer->sta->drv_priv; +- ath11k_dp_rx_update_peer_stats(arsta, &ppdu_info); ++ ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); + + if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); +@@ -3156,8 +3170,8 @@ next_skb: + rcu_read_unlock(); + + dev_kfree_skb_any(skb); +- memset(&ppdu_info, 0, sizeof(ppdu_info)); +- ppdu_info.peer_id = HAL_INVALID_PEERID; ++ memset(ppdu_info, 0, sizeof(*ppdu_info)); ++ ppdu_info->peer_id = HAL_INVALID_PEERID; + } + exit: + return num_buffs_reaped; +@@ -5116,38 +5130,6 @@ static void ath11k_dp_rx_mon_dest_proces + } + } + +-static void ath11k_dp_rx_mon_status_process_tlv(struct ath11k *ar, +- int mac_id, u32 quota, +- struct napi_struct *napi) +-{ +- struct ath11k_pdev_dp *dp = &ar->dp; +- struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; +- struct hal_rx_mon_ppdu_info *ppdu_info; +- struct sk_buff *status_skb; +- u32 tlv_status = HAL_TLV_STATUS_BUF_DONE; +- struct ath11k_pdev_mon_stats *rx_mon_stats; +- +- ppdu_info = &pmon->mon_ppdu_info; +- rx_mon_stats = &pmon->rx_mon_stats; +- +- if (pmon->mon_ppdu_status != DP_PPDU_STATUS_START) +- return; +- +- while (!skb_queue_empty(&pmon->rx_status_q)) { +- status_skb = skb_dequeue(&pmon->rx_status_q); +- +- tlv_status = ath11k_hal_rx_parse_mon_status(ar->ab, ppdu_info, +- status_skb); +- if (tlv_status == HAL_TLV_STATUS_PPDU_DONE) { +- rx_mon_stats->status_ppdu_done++; +- pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; +- ath11k_dp_rx_mon_dest_process(ar, mac_id, quota, napi); +- pmon->mon_ppdu_status = DP_PPDU_STATUS_START; +- } +- dev_kfree_skb_any(status_skb); +- } +-} +- + static u32 + ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, + void *ring_entry, struct sk_buff **head_msdu, +@@ -5499,22 +5481,6 @@ reap_status_ring: + return quota; + } + +-static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, +- struct napi_struct *napi, int budget) +-{ +- struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); +- struct ath11k_pdev_dp *dp = &ar->dp; +- struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; +- int num_buffs_reaped = 0; +- +- num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ar->ab, mac_id, &budget, +- &pmon->rx_status_q); +- if (num_buffs_reaped) +- ath11k_dp_rx_mon_status_process_tlv(ar, mac_id, budget, napi); +- +- return num_buffs_reaped; +-} +- + int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) + { +@@ -5524,8 +5490,6 @@ int ath11k_dp_rx_process_mon_rings(struc + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && + ab->hw_params.full_monitor_mode) + ret = ath11k_dp_full_mon_process_rx(ab, mac_id, napi, budget); +- else if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) +- ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget); + else + ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); + +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -65,10 +65,6 @@ enum hal_rx_reception_type { + HAL_RX_RECEPTION_TYPE_MAX, + }; + +-#define HAL_TLV_STATUS_PPDU_NOT_DONE 0 +-#define HAL_TLV_STATUS_PPDU_DONE 1 +-#define HAL_TLV_STATUS_BUF_DONE 2 +-#define HAL_TLV_STATUS_PPDU_NON_STD_DONE 3 + #define HAL_RX_FCS_LEN 4 + + enum hal_rx_mon_status { +@@ -77,6 +73,10 @@ enum hal_rx_mon_status { + HAL_RX_MON_STATUS_BUF_DONE, + }; + ++#define HAL_TLV_STATUS_PPDU_NOT_DONE HAL_RX_MON_STATUS_PPDU_NOT_DONE ++#define HAL_TLV_STATUS_PPDU_DONE HAL_RX_MON_STATUS_PPDU_DONE ++#define HAL_TLV_STATUS_BUF_DONE HAL_RX_MON_STATUS_BUF_DONE ++ + struct hal_sw_mon_ring_entries { + dma_addr_t mon_dst_paddr; + dma_addr_t mon_status_paddr; diff --git a/package/kernel/mac80211/patches/ath11k/0161-ath11k-move-function-ath11k_dp_rx_process_mon_status.patch b/package/kernel/mac80211/patches/ath11k/0161-ath11k-move-function-ath11k_dp_rx_process_mon_status.patch new file mode 100644 index 00000000000000..c0a14de99520b6 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0161-ath11k-move-function-ath11k_dp_rx_process_mon_status.patch @@ -0,0 +1,210 @@ +From 3cd04a438754aaa4297a5561ad9149eda73ce14d Mon Sep 17 00:00:00 2001 +From: Aloka Dixit +Date: Wed, 12 Jan 2022 10:15:10 +0200 +Subject: [PATCH] ath11k: move function ath11k_dp_rx_process_mon_status + +Move the function below ath11k_dp_rx_mon_dest_process() and remove +the forward declaration. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01179-QCAHKSWPL_SILICONZ-1 +Signed-off-by: Aloka Dixit +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220111032224.14093-2-quic_alokad@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 171 ++++++++++++------------ + 1 file changed, 84 insertions(+), 87 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -20,9 +20,6 @@ + + #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) + +-static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, +- u32 quota, struct napi_struct *napi); +- + static inline + u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc) + { +@@ -3093,90 +3090,6 @@ move_next: + return num_buffs_reaped; + } + +-int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, +- struct napi_struct *napi, int budget) +-{ +- struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); +- enum hal_rx_mon_status hal_status; +- struct sk_buff *skb; +- struct sk_buff_head skb_list; +- struct ath11k_peer *peer; +- struct ath11k_sta *arsta; +- int num_buffs_reaped = 0; +- u32 rx_buf_sz; +- u16 log_type = 0; +- struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; +- struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; +- struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; +- +- __skb_queue_head_init(&skb_list); +- +- num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, +- &skb_list); +- if (!num_buffs_reaped) +- goto exit; +- +- memset(ppdu_info, 0, sizeof(*ppdu_info)); +- ppdu_info->peer_id = HAL_INVALID_PEERID; +- +- while ((skb = __skb_dequeue(&skb_list))) { +- if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { +- log_type = ATH11K_PKTLOG_TYPE_LITE_RX; +- rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; +- } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { +- log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; +- rx_buf_sz = DP_RX_BUFFER_SIZE; +- } +- +- if (log_type) +- trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); +- +- hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); +- +- if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && +- pmon->mon_ppdu_status == DP_PPDU_STATUS_START && +- hal_status == HAL_TLV_STATUS_PPDU_DONE) { +- rx_mon_stats->status_ppdu_done++; +- pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; +- ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); +- pmon->mon_ppdu_status = DP_PPDU_STATUS_START; +- } +- +- if (ppdu_info->peer_id == HAL_INVALID_PEERID || +- hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { +- dev_kfree_skb_any(skb); +- continue; +- } +- +- rcu_read_lock(); +- spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); +- +- if (!peer || !peer->sta) { +- ath11k_dbg(ab, ATH11K_DBG_DATA, +- "failed to find the peer with peer_id %d\n", +- ppdu_info->peer_id); +- goto next_skb; +- } +- +- arsta = (struct ath11k_sta *)peer->sta->drv_priv; +- ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); +- +- if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) +- trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); +- +-next_skb: +- spin_unlock_bh(&ab->base_lock); +- rcu_read_unlock(); +- +- dev_kfree_skb_any(skb); +- memset(ppdu_info, 0, sizeof(*ppdu_info)); +- ppdu_info->peer_id = HAL_INVALID_PEERID; +- } +-exit: +- return num_buffs_reaped; +-} +- + static void ath11k_dp_rx_frag_timer(struct timer_list *timer) + { + struct dp_rx_tid *rx_tid = from_timer(rx_tid, timer, frag_timer); +@@ -5130,6 +5043,90 @@ static void ath11k_dp_rx_mon_dest_proces + } + } + ++int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, ++ struct napi_struct *napi, int budget) ++{ ++ struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); ++ enum hal_rx_mon_status hal_status; ++ struct sk_buff *skb; ++ struct sk_buff_head skb_list; ++ struct ath11k_peer *peer; ++ struct ath11k_sta *arsta; ++ int num_buffs_reaped = 0; ++ u32 rx_buf_sz; ++ u16 log_type = 0; ++ struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; ++ struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; ++ struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; ++ ++ __skb_queue_head_init(&skb_list); ++ ++ num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, ++ &skb_list); ++ if (!num_buffs_reaped) ++ goto exit; ++ ++ memset(ppdu_info, 0, sizeof(*ppdu_info)); ++ ppdu_info->peer_id = HAL_INVALID_PEERID; ++ ++ while ((skb = __skb_dequeue(&skb_list))) { ++ if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) { ++ log_type = ATH11K_PKTLOG_TYPE_LITE_RX; ++ rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; ++ } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { ++ log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; ++ rx_buf_sz = DP_RX_BUFFER_SIZE; ++ } ++ ++ if (log_type) ++ trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); ++ ++ hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); ++ ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && ++ pmon->mon_ppdu_status == DP_PPDU_STATUS_START && ++ hal_status == HAL_TLV_STATUS_PPDU_DONE) { ++ rx_mon_stats->status_ppdu_done++; ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; ++ ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_START; ++ } ++ ++ if (ppdu_info->peer_id == HAL_INVALID_PEERID || ++ hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { ++ dev_kfree_skb_any(skb); ++ continue; ++ } ++ ++ rcu_read_lock(); ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); ++ ++ if (!peer || !peer->sta) { ++ ath11k_dbg(ab, ATH11K_DBG_DATA, ++ "failed to find the peer with peer_id %d\n", ++ ppdu_info->peer_id); ++ goto next_skb; ++ } ++ ++ arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); ++ ++ if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) ++ trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); ++ ++next_skb: ++ spin_unlock_bh(&ab->base_lock); ++ rcu_read_unlock(); ++ ++ dev_kfree_skb_any(skb); ++ memset(ppdu_info, 0, sizeof(*ppdu_info)); ++ ppdu_info->peer_id = HAL_INVALID_PEERID; ++ } ++exit: ++ return num_buffs_reaped; ++} ++ + static u32 + ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, + void *ring_entry, struct sk_buff **head_msdu, diff --git a/package/kernel/mac80211/patches/ath11k/0162-ath11k-fix-error-code-in-ath11k_qmi_assign_target_me.patch b/package/kernel/mac80211/patches/ath11k/0162-ath11k-fix-error-code-in-ath11k_qmi_assign_target_me.patch new file mode 100644 index 00000000000000..4ed157d67694f8 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0162-ath11k-fix-error-code-in-ath11k_qmi_assign_target_me.patch @@ -0,0 +1,28 @@ +From c9b41832dc080fa59bad597de94865b3ea2d5bab Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Wed, 12 Jan 2022 10:15:11 +0200 +Subject: [PATCH] ath11k: fix error code in + ath11k_qmi_assign_target_mem_chunk() + +The "ret" vairable is not set at this point. It could be uninitialized +or zero. The correct thing to return is -ENODEV. + +Fixes: 6ac04bdc5edb ("ath11k: Use reserved host DDR addresses from DT for PCI devices") +Signed-off-by: Dan Carpenter +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220111071445.GA11243@kili +--- + drivers/net/wireless/ath/ath11k/qmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1932,7 +1932,7 @@ static int ath11k_qmi_assign_target_mem_ + if (!hremote_node) { + ath11k_dbg(ab, ATH11K_DBG_QMI, + "qmi fail to get hremote_node\n"); +- return ret; ++ return -ENODEV; + } + + ret = of_address_to_resource(hremote_node, 0, &res); diff --git a/package/kernel/mac80211/patches/ath11k/0163-ath11k-Reconfigure-hardware-rate-for-WCN6855-after-v.patch b/package/kernel/mac80211/patches/ath11k/0163-ath11k-Reconfigure-hardware-rate-for-WCN6855-after-v.patch new file mode 100644 index 00000000000000..f935a261047cf1 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0163-ath11k-Reconfigure-hardware-rate-for-WCN6855-after-v.patch @@ -0,0 +1,68 @@ +From dc7ff75690ea7e1920be08b2b9cc89647667bb90 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Wed, 12 Jan 2022 10:54:00 +0800 +Subject: [PATCH] ath11k: Reconfigure hardware rate for WCN6855 after vdev is + started + +There is an issue that WCN6855 tries to connect to an AP using +a hardware rate of 1Mb/s , even though the AP has announced +expected rates as [24, 36, 48, 54] in Probe Response frame. + +The reason is that WCN6855 firmware clears hardware rate info +of management frames when vdev starts and uses 1Mb/s as default. +To solve it, reconfigure the rate after vdev is started. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220112025400.2222-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 18 ++++++++++++++++++ + 2 files changed, 19 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -603,6 +603,7 @@ struct ath11k { + struct completion finish_11d_ch_list; + bool pending_11d; + bool regdom_set_by_user; ++ int hw_rate_code; + }; + + struct ath11k_band_cap { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2862,6 +2862,11 @@ static void ath11k_recalculate_mgmt_rate + if (ret) + ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); + ++ /* For WCN6855, firmware will clear this param when vdev starts, hence ++ * cache it here so that we can reconfigure it once vdev starts. ++ */ ++ ar->hw_rate_code = hw_rate_code; ++ + vdev_param = WMI_VDEV_PARAM_BEACON_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, + hw_rate_code); +@@ -6960,6 +6965,19 @@ static int ath11k_start_vdev_delay(struc + return ret; + } + ++ /* Reconfigure hardware rate code since it is cleared by firmware. ++ */ ++ if (ar->hw_rate_code > 0) { ++ u32 vdev_param = WMI_VDEV_PARAM_MGMT_RATE; ++ ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, ++ ar->hw_rate_code); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); ++ return ret; ++ } ++ } ++ + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ret = ath11k_wmi_vdev_up(ar, arvif->vdev_id, 0, ar->mac_addr); + if (ret) { diff --git a/package/kernel/mac80211/patches/ath11k/0164-ath11k-set-WMI_PEER_40MHZ-while-peer-assoc-for-6-GHz.patch b/package/kernel/mac80211/patches/ath11k/0164-ath11k-set-WMI_PEER_40MHZ-while-peer-assoc-for-6-GHz.patch new file mode 100644 index 00000000000000..b488ef5fba2ad4 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0164-ath11k-set-WMI_PEER_40MHZ-while-peer-assoc-for-6-GHz.patch @@ -0,0 +1,31 @@ +From 1cb747192de2edb7e55920af8c458e4792908486 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 18 Jan 2022 22:42:11 -0500 +Subject: [PATCH] ath11k: set WMI_PEER_40MHZ while peer assoc for 6 GHz + +When station connect to AP of 6 GHz with 40 MHz bandwidth, the TX is +always stay 20 MHz, it is because the flag WMI_PEER_40MHZ is not set +while peer assoc. Add the flag if remote peer is 40 MHz bandwidth. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Fixes: c3a7d7eb4c98 ("ath11k: add 6 GHz params in peer assoc command") +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220119034211.28622-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2319,6 +2319,9 @@ static void ath11k_peer_assoc_h_he_6ghz( + if (!arg->he_flag || band != NL80211_BAND_6GHZ || !sta->he_6ghz_capa.capa) + return; + ++ if (sta->bandwidth == IEEE80211_STA_RX_BW_40) ++ arg->bw_40 = true; ++ + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + arg->bw_80 = true; + diff --git a/package/kernel/mac80211/patches/ath11k/0165-ath11k-avoid-firmware-crash-when-reg-set-for-QCA6390.patch b/package/kernel/mac80211/patches/ath11k/0165-ath11k-avoid-firmware-crash-when-reg-set-for-QCA6390.patch new file mode 100644 index 00000000000000..cb83d0c3d490d8 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0165-ath11k-avoid-firmware-crash-when-reg-set-for-QCA6390.patch @@ -0,0 +1,136 @@ +From 0d6e997b76216ca104167a2a0fb79823a7fa9e97 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 18 Jan 2022 23:13:55 -0500 +Subject: [PATCH] ath11k: avoid firmware crash when reg set for QCA6390/WCN6855 + +For the NL80211_REGDOM_SET_BY_USER hint from cfg80211, it set the new +alpha2 code to ath11k, then ath11k send WMI_SET_INIT_COUNTRY_CMDID to +firmware for all chips currently. When test with WCN6855/QCA6390 chips, +this WMI CMD leads firmware crash. + +For AP based chips(ipq8074/qcn9074), WMI_SET_INIT_COUNTRY_CMDID is used +to send to firmware, for STATION based chips(WCN6855/QCA6390), it need to +use another WMI CMD WMI_SET_CURRENT_COUNTRY_CMDID. + +Add flag current_cc_support in hardware parameters, it is used to +distinguish AP/STA platform. After that, the firmware will work +normal and the regulatory feature works well for QCA6390/WCN6855. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220119041355.32014-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/reg.c | 25 +++++++++++++++++-------- + 3 files changed, 24 insertions(+), 8 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -98,6 +98,7 @@ static const struct ath11k_hw_params ath + .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, ++ .current_cc_support = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -162,6 +163,7 @@ static const struct ath11k_hw_params ath + .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, ++ .current_cc_support = false, + }, + { + .name = "qca6390 hw2.0", +@@ -225,6 +227,7 @@ static const struct ath11k_hw_params ath + .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, ++ .current_cc_support = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -288,6 +291,7 @@ static const struct ath11k_hw_params ath + .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, ++ .current_cc_support = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -351,6 +355,7 @@ static const struct ath11k_hw_params ath + .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, ++ .current_cc_support = true, + }, + { + .name = "wcn6855 hw2.1", +@@ -413,6 +418,7 @@ static const struct ath11k_hw_params ath + .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, ++ .current_cc_support = true, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -192,6 +192,7 @@ struct ath11k_hw_params { + bool wakeup_mhi; + bool supports_rssi_stats; + bool fw_wmi_diag_event; ++ bool current_cc_support; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -48,6 +48,7 @@ ath11k_reg_notifier(struct wiphy *wiphy, + { + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wmi_init_country_params init_country_param; ++ struct wmi_set_current_country_params set_current_param = {}; + struct ath11k *ar = hw->priv; + int ret; + +@@ -76,18 +77,26 @@ ath11k_reg_notifier(struct wiphy *wiphy, + return; + } + +- /* Set the country code to the firmware and wait for ++ /* Set the country code to the firmware and will receive + * the WMI_REG_CHAN_LIST_CC EVENT for updating the + * reg info + */ +- init_country_param.flags = ALPHA_IS_SET; +- memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); +- init_country_param.cc_info.alpha2[2] = 0; +- +- ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); +- if (ret) +- ath11k_warn(ar->ab, +- "INIT Country code set to fw failed : %d\n", ret); ++ if (ar->ab->hw_params.current_cc_support) { ++ memcpy(&set_current_param.alpha2, request->alpha2, 2); ++ ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "failed set current country code: %d\n", ret); ++ } else { ++ init_country_param.flags = ALPHA_IS_SET; ++ memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); ++ init_country_param.cc_info.alpha2[2] = 0; ++ ++ ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "INIT Country code set to fw failed : %d\n", ret); ++ } + + ath11k_mac_11d_scan_stop(ar); + ar->regdom_set_by_user = true; diff --git a/package/kernel/mac80211/patches/ath11k/0166-ath11k-Rename-ath11k_ahb_ext_irq_config.patch b/package/kernel/mac80211/patches/ath11k/0166-ath11k-Rename-ath11k_ahb_ext_irq_config.patch new file mode 100644 index 00000000000000..c1d4de513de99b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0166-ath11k-Rename-ath11k_ahb_ext_irq_config.patch @@ -0,0 +1,35 @@ +From a76ed59163ba82462ccb262b4c3590a3c1a115dd Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Wed, 19 Jan 2022 14:48:13 +0530 +Subject: [PATCH] ath11k: Rename ath11k_ahb_ext_irq_config + +Rename ath11k_ahb_ext_irq_config() to ath11k_ahb_config_ext_irq() +for just symmetry with ath11k_ahb_free_ext_irq(). + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1642583893-21485-1-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ahb.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -466,7 +466,7 @@ static irqreturn_t ath11k_ahb_ext_interr + return IRQ_HANDLED; + } + +-static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) ++static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab) + { + struct ath11k_hw_params *hw = &ab->hw_params; + int i, j; +@@ -574,7 +574,7 @@ static int ath11k_ahb_config_irq(struct + } + + /* Configure external interrupts */ +- ret = ath11k_ahb_ext_irq_config(ab); ++ ret = ath11k_ahb_config_ext_irq(ab); + + return ret; + } diff --git a/package/kernel/mac80211/patches/ath11k/0167-ath11k-fix-kernel-panic-during-unload-load-ath11k-mo.patch b/package/kernel/mac80211/patches/ath11k/0167-ath11k-fix-kernel-panic-during-unload-load-ath11k-mo.patch new file mode 100644 index 00000000000000..c81c650f85ac39 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0167-ath11k-fix-kernel-panic-during-unload-load-ath11k-mo.patch @@ -0,0 +1,55 @@ +From 22b59cb965f79ee1accf83172441c9ca0ecb632a Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Wed, 19 Jan 2022 14:49:33 +0530 +Subject: [PATCH] ath11k: fix kernel panic during unload/load ath11k modules + +Call netif_napi_del() from ath11k_ahb_free_ext_irq() to fix +the following kernel panic when unload/load ath11k modules +for few iterations. + +[ 971.201365] Unable to handle kernel paging request at virtual address 6d97a208 +[ 971.204227] pgd = 594c2919 +[ 971.211478] [6d97a208] *pgd=00000000 +[ 971.214120] Internal error: Oops: 5 [#1] PREEMPT SMP ARM +[ 971.412024] CPU: 2 PID: 4435 Comm: insmod Not tainted 5.4.89 #0 +[ 971.434256] Hardware name: Generic DT based system +[ 971.440165] PC is at napi_by_id+0x10/0x40 +[ 971.445019] LR is at netif_napi_add+0x160/0x1dc + +[ 971.743127] (napi_by_id) from [<807d89a0>] (netif_napi_add+0x160/0x1dc) +[ 971.751295] (netif_napi_add) from [<7f1209ac>] (ath11k_ahb_config_irq+0xf8/0x414 [ath11k_ahb]) +[ 971.759164] (ath11k_ahb_config_irq [ath11k_ahb]) from [<7f12135c>] (ath11k_ahb_probe+0x40c/0x51c [ath11k_ahb]) +[ 971.768567] (ath11k_ahb_probe [ath11k_ahb]) from [<80666864>] (platform_drv_probe+0x48/0x94) +[ 971.779670] (platform_drv_probe) from [<80664718>] (really_probe+0x1c8/0x450) +[ 971.789389] (really_probe) from [<80664cc4>] (driver_probe_device+0x15c/0x1b8) +[ 971.797547] (driver_probe_device) from [<80664f60>] (device_driver_attach+0x44/0x60) +[ 971.805795] (device_driver_attach) from [<806650a0>] (__driver_attach+0x124/0x140) +[ 971.814822] (__driver_attach) from [<80662adc>] (bus_for_each_dev+0x58/0xa4) +[ 971.823328] (bus_for_each_dev) from [<80663a2c>] (bus_add_driver+0xf0/0x1e8) +[ 971.831662] (bus_add_driver) from [<806658a4>] (driver_register+0xa8/0xf0) +[ 971.839822] (driver_register) from [<8030269c>] (do_one_initcall+0x78/0x1ac) +[ 971.847638] (do_one_initcall) from [<80392524>] (do_init_module+0x54/0x200) +[ 971.855968] (do_init_module) from [<803945b0>] (load_module+0x1e30/0x1ffc) +[ 971.864126] (load_module) from [<803948b0>] (sys_init_module+0x134/0x17c) +[ 971.871852] (sys_init_module) from [<80301000>] (ret_fast_syscall+0x0/0x50) + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.6.0.1-00760-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1642583973-21599-1-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ahb.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -391,6 +391,8 @@ static void ath11k_ahb_free_ext_irq(stru + + for (j = 0; j < irq_grp->num_irq; j++) + free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); ++ ++ netif_napi_del(&irq_grp->napi); + } + } + diff --git a/package/kernel/mac80211/patches/ath11k/0168-ath11k-Fix-uninitialized-symbol-rx_buf_sz.patch b/package/kernel/mac80211/patches/ath11k/0168-ath11k-Fix-uninitialized-symbol-rx_buf_sz.patch new file mode 100644 index 00000000000000..ccc13290ba6f6d --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0168-ath11k-Fix-uninitialized-symbol-rx_buf_sz.patch @@ -0,0 +1,57 @@ +From dca857f07dc14c923a3c47965e771f435807b39c Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Wed, 19 Jan 2022 20:53:13 +0530 +Subject: [PATCH] ath11k: Fix uninitialized symbol 'rx_buf_sz' + +Add missing else statement in ath11k_dp_rx_process_mon_status() +to fix below smatch warnings, + drivers/net/wireless/ath/ath11k/dp_rx.c:3105 + ath11k_dp_rx_process_mon_status() + error: uninitialized symbol 'rx_buf_sz'. + +Fixes: ab18e3bc1c13 ("ath11k: Fix pktlog lite rx events") + +Reported-by: Dan Carpenter +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1642605793-13518-1-git-send-email-quic_akolli@quicinc.com +--- + drivers/net/wireless/ath/ath11k/debugfs.h | 1 + + drivers/net/wireless/ath/ath11k/dp_rx.c | 7 +++++-- + 2 files changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs.h +@@ -88,6 +88,7 @@ enum ath11k_pktlog_mode { + }; + + enum ath11k_pktlog_enum { ++ ATH11K_PKTLOG_TYPE_INVALID = 0, + ATH11K_PKTLOG_TYPE_TX_CTRL = 1, + ATH11K_PKTLOG_TYPE_TX_STAT = 2, + ATH11K_PKTLOG_TYPE_TX_MSDU_ID = 3, +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -5054,7 +5054,7 @@ int ath11k_dp_rx_process_mon_status(stru + struct ath11k_sta *arsta; + int num_buffs_reaped = 0; + u32 rx_buf_sz; +- u16 log_type = 0; ++ u16 log_type; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&ar->dp.mon_data; + struct ath11k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; + struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; +@@ -5076,9 +5076,12 @@ int ath11k_dp_rx_process_mon_status(stru + } else if (ath11k_debugfs_is_pktlog_rx_stats_enabled(ar)) { + log_type = ATH11K_PKTLOG_TYPE_RX_STATBUF; + rx_buf_sz = DP_RX_BUFFER_SIZE; ++ } else { ++ log_type = ATH11K_PKTLOG_TYPE_INVALID; ++ rx_buf_sz = 0; + } + +- if (log_type) ++ if (log_type != ATH11K_PKTLOG_TYPE_INVALID) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + + hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); diff --git a/package/kernel/mac80211/patches/ath11k/0169-ath11k-Fix-missing-rx_desc_get_ldpc_support-in-wcn68.patch b/package/kernel/mac80211/patches/ath11k/0169-ath11k-Fix-missing-rx_desc_get_ldpc_support-in-wcn68.patch new file mode 100644 index 00000000000000..1249b4639fdafd --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0169-ath11k-Fix-missing-rx_desc_get_ldpc_support-in-wcn68.patch @@ -0,0 +1,67 @@ +From 648ab4720cb76d0b5b1e49d3c0f0bdf28e22e52a Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Wed, 26 Jan 2022 09:01:44 +0800 +Subject: [PATCH] ath11k: Fix missing rx_desc_get_ldpc_support in wcn6855_ops + +rx_desc_get_ldpc_support is missing in wcn6855_ops, resulting on WCN6855 a +kernel crash after connecting to an AP and waiting for few minutes. Fix it by +implementing WCN6855's version of this field and adding it to wcn6855_ops. + +Crash stack: +[ 184.862605] BUG: kernel NULL pointer dereference, address: 0000000000000000 +[ 184.862615] #PF: supervisor instruction fetch in kernel mode +[ 184.862620] #PF: error_code(0x0010) - not-present page +[ 184.862626] PGD 0 P4D 0 +[ 184.862634] Oops: 0010 [#1] PREEMPT SMP PTI +[ 184.862642] CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Not tainted 5.16.0-wt-ath+ #1 +[ 184.862651] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0059.2019.1112.1124 11/12/2019 +[ 184.862656] RIP: 0010:0x0 +[ 184.862669] Code: Unable to access opcode bytes at RIP 0xffffffffffffffd6. +[ 184.862673] RSP: 0018:ffff9eedc003cca8 EFLAGS: 00010202 +[ 184.862680] RAX: 0000000000000000 RBX: ffff9eedc003cd30 RCX: 0000000000000002 +[ 184.862686] RDX: 0000000000000002 RSI: ffffffffc1773458 RDI: ffff8eb5843de240 +[ 184.862692] RBP: ffff8eb59685a0e0 R08: 0000000000000001 R09: ffff8eb6fef2b000 +[ 184.862700] R10: ffff9eedc003cd70 R11: ffff8eb5880a9ff0 R12: ffff8eb5843de240 +[ 184.862707] R13: 0000000000000000 R14: 0000000000000008 R15: 0000000000000003 +[ 184.862714] FS: 0000000000000000(0000) GS:ffff8eb6f6c40000(0000) knlGS:0000000000000000 +[ 184.862723] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 184.862733] CR2: ffffffffffffffd6 CR3: 000000002f60a001 CR4: 00000000003706e0 +[ 184.862743] Call Trace: +[ 184.862751] +[ 184.862759] ath11k_dp_rx_h_ppdu+0x210/0x350 [ath11k] +[ 184.862841] ath11k_dp_rx_process_received_packets+0x1e6/0x6b0 [ath11k] +[ 184.862891] ath11k_dp_process_rx+0x32d/0x3e0 [ath11k] + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Fixes: b3febdccde3e ("ath11k: add LDPC FEC type in 802.11 radiotap header") +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220126010144.2090-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/hw.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -813,6 +813,12 @@ static u16 ath11k_hw_wcn6855_mpdu_info_g + return peer_id; + } + ++static bool ath11k_hw_wcn6855_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) ++{ ++ return FIELD_GET(RX_MSDU_START_INFO2_LDPC, ++ __le32_to_cpu(desc->u.wcn6855.msdu_start.info2)); ++} ++ + const struct ath11k_hw_ops ipq8074_ops = { + .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = ath11k_init_wmi_config_ipq8074, +@@ -983,6 +989,7 @@ const struct ath11k_hw_ops wcn6855_ops = + .rx_desc_get_encrypt_type = ath11k_hw_wcn6855_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_wcn6855_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_wcn6855_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ldpc_support = ath11k_hw_wcn6855_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_wcn6855_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_wcn6855_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath11k_hw_wcn6855_rx_desc_get_mpdu_start_seq_no, diff --git a/package/kernel/mac80211/patches/ath11k/0170-ath11k-pci-fix-crash-on-suspend-if-board-file-is-not.patch b/package/kernel/mac80211/patches/ath11k/0170-ath11k-pci-fix-crash-on-suspend-if-board-file-is-not.patch new file mode 100644 index 00000000000000..436f7e47b91227 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0170-ath11k-pci-fix-crash-on-suspend-if-board-file-is-not.patch @@ -0,0 +1,84 @@ +From b4f4c56459a5c744f7f066b9fc2b54ea995030c5 Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Thu, 27 Jan 2022 11:01:16 +0200 +Subject: [PATCH] ath11k: pci: fix crash on suspend if board file is not found + +Mario reported that the kernel was crashing on suspend if ath11k was not able +to find a board file: + +[ 473.693286] PM: Suspending system (s2idle) +[ 473.693291] printk: Suspending console(s) (use no_console_suspend to debug) +[ 474.407787] BUG: unable to handle page fault for address: 0000000000002070 +[ 474.407791] #PF: supervisor read access in kernel mode +[ 474.407794] #PF: error_code(0x0000) - not-present page +[ 474.407798] PGD 0 P4D 0 +[ 474.407801] Oops: 0000 [#1] PREEMPT SMP NOPTI +[ 474.407805] CPU: 2 PID: 2350 Comm: kworker/u32:14 Tainted: G W 5.16.0 #248 +[...] +[ 474.407868] Call Trace: +[ 474.407870] +[ 474.407874] ? _raw_spin_lock_irqsave+0x2a/0x60 +[ 474.407882] ? lock_timer_base+0x72/0xa0 +[ 474.407889] ? _raw_spin_unlock_irqrestore+0x29/0x3d +[ 474.407892] ? try_to_del_timer_sync+0x54/0x80 +[ 474.407896] ath11k_dp_rx_pktlog_stop+0x49/0xc0 [ath11k] +[ 474.407912] ath11k_core_suspend+0x34/0x130 [ath11k] +[ 474.407923] ath11k_pci_pm_suspend+0x1b/0x50 [ath11k_pci] +[ 474.407928] pci_pm_suspend+0x7e/0x170 +[ 474.407935] ? pci_pm_freeze+0xc0/0xc0 +[ 474.407939] dpm_run_callback+0x4e/0x150 +[ 474.407947] __device_suspend+0x148/0x4c0 +[ 474.407951] async_suspend+0x20/0x90 +dmesg-efi-164255130401001: +Oops#1 Part1 +[ 474.407955] async_run_entry_fn+0x33/0x120 +[ 474.407959] process_one_work+0x220/0x3f0 +[ 474.407966] worker_thread+0x4a/0x3d0 +[ 474.407971] kthread+0x17a/0x1a0 +[ 474.407975] ? process_one_work+0x3f0/0x3f0 +[ 474.407979] ? set_kthread_struct+0x40/0x40 +[ 474.407983] ret_from_fork+0x22/0x30 +[ 474.407991] + +The issue here is that board file loading happens after ath11k_pci_probe() +succesfully returns (ath11k initialisation happends asynchronously) and the +suspend handler is still enabled, of course failing as ath11k is not properly +initialised. Fix this by checking ATH11K_FLAG_QMI_FAIL during both suspend and +resume. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Reported-by: Mario Limonciello +Link: https://bugzilla.kernel.org/show_bug.cgi?id=215504 +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220127090117.2024-1-kvalo@kernel.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -1571,6 +1571,11 @@ static __maybe_unused int ath11k_pci_pm_ + struct ath11k_base *ab = dev_get_drvdata(dev); + int ret; + ++ if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci suspend as qmi is not initialised\n"); ++ return 0; ++ } ++ + ret = ath11k_core_suspend(ab); + if (ret) + ath11k_warn(ab, "failed to suspend core: %d\n", ret); +@@ -1583,6 +1588,11 @@ static __maybe_unused int ath11k_pci_pm_ + struct ath11k_base *ab = dev_get_drvdata(dev); + int ret; + ++ if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci resume as qmi is not initialised\n"); ++ return 0; ++ } ++ + ret = ath11k_core_resume(ab); + if (ret) + ath11k_warn(ab, "failed to resume core: %d\n", ret); diff --git a/package/kernel/mac80211/patches/ath11k/0171-ath11k-mhi-use-mhi_sync_power_up.patch b/package/kernel/mac80211/patches/ath11k/0171-ath11k-mhi-use-mhi_sync_power_up.patch new file mode 100644 index 00000000000000..6e23d989c73a17 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0171-ath11k-mhi-use-mhi_sync_power_up.patch @@ -0,0 +1,77 @@ +From 3df6d74aedfdca919cca475d15dfdbc8b05c9e5d Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Thu, 27 Jan 2022 11:01:17 +0200 +Subject: [PATCH] ath11k: mhi: use mhi_sync_power_up() + +If amss.bin was missing ath11k would crash during 'rmmod ath11k_pci'. The +reason for that was that we were using mhi_async_power_up() which does not +check any errors. But mhi_sync_power_up() on the other hand does check for +errors so let's use that to fix the crash. + +I was not able to find a reason why an async version was used. +ath11k_mhi_start() (which enables state ATH11K_MHI_POWER_ON) is called from +ath11k_hif_power_up(), which can sleep. So sync version should be safe to use +here. + +[ 145.569731] general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC KASAN PTI +[ 145.569789] KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] +[ 145.569843] CPU: 2 PID: 1628 Comm: rmmod Kdump: loaded Tainted: G W 5.16.0-wt-ath+ #567 +[ 145.569898] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0067.2021.0528.1339 05/28/2021 +[ 145.569956] RIP: 0010:ath11k_hal_srng_access_begin+0xb5/0x2b0 [ath11k] +[ 145.570028] Code: df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 ec 01 00 00 48 8b ab a8 00 00 00 48 b8 00 00 00 00 00 fc ff df 48 89 ea 48 c1 ea 03 <0f> b6 14 02 48 89 e8 83 e0 07 83 c0 03 45 85 ed 75 48 38 d0 7c 08 +[ 145.570089] RSP: 0018:ffffc900025d7ac0 EFLAGS: 00010246 +[ 145.570144] RAX: dffffc0000000000 RBX: ffff88814fca2dd8 RCX: 1ffffffff50cb455 +[ 145.570196] RDX: 0000000000000000 RSI: ffff88814fca2dd8 RDI: ffff88814fca2e80 +[ 145.570252] RBP: 0000000000000000 R08: 0000000000000000 R09: ffffffffa8659497 +[ 145.570329] R10: fffffbfff50cb292 R11: 0000000000000001 R12: ffff88814fca0000 +[ 145.570410] R13: 0000000000000000 R14: ffff88814fca2798 R15: ffff88814fca2dd8 +[ 145.570465] FS: 00007fa399988540(0000) GS:ffff888233e00000(0000) knlGS:0000000000000000 +[ 145.570519] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 145.570571] CR2: 00007fa399b51421 CR3: 0000000137898002 CR4: 00000000003706e0 +[ 145.570623] Call Trace: +[ 145.570675] +[ 145.570727] ? ath11k_ce_tx_process_cb+0x34b/0x860 [ath11k] +[ 145.570797] ath11k_ce_tx_process_cb+0x356/0x860 [ath11k] +[ 145.570864] ? tasklet_init+0x150/0x150 +[ 145.570919] ? ath11k_ce_alloc_pipes+0x280/0x280 [ath11k] +[ 145.570986] ? tasklet_clear_sched+0x42/0xe0 +[ 145.571042] ? tasklet_kill+0xe9/0x1b0 +[ 145.571095] ? tasklet_clear_sched+0xe0/0xe0 +[ 145.571148] ? irq_has_action+0x120/0x120 +[ 145.571202] ath11k_ce_cleanup_pipes+0x45a/0x580 [ath11k] +[ 145.571270] ? ath11k_pci_stop+0x10e/0x170 [ath11k_pci] +[ 145.571345] ath11k_core_stop+0x8a/0xc0 [ath11k] +[ 145.571434] ath11k_core_deinit+0x9e/0x150 [ath11k] +[ 145.571499] ath11k_pci_remove+0xd2/0x260 [ath11k_pci] +[ 145.571553] pci_device_remove+0x9a/0x1c0 +[ 145.571605] __device_release_driver+0x332/0x660 +[ 145.571659] driver_detach+0x1e7/0x2c0 +[ 145.571712] bus_remove_driver+0xe2/0x2d0 +[ 145.571772] pci_unregister_driver+0x21/0x250 +[ 145.571826] __do_sys_delete_module+0x30a/0x4b0 +[ 145.571879] ? free_module+0xac0/0xac0 +[ 145.571933] ? lockdep_hardirqs_on_prepare.part.0+0x18c/0x370 +[ 145.571986] ? syscall_enter_from_user_mode+0x1d/0x50 +[ 145.572039] ? lockdep_hardirqs_on+0x79/0x100 +[ 145.572097] do_syscall_64+0x3b/0x90 +[ 145.572153] entry_SYSCALL_64_after_hwframe+0x44/0xae + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220127090117.2024-2-kvalo@kernel.org +--- + drivers/net/wireless/ath/ath11k/mhi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -563,7 +563,7 @@ static int ath11k_mhi_set_state(struct a + ret = 0; + break; + case ATH11K_MHI_POWER_ON: +- ret = mhi_async_power_up(ab_pci->mhi_ctrl); ++ ret = mhi_sync_power_up(ab_pci->mhi_ctrl); + break; + case ATH11K_MHI_POWER_OFF: + mhi_power_down(ab_pci->mhi_ctrl, true); diff --git a/package/kernel/mac80211/patches/ath11k/0172-ath11k-Add-debugfs-interface-to-configure-firmware-d.patch b/package/kernel/mac80211/patches/ath11k/0172-ath11k-Add-debugfs-interface-to-configure-firmware-d.patch new file mode 100644 index 00000000000000..3c4c3bed6bf0b5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0172-ath11k-Add-debugfs-interface-to-configure-firmware-d.patch @@ -0,0 +1,404 @@ +From f295ad912910e08d9b887a0c952f82d9612459d4 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Mon, 31 Jan 2022 16:16:44 +0200 +Subject: [PATCH] ath11k: Add debugfs interface to configure firmware debug log + level + +Add debugfs interface "fw_dbglog_config" to configure firmware log level. +Configuration is done via WMI command WMI_DBGLOG_CFG_CMDID. + +Command to configure, +echo " " > +/sys/kernel/debug/ath11k//macX/fw_dbglog_config + +where dbglog_param can be, + 1) WMI_DEBUG_LOG_PARAM_LOG_LEVEL - configure log level for a given module + here, = <0xaaaa00bb>, 'aaaa' - module id and 'bb' - loglevel + 2) WMI_DEBUG_LOG_PARAM_VDEV_ENABLE - enable debug log for a given vdev + here, = vdev_id + 3) WMI_DEBUG_LOG_PARAM_VDEV_DISABLE - disable debug log for a given vdev + except ERROR logs + here, = vdev_id + 4) WMI_DEBUG_LOG_PARAM_VDEV_ENABLE_BITMAP - set vdev enable bitmap + here, = vdev_enable_bitmap + 5) WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP - set a given log level to all the + modules specified in the module bitmap. Command to configure for this log param, + + $ echo "5 " > + /sys/kernel/debug/ath11k//macX/fw_dbglog_config + here, + = <0xaaaaaaaa000000bb>, 'aaaaaaaa' - module bitmap and + 'bb' - loglevel + = index of module bitmap. Max module id is 512. + So, module_id_index is 0-15. + = to indicate if more configuration to follow. + + 6) WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP - Wow mode specific logging enable. + Command to configure for this log param, + + $ echo "6 " > + /sys/kernel/debug/ath11k//macX/fw_dbglog_config + here, + = <0xaaaaaaaa000000bb>, 'aaaaaaaa' - module bitmap and + 'bb' - loglevel + = index of module bitmap. Max module id is 512. + So, module_id_index is 0-15. + = to indicate if more configuration to follow. + +Sample command usage, + +To enable module WLAN_MODULE_WMI and log level ATH11K_FW_DBGLOG_VERBOSE, +echo "1 0x10001" > /sys/kernel/debug/ath11k//macX/fw_dbglog_config + +To enable module bit map from 32 to 63 and log level ATH11K_FW_DBGLOG_VERBOSE, +echo "5 0xffffffff00000001 1 1" > /sys/kernel/debug/ath11k//macX/fw_dbglog_config + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1-01734-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1642405103-32302-1-git-send-email-quic_seevalam@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 4 + + drivers/net/wireless/ath/ath11k/debugfs.c | 66 ++++++++++++ + drivers/net/wireless/ath/ath11k/debugfs.h | 124 ++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 53 +++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 18 ++++ + 5 files changed, 265 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -441,6 +441,8 @@ struct ath11k_dbg_htt_stats { + spinlock_t lock; + }; + ++#define MAX_MODULE_ID_BITMAP_WORDS 16 ++ + struct ath11k_debug { + struct dentry *debugfs_pdev; + struct ath11k_dbg_htt_stats htt_stats; +@@ -454,6 +456,8 @@ struct ath11k_debug { + u32 pktlog_peer_valid; + u8 pktlog_peer_addr[ETH_ALEN]; + u32 rx_filter; ++ u32 mem_offset; ++ u32 module_id_bitmap[MAX_MODULE_ID_BITMAP_WORDS]; + }; + + struct ath11k_per_peer_tx_stats { +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -876,6 +876,69 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++static ssize_t ath11k_write_fw_dbglog(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k *ar = file->private_data; ++ char buf[128] = {0}; ++ struct ath11k_fw_dbglog dbglog; ++ unsigned int param, mod_id_index, is_end; ++ u64 value; ++ int ret, num; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ++ user_buf, count); ++ if (ret <= 0) ++ return ret; ++ ++ num = sscanf(buf, "%u %llx %u %u", ¶m, &value, &mod_id_index, &is_end); ++ ++ if (num < 2) ++ return -EINVAL; ++ ++ mutex_lock(&ar->conf_mutex); ++ if (param == WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP || ++ param == WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP) { ++ if (num != 4 || mod_id_index > (MAX_MODULE_ID_BITMAP_WORDS - 1)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ar->debug.module_id_bitmap[mod_id_index] = upper_32_bits(value); ++ if (!is_end) { ++ ret = count; ++ goto out; ++ } ++ } else { ++ if (num != 2) { ++ ret = -EINVAL; ++ goto out; ++ } ++ } ++ ++ dbglog.param = param; ++ dbglog.value = lower_32_bits(value); ++ ret = ath11k_wmi_fw_dbglog_cfg(ar, ar->debug.module_id_bitmap, &dbglog); ++ if (ret) { ++ ath11k_warn(ar->ab, "fw dbglog config failed from debugfs: %d\n", ++ ret); ++ goto out; ++ } ++ ++ ret = count; ++ ++out: ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ ++static const struct file_operations fops_fw_dbglog = { ++ .write = ath11k_write_fw_dbglog, ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ + int ath11k_debugfs_pdev_create(struct ath11k_base *ab) + { + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) +@@ -1142,6 +1205,9 @@ int ath11k_debugfs_register(struct ath11 + debugfs_create_file("pktlog_filter", 0644, + ar->debug.debugfs_pdev, ar, + &fops_pktlog_filter); ++ debugfs_create_file("fw_dbglog_config", 0600, ++ ar->debug.debugfs_pdev, ar, ++ &fops_fw_dbglog); + + if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { + debugfs_create_file("dfs_simulate_radar", 0200, +--- a/drivers/net/wireless/ath/ath11k/debugfs.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs.h +@@ -108,6 +108,130 @@ enum ath11k_dbg_aggr_mode { + ATH11K_DBG_AGGR_MODE_MAX, + }; + ++enum fw_dbglog_wlan_module_id { ++ WLAN_MODULE_ID_MIN = 0, ++ WLAN_MODULE_INF = WLAN_MODULE_ID_MIN, ++ WLAN_MODULE_WMI, ++ WLAN_MODULE_STA_PWRSAVE, ++ WLAN_MODULE_WHAL, ++ WLAN_MODULE_COEX, ++ WLAN_MODULE_ROAM, ++ WLAN_MODULE_RESMGR_CHAN_MANAGER, ++ WLAN_MODULE_RESMGR, ++ WLAN_MODULE_VDEV_MGR, ++ WLAN_MODULE_SCAN, ++ WLAN_MODULE_RATECTRL, ++ WLAN_MODULE_AP_PWRSAVE, ++ WLAN_MODULE_BLOCKACK, ++ WLAN_MODULE_MGMT_TXRX, ++ WLAN_MODULE_DATA_TXRX, ++ WLAN_MODULE_HTT, ++ WLAN_MODULE_HOST, ++ WLAN_MODULE_BEACON, ++ WLAN_MODULE_OFFLOAD, ++ WLAN_MODULE_WAL, ++ WLAN_WAL_MODULE_DE, ++ WLAN_MODULE_PCIELP, ++ WLAN_MODULE_RTT, ++ WLAN_MODULE_RESOURCE, ++ WLAN_MODULE_DCS, ++ WLAN_MODULE_CACHEMGR, ++ WLAN_MODULE_ANI, ++ WLAN_MODULE_P2P, ++ WLAN_MODULE_CSA, ++ WLAN_MODULE_NLO, ++ WLAN_MODULE_CHATTER, ++ WLAN_MODULE_WOW, ++ WLAN_MODULE_WAL_VDEV, ++ WLAN_MODULE_WAL_PDEV, ++ WLAN_MODULE_TEST, ++ WLAN_MODULE_STA_SMPS, ++ WLAN_MODULE_SWBMISS, ++ WLAN_MODULE_WMMAC, ++ WLAN_MODULE_TDLS, ++ WLAN_MODULE_HB, ++ WLAN_MODULE_TXBF, ++ WLAN_MODULE_BATCH_SCAN, ++ WLAN_MODULE_THERMAL_MGR, ++ WLAN_MODULE_PHYERR_DFS, ++ WLAN_MODULE_RMC, ++ WLAN_MODULE_STATS, ++ WLAN_MODULE_NAN, ++ WLAN_MODULE_IBSS_PWRSAVE, ++ WLAN_MODULE_HIF_UART, ++ WLAN_MODULE_LPI, ++ WLAN_MODULE_EXTSCAN, ++ WLAN_MODULE_UNIT_TEST, ++ WLAN_MODULE_MLME, ++ WLAN_MODULE_SUPPL, ++ WLAN_MODULE_ERE, ++ WLAN_MODULE_OCB, ++ WLAN_MODULE_RSSI_MONITOR, ++ WLAN_MODULE_WPM, ++ WLAN_MODULE_CSS, ++ WLAN_MODULE_PPS, ++ WLAN_MODULE_SCAN_CH_PREDICT, ++ WLAN_MODULE_MAWC, ++ WLAN_MODULE_CMC_QMIC, ++ WLAN_MODULE_EGAP, ++ WLAN_MODULE_NAN20, ++ WLAN_MODULE_QBOOST, ++ WLAN_MODULE_P2P_LISTEN_OFFLOAD, ++ WLAN_MODULE_HALPHY, ++ WLAN_WAL_MODULE_ENQ, ++ WLAN_MODULE_GNSS, ++ WLAN_MODULE_WAL_MEM, ++ WLAN_MODULE_SCHED_ALGO, ++ WLAN_MODULE_TX, ++ WLAN_MODULE_RX, ++ WLAN_MODULE_WLM, ++ WLAN_MODULE_RU_ALLOCATOR, ++ WLAN_MODULE_11K_OFFLOAD, ++ WLAN_MODULE_STA_TWT, ++ WLAN_MODULE_AP_TWT, ++ WLAN_MODULE_UL_OFDMA, ++ WLAN_MODULE_HPCS_PULSE, ++ WLAN_MODULE_DTF, ++ WLAN_MODULE_QUIET_IE, ++ WLAN_MODULE_SHMEM_MGR, ++ WLAN_MODULE_CFIR, ++ WLAN_MODULE_CODE_COVER, ++ WLAN_MODULE_SHO, ++ WLAN_MODULE_MLO_MGR, ++ WLAN_MODULE_PEER_INIT, ++ WLAN_MODULE_STA_MLO_PS, ++ ++ WLAN_MODULE_ID_MAX, ++ WLAN_MODULE_ID_INVALID = WLAN_MODULE_ID_MAX, ++}; ++ ++enum fw_dbglog_log_level { ++ ATH11K_FW_DBGLOG_ML = 0, ++ ATH11K_FW_DBGLOG_VERBOSE = 0, ++ ATH11K_FW_DBGLOG_INFO, ++ ATH11K_FW_DBGLOG_INFO_LVL_1, ++ ATH11K_FW_DBGLOG_INFO_LVL_2, ++ ATH11K_FW_DBGLOG_WARN, ++ ATH11K_FW_DBGLOG_ERR, ++ ATH11K_FW_DBGLOG_LVL_MAX ++}; ++ ++struct ath11k_fw_dbglog { ++ enum wmi_debug_log_param param; ++ union { ++ struct { ++ /* log_level values are given in enum fw_dbglog_log_level */ ++ u16 log_level; ++ /* module_id values are given in enum fw_dbglog_wlan_module_id */ ++ u16 module_id; ++ }; ++ /* value is either log_level&module_id/vdev_id/vdev_id_bitmap/log_level ++ * according to param ++ */ ++ u32 value; ++ }; ++}; ++ + #ifdef CPTCFG_ATH11K_DEBUGFS + int ath11k_debugfs_soc_create(struct ath11k_base *ab); + void ath11k_debugfs_soc_destroy(struct ath11k_base *ab); +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7798,6 +7798,59 @@ int ath11k_wmi_simulate_radar(struct ath + return ath11k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); + } + ++int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap, ++ struct ath11k_fw_dbglog *dbglog) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_debug_log_config_cmd_fixed_param *cmd; ++ struct sk_buff *skb; ++ struct wmi_tlv *tlv; ++ int ret, len; ++ ++ len = sizeof(*cmd) + TLV_HDR_SIZE + (MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_debug_log_config_cmd_fixed_param *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_DEBUG_LOG_CONFIG_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ cmd->dbg_log_param = dbglog->param; ++ ++ tlv = (struct wmi_tlv *)((u8 *)cmd + sizeof(*cmd)); ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | ++ FIELD_PREP(WMI_TLV_LEN, MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); ++ ++ switch (dbglog->param) { ++ case WMI_DEBUG_LOG_PARAM_LOG_LEVEL: ++ case WMI_DEBUG_LOG_PARAM_VDEV_ENABLE: ++ case WMI_DEBUG_LOG_PARAM_VDEV_DISABLE: ++ case WMI_DEBUG_LOG_PARAM_VDEV_ENABLE_BITMAP: ++ cmd->value = dbglog->value; ++ break; ++ case WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP: ++ case WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP: ++ cmd->value = dbglog->value; ++ memcpy(tlv->value, module_id_bitmap, ++ MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); ++ /* clear current config to be used for next user config */ ++ memset(module_id_bitmap, 0, ++ MAX_MODULE_ID_BITMAP_WORDS * sizeof(u32)); ++ break; ++ default: ++ dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_DBGLOG_CFG_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to send WMI_DBGLOG_CFG_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ return ret; ++} ++ + int ath11k_wmi_connect(struct ath11k_base *ab) + { + u32 i; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -12,6 +12,7 @@ + struct ath11k_base; + struct ath11k; + struct ath11k_fw_stats; ++struct ath11k_fw_dbglog; + + #define PSOC_HOST_MAX_NUM_SS (8) + +@@ -5240,6 +5241,21 @@ struct wmi_rfkill_state_change_ev { + u32 radio_state; + } __packed; + ++enum wmi_debug_log_param { ++ WMI_DEBUG_LOG_PARAM_LOG_LEVEL = 0x1, ++ WMI_DEBUG_LOG_PARAM_VDEV_ENABLE, ++ WMI_DEBUG_LOG_PARAM_VDEV_DISABLE, ++ WMI_DEBUG_LOG_PARAM_VDEV_ENABLE_BITMAP, ++ WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP, ++ WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP, ++}; ++ ++struct wmi_debug_log_config_cmd_fixed_param { ++ u32 tlv_header; ++ u32 dbg_log_param; ++ u32 value; ++} __packed; ++ + #define WMI_MAX_MEM_REQS 32 + + #define MAX_RADIOS 3 +@@ -5582,4 +5598,6 @@ int ath11k_wmi_wow_host_wakeup_ind(struc + int ath11k_wmi_wow_enable(struct ath11k *ar); + int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, + const u8 mac_addr[ETH_ALEN]); ++int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap, ++ struct ath11k_fw_dbglog *dbglog); + #endif diff --git a/package/kernel/mac80211/patches/ath11k/0173-ath11k-add-WMI-calls-to-manually-add-del-pause-resum.patch b/package/kernel/mac80211/patches/ath11k/0173-ath11k-add-WMI-calls-to-manually-add-del-pause-resum.patch new file mode 100644 index 00000000000000..596edee3532a7b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0173-ath11k-add-WMI-calls-to-manually-add-del-pause-resum.patch @@ -0,0 +1,455 @@ +From 3d00e8b5b818266f43d1fabdb961e215cab45dfb Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 31 Jan 2022 16:16:44 +0200 +Subject: [PATCH] ath11k: add WMI calls to manually add/del/pause/resume TWT + dialogs + +These calls are used for debugging and will be required for WFA +certification tests. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01179-QCAHKSWPL_SILICONZ-1 +Signed-off-by: John Crispin +Signed-off-by: Aloka Dixit +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220131031043.1295-1-alokad@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/wmi.c | 247 ++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/wmi.h | 114 ++++++++++++ + 3 files changed, 358 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -608,6 +608,7 @@ struct ath11k { + bool pending_11d; + bool regdom_set_by_user; + int hw_rate_code; ++ u8 twt_enabled; + }; + + struct ath11k_band_cap { +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -144,6 +144,8 @@ static const struct wmi_tlv_policy wmi_t + .min_len = sizeof(struct wmi_11d_new_cc_ev) }, + [WMI_TAG_PER_CHAIN_RSSI_STATS] = { + .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, ++ [WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT] = { ++ .min_len = sizeof(struct wmi_twt_add_dialog_event) }, + }; + + #define PRIMAP(_hw_mode_) \ +@@ -3085,11 +3087,12 @@ ath11k_wmi_send_twt_enable_cmd(struct at + /* TODO add MBSSID support */ + cmd->mbss_support = 0; + +- ret = ath11k_wmi_cmd_send(wmi, skb, +- WMI_TWT_ENABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_ENABLE_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_TWT_ENABLE_CMDID"); + dev_kfree_skb(skb); ++ } else { ++ ar->twt_enabled = 1; + } + return ret; + } +@@ -3114,11 +3117,181 @@ ath11k_wmi_send_twt_disable_cmd(struct a + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + +- ret = ath11k_wmi_cmd_send(wmi, skb, +- WMI_TWT_DISABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_DISABLE_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_TWT_DISABLE_CMDID"); + dev_kfree_skb(skb); ++ } else { ++ ar->twt_enabled = 0; ++ } ++ return ret; ++} ++ ++int ath11k_wmi_send_twt_add_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_add_dialog_params *params) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct ath11k_base *ab = wmi->wmi_ab->ab; ++ struct wmi_twt_add_dialog_params_cmd *cmd; ++ struct sk_buff *skb; ++ int ret, len; ++ ++ len = sizeof(*cmd); ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_twt_add_dialog_params_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_ADD_DIALOG_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = params->vdev_id; ++ ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); ++ cmd->dialog_id = params->dialog_id; ++ cmd->wake_intvl_us = params->wake_intvl_us; ++ cmd->wake_intvl_mantis = params->wake_intvl_mantis; ++ cmd->wake_dura_us = params->wake_dura_us; ++ cmd->sp_offset_us = params->sp_offset_us; ++ cmd->flags = params->twt_cmd; ++ if (params->flag_bcast) ++ cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_BCAST; ++ if (params->flag_trigger) ++ cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_TRIGGER; ++ if (params->flag_flow_type) ++ cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_FLOW_TYPE; ++ if (params->flag_protection) ++ cmd->flags |= WMI_TWT_ADD_DIALOG_FLAG_PROTECTION; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi add twt dialog vdev %u dialog id %u wake interval %u mantissa %u wake duration %u service period offset %u flags 0x%x\n", ++ cmd->vdev_id, cmd->dialog_id, cmd->wake_intvl_us, ++ cmd->wake_intvl_mantis, cmd->wake_dura_us, cmd->sp_offset_us, ++ cmd->flags); ++ ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_ADD_DIALOG_CMDID); ++ ++ if (ret) { ++ ath11k_warn(ab, ++ "failed to send wmi command to add twt dialog: %d", ++ ret); ++ dev_kfree_skb(skb); ++ } ++ return ret; ++} ++ ++int ath11k_wmi_send_twt_del_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_del_dialog_params *params) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct ath11k_base *ab = wmi->wmi_ab->ab; ++ struct wmi_twt_del_dialog_params_cmd *cmd; ++ struct sk_buff *skb; ++ int ret, len; ++ ++ len = sizeof(*cmd); ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_twt_del_dialog_params_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_DEL_DIALOG_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = params->vdev_id; ++ ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); ++ cmd->dialog_id = params->dialog_id; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi delete twt dialog vdev %u dialog id %u\n", ++ cmd->vdev_id, cmd->dialog_id); ++ ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_DEL_DIALOG_CMDID); ++ if (ret) { ++ ath11k_warn(ab, ++ "failed to send wmi command to delete twt dialog: %d", ++ ret); ++ dev_kfree_skb(skb); ++ } ++ return ret; ++} ++ ++int ath11k_wmi_send_twt_pause_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_pause_dialog_params *params) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct ath11k_base *ab = wmi->wmi_ab->ab; ++ struct wmi_twt_pause_dialog_params_cmd *cmd; ++ struct sk_buff *skb; ++ int ret, len; ++ ++ len = sizeof(*cmd); ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_twt_pause_dialog_params_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_TWT_PAUSE_DIALOG_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = params->vdev_id; ++ ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); ++ cmd->dialog_id = params->dialog_id; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi pause twt dialog vdev %u dialog id %u\n", ++ cmd->vdev_id, cmd->dialog_id); ++ ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_PAUSE_DIALOG_CMDID); ++ if (ret) { ++ ath11k_warn(ab, ++ "failed to send wmi command to pause twt dialog: %d", ++ ret); ++ dev_kfree_skb(skb); ++ } ++ return ret; ++} ++ ++int ath11k_wmi_send_twt_resume_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_resume_dialog_params *params) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct ath11k_base *ab = wmi->wmi_ab->ab; ++ struct wmi_twt_resume_dialog_params_cmd *cmd; ++ struct sk_buff *skb; ++ int ret, len; ++ ++ len = sizeof(*cmd); ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_twt_resume_dialog_params_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_TWT_RESUME_DIALOG_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = params->vdev_id; ++ ether_addr_copy(cmd->peer_macaddr.addr, params->peer_macaddr); ++ cmd->dialog_id = params->dialog_id; ++ cmd->sp_offset_us = params->sp_offset_us; ++ cmd->next_twt_size = params->next_twt_size; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi resume twt dialog vdev %u dialog id %u service period offset %u next twt subfield size %u\n", ++ cmd->vdev_id, cmd->dialog_id, cmd->sp_offset_us, ++ cmd->next_twt_size); ++ ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_RESUME_DIALOG_CMDID); ++ if (ret) { ++ ath11k_warn(ab, ++ "failed to send wmi command to resume twt dialog: %d", ++ ret); ++ dev_kfree_skb(skb); + } + return ret; + } +@@ -7532,6 +7705,66 @@ ath11k_wmi_diag_event(struct ath11k_base + trace_ath11k_wmi_diag(ab, skb->data, skb->len); + } + ++static const char *ath11k_wmi_twt_add_dialog_event_status(u32 status) ++{ ++ switch (status) { ++ case WMI_ADD_TWT_STATUS_OK: ++ return "ok"; ++ case WMI_ADD_TWT_STATUS_TWT_NOT_ENABLED: ++ return "twt disabled"; ++ case WMI_ADD_TWT_STATUS_USED_DIALOG_ID: ++ return "dialog id in use"; ++ case WMI_ADD_TWT_STATUS_INVALID_PARAM: ++ return "invalid parameters"; ++ case WMI_ADD_TWT_STATUS_NOT_READY: ++ return "not ready"; ++ case WMI_ADD_TWT_STATUS_NO_RESOURCE: ++ return "resource unavailable"; ++ case WMI_ADD_TWT_STATUS_NO_ACK: ++ return "no ack"; ++ case WMI_ADD_TWT_STATUS_NO_RESPONSE: ++ return "no response"; ++ case WMI_ADD_TWT_STATUS_DENIED: ++ return "denied"; ++ case WMI_ADD_TWT_STATUS_UNKNOWN_ERROR: ++ fallthrough; ++ default: ++ return "unknown error"; ++ } ++} ++ ++static void ath11k_wmi_twt_add_dialog_event(struct ath11k_base *ab, ++ struct sk_buff *skb) ++{ ++ const void **tb; ++ const struct wmi_twt_add_dialog_event *ev; ++ int ret; ++ ++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); ++ if (IS_ERR(tb)) { ++ ret = PTR_ERR(tb); ++ ath11k_warn(ab, ++ "failed to parse wmi twt add dialog status event tlv: %d\n", ++ ret); ++ return; ++ } ++ ++ ev = tb[WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT]; ++ if (!ev) { ++ ath11k_warn(ab, "failed to fetch twt add dialog wmi event\n"); ++ goto exit; ++ } ++ ++ if (ev->status) ++ ath11k_warn(ab, ++ "wmi add twt dialog event vdev %d dialog id %d status %s\n", ++ ev->vdev_id, ev->dialog_id, ++ ath11k_wmi_twt_add_dialog_event_status(ev->status)); ++ ++exit: ++ kfree(tb); ++} ++ + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) + { + struct wmi_cmd_hdr *cmd_hdr; +@@ -7629,11 +7862,17 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: + ath11k_wmi_obss_color_collision_event(ab, skb); + break; ++ case WMI_TWT_ADD_DIALOG_EVENTID: ++ ath11k_wmi_twt_add_dialog_event(ab, skb); ++ break; + /* add Unsupported events here */ + case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: + case WMI_PEER_OPER_MODE_CHANGE_EVENTID: + case WMI_TWT_ENABLE_EVENTID: + case WMI_TWT_DISABLE_EVENTID: ++ case WMI_TWT_DEL_DIALOG_EVENTID: ++ case WMI_TWT_PAUSE_DIALOG_EVENTID: ++ case WMI_TWT_RESUME_DIALOG_EVENTID: + case WMI_PDEV_DMA_RING_CFG_RSP_EVENTID: + case WMI_PEER_CREATE_CONF_EVENTID: + ath11k_dbg(ab, ATH11K_DBG_WMI, +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -4953,6 +4953,112 @@ struct wmi_twt_disable_params_cmd { + u32 pdev_id; + } __packed; + ++enum WMI_HOST_TWT_COMMAND { ++ WMI_HOST_TWT_COMMAND_REQUEST_TWT = 0, ++ WMI_HOST_TWT_COMMAND_SUGGEST_TWT, ++ WMI_HOST_TWT_COMMAND_DEMAND_TWT, ++ WMI_HOST_TWT_COMMAND_TWT_GROUPING, ++ WMI_HOST_TWT_COMMAND_ACCEPT_TWT, ++ WMI_HOST_TWT_COMMAND_ALTERNATE_TWT, ++ WMI_HOST_TWT_COMMAND_DICTATE_TWT, ++ WMI_HOST_TWT_COMMAND_REJECT_TWT, ++}; ++ ++#define WMI_TWT_ADD_DIALOG_FLAG_BCAST BIT(8) ++#define WMI_TWT_ADD_DIALOG_FLAG_TRIGGER BIT(9) ++#define WMI_TWT_ADD_DIALOG_FLAG_FLOW_TYPE BIT(10) ++#define WMI_TWT_ADD_DIALOG_FLAG_PROTECTION BIT(11) ++ ++struct wmi_twt_add_dialog_params_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ struct wmi_mac_addr peer_macaddr; ++ u32 dialog_id; ++ u32 wake_intvl_us; ++ u32 wake_intvl_mantis; ++ u32 wake_dura_us; ++ u32 sp_offset_us; ++ u32 flags; ++} __packed; ++ ++struct wmi_twt_add_dialog_params { ++ u32 vdev_id; ++ u8 peer_macaddr[ETH_ALEN]; ++ u32 dialog_id; ++ u32 wake_intvl_us; ++ u32 wake_intvl_mantis; ++ u32 wake_dura_us; ++ u32 sp_offset_us; ++ u8 twt_cmd; ++ u8 flag_bcast; ++ u8 flag_trigger; ++ u8 flag_flow_type; ++ u8 flag_protection; ++} __packed; ++ ++enum wmi_twt_add_dialog_status { ++ WMI_ADD_TWT_STATUS_OK, ++ WMI_ADD_TWT_STATUS_TWT_NOT_ENABLED, ++ WMI_ADD_TWT_STATUS_USED_DIALOG_ID, ++ WMI_ADD_TWT_STATUS_INVALID_PARAM, ++ WMI_ADD_TWT_STATUS_NOT_READY, ++ WMI_ADD_TWT_STATUS_NO_RESOURCE, ++ WMI_ADD_TWT_STATUS_NO_ACK, ++ WMI_ADD_TWT_STATUS_NO_RESPONSE, ++ WMI_ADD_TWT_STATUS_DENIED, ++ WMI_ADD_TWT_STATUS_UNKNOWN_ERROR, ++}; ++ ++struct wmi_twt_add_dialog_event { ++ u32 vdev_id; ++ struct wmi_mac_addr peer_macaddr; ++ u32 dialog_id; ++ u32 status; ++} __packed; ++ ++struct wmi_twt_del_dialog_params { ++ u32 vdev_id; ++ u8 peer_macaddr[ETH_ALEN]; ++ u32 dialog_id; ++} __packed; ++ ++struct wmi_twt_del_dialog_params_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ struct wmi_mac_addr peer_macaddr; ++ u32 dialog_id; ++} __packed; ++ ++struct wmi_twt_pause_dialog_params { ++ u32 vdev_id; ++ u8 peer_macaddr[ETH_ALEN]; ++ u32 dialog_id; ++} __packed; ++ ++struct wmi_twt_pause_dialog_params_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ struct wmi_mac_addr peer_macaddr; ++ u32 dialog_id; ++} __packed; ++ ++struct wmi_twt_resume_dialog_params { ++ u32 vdev_id; ++ u8 peer_macaddr[ETH_ALEN]; ++ u32 dialog_id; ++ u32 sp_offset_us; ++ u32 next_twt_size; ++} __packed; ++ ++struct wmi_twt_resume_dialog_params_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ struct wmi_mac_addr peer_macaddr; ++ u32 dialog_id; ++ u32 sp_offset_us; ++ u32 next_twt_size; ++} __packed; ++ + struct wmi_obss_spatial_reuse_params_cmd { + u32 tlv_header; + u32 pdev_id; +@@ -5562,6 +5668,14 @@ void ath11k_wmi_fw_stats_fill(struct ath + int ath11k_wmi_simulate_radar(struct ath11k *ar); + int ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id); + int ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id); ++int ath11k_wmi_send_twt_add_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_add_dialog_params *params); ++int ath11k_wmi_send_twt_del_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_del_dialog_params *params); ++int ath11k_wmi_send_twt_pause_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_pause_dialog_params *params); ++int ath11k_wmi_send_twt_resume_dialog_cmd(struct ath11k *ar, ++ struct wmi_twt_resume_dialog_params *params); + int ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, + struct ieee80211_he_obss_pd *he_obss_pd); + int ath11k_wmi_pdev_set_srg_bss_color_bitmap(struct ath11k *ar, u32 *bitmap); diff --git a/package/kernel/mac80211/patches/ath11k/0174-ath11k-add-debugfs-for-TWT-debug-calls.patch b/package/kernel/mac80211/patches/ath11k/0174-ath11k-add-debugfs-for-TWT-debug-calls.patch new file mode 100644 index 00000000000000..1cc77a650526e0 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0174-ath11k-add-debugfs-for-TWT-debug-calls.patch @@ -0,0 +1,347 @@ +From fe98a6137d03a9e51db2197674c355d64eb3b709 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 31 Jan 2022 16:16:44 +0200 +Subject: [PATCH] ath11k: add debugfs for TWT debug calls + +New debugfs files to manually add/delete/pause/resume TWT +dialogs for test/debug purposes. + +The debugfs files expect the following parameters +- Add dialog +echo ' + + <1:Broadcast /0:Individual> <1:Triggered / 0:Untriggered> + <1:Unannounced /0:Announced> <1:Protected / 0:Unprotected>' > + /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/twt/add_dialog + +Example (Non-triggered and un-announced): +echo '00:03:7F:20:13:52 1 102400 100 30720 20480 4 0 0 1 0' > + /sys/kernel/debug/ieee80211/phy0/netdev:wlan0/twt/add_dialog + +- Delete dialog +echo ' ' > + /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/twt/del_dialog + +- Pause dialog +echo ' ' > + /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/twt/pause_dialog + +- Resume dialog +echo ' ' > + /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/twt/resume_dialog + +Example: +echo '00:03:7F:20:13:52 1 2000000 3' > + /sys/kernel/debug/ieee80211/phy0/netdev:wlan0/twt/resume_dialog + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01179-QCAHKSWPL_SILICONZ-1 +Signed-off-by: John Crispin +Signed-off-by: Aloka Dixit +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220131031043.1295-2-alokad@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.h | 3 + + drivers/net/wireless/ath/ath11k/debugfs.c | 222 ++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/debugfs.h | 14 +- + drivers/net/wireless/ath/ath11k/mac.c | 7 + + 4 files changed, 245 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -263,6 +263,9 @@ struct ath11k_vif { + bool bcca_zero_sent; + bool do_not_send_tmpl; + struct ieee80211_chanctx_conf chanctx; ++#ifdef CPTCFG_ATH11K_DEBUGFS ++ struct dentry *debugfs_twt; ++#endif /* CPTCFG_ATH11K_DEBUGFS */ + }; + + struct ath11k_vif_iter { +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -1224,3 +1224,225 @@ int ath11k_debugfs_register(struct ath11 + void ath11k_debugfs_unregister(struct ath11k *ar) + { + } ++ ++static ssize_t ath11k_write_twt_add_dialog(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct wmi_twt_add_dialog_params params = { 0 }; ++ u8 buf[128] = {0}; ++ int ret; ++ ++ if (arvif->ar->twt_enabled == 0) { ++ ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, ++ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u %u %u %hhu %hhu %hhu %hhu %hhu", ++ ¶ms.peer_macaddr[0], ++ ¶ms.peer_macaddr[1], ++ ¶ms.peer_macaddr[2], ++ ¶ms.peer_macaddr[3], ++ ¶ms.peer_macaddr[4], ++ ¶ms.peer_macaddr[5], ++ ¶ms.dialog_id, ++ ¶ms.wake_intvl_us, ++ ¶ms.wake_intvl_mantis, ++ ¶ms.wake_dura_us, ++ ¶ms.sp_offset_us, ++ ¶ms.twt_cmd, ++ ¶ms.flag_bcast, ++ ¶ms.flag_trigger, ++ ¶ms.flag_flow_type, ++ ¶ms.flag_protection); ++ if (ret != 16) ++ return -EINVAL; ++ ++ params.vdev_id = arvif->vdev_id; ++ ++ ret = ath11k_wmi_send_twt_add_dialog_cmd(arvif->ar, ¶ms); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static ssize_t ath11k_write_twt_del_dialog(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct wmi_twt_del_dialog_params params = { 0 }; ++ u8 buf[64] = {0}; ++ int ret; ++ ++ if (arvif->ar->twt_enabled == 0) { ++ ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u", ++ ¶ms.peer_macaddr[0], ++ ¶ms.peer_macaddr[1], ++ ¶ms.peer_macaddr[2], ++ ¶ms.peer_macaddr[3], ++ ¶ms.peer_macaddr[4], ++ ¶ms.peer_macaddr[5], ++ ¶ms.dialog_id); ++ if (ret != 7) ++ return -EINVAL; ++ ++ params.vdev_id = arvif->vdev_id; ++ ++ ret = ath11k_wmi_send_twt_del_dialog_cmd(arvif->ar, ¶ms); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static ssize_t ath11k_write_twt_pause_dialog(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct wmi_twt_pause_dialog_params params = { 0 }; ++ u8 buf[64] = {0}; ++ int ret; ++ ++ if (arvif->ar->twt_enabled == 0) { ++ ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u", ++ ¶ms.peer_macaddr[0], ++ ¶ms.peer_macaddr[1], ++ ¶ms.peer_macaddr[2], ++ ¶ms.peer_macaddr[3], ++ ¶ms.peer_macaddr[4], ++ ¶ms.peer_macaddr[5], ++ ¶ms.dialog_id); ++ if (ret != 7) ++ return -EINVAL; ++ ++ params.vdev_id = arvif->vdev_id; ++ ++ ret = ath11k_wmi_send_twt_pause_dialog_cmd(arvif->ar, ¶ms); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static ssize_t ath11k_write_twt_resume_dialog(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct wmi_twt_resume_dialog_params params = { 0 }; ++ u8 buf[64] = {0}; ++ int ret; ++ ++ if (arvif->ar->twt_enabled == 0) { ++ ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u", ++ ¶ms.peer_macaddr[0], ++ ¶ms.peer_macaddr[1], ++ ¶ms.peer_macaddr[2], ++ ¶ms.peer_macaddr[3], ++ ¶ms.peer_macaddr[4], ++ ¶ms.peer_macaddr[5], ++ ¶ms.dialog_id, ++ ¶ms.sp_offset_us, ++ ¶ms.next_twt_size); ++ if (ret != 9) ++ return -EINVAL; ++ ++ params.vdev_id = arvif->vdev_id; ++ ++ ret = ath11k_wmi_send_twt_resume_dialog_cmd(arvif->ar, ¶ms); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static const struct file_operations ath11k_fops_twt_add_dialog = { ++ .write = ath11k_write_twt_add_dialog, ++ .open = simple_open ++}; ++ ++static const struct file_operations ath11k_fops_twt_del_dialog = { ++ .write = ath11k_write_twt_del_dialog, ++ .open = simple_open ++}; ++ ++static const struct file_operations ath11k_fops_twt_pause_dialog = { ++ .write = ath11k_write_twt_pause_dialog, ++ .open = simple_open ++}; ++ ++static const struct file_operations ath11k_fops_twt_resume_dialog = { ++ .write = ath11k_write_twt_resume_dialog, ++ .open = simple_open ++}; ++ ++int ath11k_debugfs_add_interface(struct ath11k_vif *arvif) ++{ ++ if (arvif->vif->type == NL80211_IFTYPE_AP && !arvif->debugfs_twt) { ++ arvif->debugfs_twt = debugfs_create_dir("twt", ++ arvif->vif->debugfs_dir); ++ if (!arvif->debugfs_twt || IS_ERR(arvif->debugfs_twt)) { ++ ath11k_warn(arvif->ar->ab, ++ "failed to create directory %p\n", ++ arvif->debugfs_twt); ++ arvif->debugfs_twt = NULL; ++ return -1; ++ } ++ ++ debugfs_create_file("add_dialog", 0200, arvif->debugfs_twt, ++ arvif, &ath11k_fops_twt_add_dialog); ++ ++ debugfs_create_file("del_dialog", 0200, arvif->debugfs_twt, ++ arvif, &ath11k_fops_twt_del_dialog); ++ ++ debugfs_create_file("pause_dialog", 0200, arvif->debugfs_twt, ++ arvif, &ath11k_fops_twt_pause_dialog); ++ ++ debugfs_create_file("resume_dialog", 0200, arvif->debugfs_twt, ++ arvif, &ath11k_fops_twt_resume_dialog); ++ } ++ return 0; ++} ++ ++void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif) ++{ ++ debugfs_remove_recursive(arvif->debugfs_twt); ++ arvif->debugfs_twt = NULL; ++} +--- a/drivers/net/wireless/ath/ath11k/debugfs.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs.h +@@ -276,6 +276,9 @@ static inline int ath11k_debugfs_rx_filt + return ar->debug.rx_filter; + } + ++int ath11k_debugfs_add_interface(struct ath11k_vif *arvif); ++void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif); ++ + #else + static inline int ath11k_debugfs_soc_create(struct ath11k_base *ab) + { +@@ -349,6 +352,15 @@ static inline int ath11k_debugfs_get_fw_ + return 0; + } + +-#endif /* CPTCFG_MAC80211_DEBUGFS*/ ++static inline int ath11k_debugfs_add_interface(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ ++static inline void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif) ++{ ++} ++ ++#endif /* CPTCFG_ATH11K_DEBUGFS*/ + + #endif /* _ATH11K_DEBUGFS_H_ */ +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6354,6 +6354,10 @@ static int ath11k_mac_op_add_interface(s + } + } + ++ ret = ath11k_debugfs_add_interface(arvif); ++ if (ret) ++ goto err_peer_del; ++ + mutex_unlock(&ar->conf_mutex); + + return 0; +@@ -6388,6 +6392,7 @@ err_vdev_del: + spin_unlock_bh(&ar->data_lock); + + err: ++ ath11k_debugfs_remove_interface(arvif); + mutex_unlock(&ar->conf_mutex); + + return ret; +@@ -6486,6 +6491,8 @@ err_vdev_del: + /* Recalc txpower for remaining vdev */ + ath11k_mac_txpower_recalc(ar); + ++ ath11k_debugfs_remove_interface(arvif); ++ + /* TODO: recal traffic pause state based on the available vdevs */ + + mutex_unlock(&ar->conf_mutex); diff --git a/package/kernel/mac80211/patches/ath11k/0176-ath11k-fix-uninitialized-rate_idx-in-ath11k_dp_tx_up.patch b/package/kernel/mac80211/patches/ath11k/0176-ath11k-fix-uninitialized-rate_idx-in-ath11k_dp_tx_up.patch new file mode 100644 index 00000000000000..138f24f593fc79 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0176-ath11k-fix-uninitialized-rate_idx-in-ath11k_dp_tx_up.patch @@ -0,0 +1,31 @@ +From 8c4c567fa291e4805d5116f1333b2ed83877032b Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 9 Feb 2022 01:08:16 -0500 +Subject: [PATCH] ath11k: fix uninitialized rate_idx in + ath11k_dp_tx_update_txcompl() + +The rate_idx which passed to ath11k_debugfs_sta_add_tx_stats() by +ath11k_dp_tx_update_txcompl() is not initialized, add initialization +for it. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Fixes: 1b8bb94c0612 ("ath11k: report tx bitrate for iw wlan station dump") +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220209060816.423-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -426,7 +426,7 @@ void ath11k_dp_tx_update_txcompl(struct + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; + u16 rate, ru_tones; +- u8 mcs, rate_idx, ofdma; ++ u8 mcs, rate_idx = 0, ofdma; + int ret; + + spin_lock_bh(&ab->base_lock); diff --git a/package/kernel/mac80211/patches/ath11k/0177-ath11k-fix-WARN_ON-during-ath11k_mac_update_vif_chan.patch b/package/kernel/mac80211/patches/ath11k/0177-ath11k-fix-WARN_ON-during-ath11k_mac_update_vif_chan.patch new file mode 100644 index 00000000000000..d41724a1f4ea1a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0177-ath11k-fix-WARN_ON-during-ath11k_mac_update_vif_chan.patch @@ -0,0 +1,63 @@ +From 5ed98fb704d97894a2c7217989fd8290e4b7b985 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Wed, 9 Feb 2022 19:43:38 +0530 +Subject: [PATCH] ath11k: fix WARN_ON during ath11k_mac_update_vif_chan + +Fix WARN_ON() from ath11k_mac_update_vif_chan() if vdev is not up. +Since change_chanctx can be called even before vdev_up from +ieee80211_start_ap->ieee80211_vif_use_channel-> +ieee80211_recalc_radar_chanctx. + +Do vdev stop followed by a vdev start in case of vdev is down. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644416019-820-1-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 32 ++++++++++++++++++++++----- + 1 file changed, 26 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6856,13 +6856,33 @@ ath11k_mac_update_vif_chan(struct ath11k + if (WARN_ON(!arvif->is_started)) + continue; + +- if (WARN_ON(!arvif->is_up)) +- continue; ++ /* change_chanctx can be called even before vdev_up from ++ * ieee80211_start_ap->ieee80211_vif_use_channel-> ++ * ieee80211_recalc_radar_chanctx. ++ * ++ * Firmware expect vdev_restart only if vdev is up. ++ * If vdev is down then it expect vdev_stop->vdev_start. ++ */ ++ if (arvif->is_up) { ++ ret = ath11k_mac_vdev_restart(arvif, &vifs[i].new_ctx->def); ++ if (ret) { ++ ath11k_warn(ab, "failed to restart vdev %d: %d\n", ++ arvif->vdev_id, ret); ++ continue; ++ } ++ } else { ++ ret = ath11k_mac_vdev_stop(arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to stop vdev %d: %d\n", ++ arvif->vdev_id, ret); ++ continue; ++ } ++ ++ ret = ath11k_mac_vdev_start(arvif, &vifs[i].new_ctx->def); ++ if (ret) ++ ath11k_warn(ab, "failed to start vdev %d: %d\n", ++ arvif->vdev_id, ret); + +- ret = ath11k_mac_vdev_restart(arvif, &vifs[i].new_ctx->def); +- if (ret) { +- ath11k_warn(ab, "failed to restart vdev %d: %d\n", +- arvif->vdev_id, ret); + continue; + } + diff --git a/package/kernel/mac80211/patches/ath11k/0178-ath11k-fix-radar-detection-in-160-Mhz.patch b/package/kernel/mac80211/patches/ath11k/0178-ath11k-fix-radar-detection-in-160-Mhz.patch new file mode 100644 index 00000000000000..64f34d3c1088cd --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0178-ath11k-fix-radar-detection-in-160-Mhz.patch @@ -0,0 +1,118 @@ +From 4f6dd92305f74c43f09e2ff867011e0029ee0e0d Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Wed, 9 Feb 2022 19:43:39 +0530 +Subject: [PATCH] ath11k: fix radar detection in 160 Mhz + +Radar detection fails in the secondary 80 MHz when the +the AP's primary 80 MHz is in non-DFS region in 160 MHz. + +This is due to WMI channel flag WMI_CHAN_INFO_DFS_FREQ2 is not set +properly in case of the primary 80 MHz is in non-DFS region. +HALPHY detects the radar pulses in the secondary 80 MHz only when +WMI_CHAN_INFO_DFS_FREQ2 is set. + +Fix this issue by setting WMI channel flag WMI_CHAN_INFO_DFS_FREQ2 +based on the radar_enabled flag from the channel context. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644416019-820-2-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 25 +++++++++++++------------ + 1 file changed, 13 insertions(+), 12 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6630,12 +6630,13 @@ static void ath11k_mac_op_remove_chanctx + + static int + ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, +- const struct cfg80211_chan_def *chandef, ++ struct ieee80211_chanctx_conf *ctx, + bool restart) + { + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct wmi_vdev_start_req_arg arg = {}; ++ const struct cfg80211_chan_def *chandef = &ctx->def; + int he_support = arvif->vif->bss_conf.he_support; + int ret = 0; + +@@ -6670,8 +6671,7 @@ ath11k_mac_vdev_start_restart(struct ath + arg.channel.chan_radar = + !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); + +- arg.channel.freq2_radar = +- !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); ++ arg.channel.freq2_radar = ctx->radar_enabled; + + arg.channel.passive = arg.channel.chan_radar; + +@@ -6781,15 +6781,15 @@ err: + } + + static int ath11k_mac_vdev_start(struct ath11k_vif *arvif, +- const struct cfg80211_chan_def *chandef) ++ struct ieee80211_chanctx_conf *ctx) + { +- return ath11k_mac_vdev_start_restart(arvif, chandef, false); ++ return ath11k_mac_vdev_start_restart(arvif, ctx, false); + } + + static int ath11k_mac_vdev_restart(struct ath11k_vif *arvif, +- const struct cfg80211_chan_def *chandef) ++ struct ieee80211_chanctx_conf *ctx) + { +- return ath11k_mac_vdev_start_restart(arvif, chandef, true); ++ return ath11k_mac_vdev_start_restart(arvif, ctx, true); + } + + struct ath11k_mac_change_chanctx_arg { +@@ -6864,7 +6864,7 @@ ath11k_mac_update_vif_chan(struct ath11k + * If vdev is down then it expect vdev_stop->vdev_start. + */ + if (arvif->is_up) { +- ret = ath11k_mac_vdev_restart(arvif, &vifs[i].new_ctx->def); ++ ret = ath11k_mac_vdev_restart(arvif, vifs[i].new_ctx); + if (ret) { + ath11k_warn(ab, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); +@@ -6878,7 +6878,7 @@ ath11k_mac_update_vif_chan(struct ath11k + continue; + } + +- ret = ath11k_mac_vdev_start(arvif, &vifs[i].new_ctx->def); ++ ret = ath11k_mac_vdev_start(arvif, vifs[i].new_ctx); + if (ret) + ath11k_warn(ab, "failed to start vdev %d: %d\n", + arvif->vdev_id, ret); +@@ -6967,7 +6967,8 @@ static void ath11k_mac_op_change_chanctx + if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) + goto unlock; + +- if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) ++ if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH || ++ changed & IEEE80211_CHANCTX_CHANGE_RADAR) + ath11k_mac_update_active_vif_chan(ar, ctx); + + /* TODO: Recalc radar detection */ +@@ -6987,7 +6988,7 @@ static int ath11k_start_vdev_delay(struc + if (WARN_ON(arvif->is_started)) + return -EBUSY; + +- ret = ath11k_mac_vdev_start(arvif, &arvif->chanctx.def); ++ ret = ath11k_mac_vdev_start(arvif, &arvif->chanctx); + if (ret) { + ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", + arvif->vdev_id, vif->addr, +@@ -7081,7 +7082,7 @@ ath11k_mac_op_assign_vif_chanctx(struct + goto out; + } + +- ret = ath11k_mac_vdev_start(arvif, &ctx->def); ++ ret = ath11k_mac_vdev_start(arvif, ctx); + if (ret) { + ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", + arvif->vdev_id, vif->addr, diff --git a/package/kernel/mac80211/patches/ath11k/0179-ath11k-fix-destination-monitor-ring-out-of-sync.patch b/package/kernel/mac80211/patches/ath11k/0179-ath11k-fix-destination-monitor-ring-out-of-sync.patch new file mode 100644 index 00000000000000..5352ae89a42d10 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0179-ath11k-fix-destination-monitor-ring-out-of-sync.patch @@ -0,0 +1,111 @@ +From 1e15aacd12386d8f1372929a3fd52db9ef3344fc Mon Sep 17 00:00:00 2001 +From: Karthikeyan Kathirvel +Date: Thu, 10 Feb 2022 12:17:06 +0530 +Subject: [PATCH] ath11k: fix destination monitor ring out of sync + +More than 20000 PPDU id jumping causing status ring and destination +ring processing not sync. The status ring is processed and the +destination ring is not processed. Since destination is not reaped for +so long, backpressure occurs at the destination ring. + +To address this issue update the PPDU id with the latest PPDU, this +will allow the destination ring to be reaped and will prevent the +rings from getting out of sync. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1.r1-00026-QCAHKSWPL_SILICONZ-2 +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220210064706.6171-1-quic_kathirve@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp.h | 3 ++ + drivers/net/wireless/ath/ath11k/dp_rx.c | 42 +++++++++++++++++++++---- + 2 files changed, 39 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -115,6 +115,8 @@ struct ath11k_pdev_mon_stats { + u32 dest_mpdu_drop; + u32 dup_mon_linkdesc_cnt; + u32 dup_mon_buf_cnt; ++ u32 dest_mon_stuck; ++ u32 dest_mon_not_reaped; + }; + + struct dp_full_mon_mpdu { +@@ -167,6 +169,7 @@ struct ath11k_mon_data { + + struct ath11k_pdev_dp { + u32 mac_id; ++ u32 mon_dest_ring_stuck_cnt; + atomic_t num_tx_pending; + wait_queue_head_t tx_empty_waitq; + struct dp_rxdma_ring rx_refill_buf_ring; +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -4959,6 +4959,12 @@ mon_deliver_fail: + return -EINVAL; + } + ++/* The destination ring processing is stuck if the destination is not ++ * moving while status ring moves 16 PPDU. The destination ring processing ++ * skips this destination ring PPDU as a workaround. ++ */ ++#define MON_DEST_RING_STUCK_MAX_CNT 16 ++ + static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, + u32 quota, struct napi_struct *napi) + { +@@ -4972,6 +4978,7 @@ static void ath11k_dp_rx_mon_dest_proces + u32 ring_id; + struct ath11k_pdev_mon_stats *rx_mon_stats; + u32 npackets = 0; ++ u32 mpdu_rx_bufs_used; + + if (ar->ab->hw_params.rxdma1_enable) + ring_id = dp->rxdma_mon_dst_ring.ring_id; +@@ -5001,16 +5008,39 @@ static void ath11k_dp_rx_mon_dest_proces + head_msdu = NULL; + tail_msdu = NULL; + +- rx_bufs_used += ath11k_dp_rx_mon_mpdu_pop(ar, mac_id, ring_entry, +- &head_msdu, +- &tail_msdu, +- &npackets, &ppdu_id); ++ mpdu_rx_bufs_used = ath11k_dp_rx_mon_mpdu_pop(ar, mac_id, ring_entry, ++ &head_msdu, ++ &tail_msdu, ++ &npackets, &ppdu_id); ++ ++ rx_bufs_used += mpdu_rx_bufs_used; ++ ++ if (mpdu_rx_bufs_used) { ++ dp->mon_dest_ring_stuck_cnt = 0; ++ } else { ++ dp->mon_dest_ring_stuck_cnt++; ++ rx_mon_stats->dest_mon_not_reaped++; ++ } ++ ++ if (dp->mon_dest_ring_stuck_cnt > MON_DEST_RING_STUCK_MAX_CNT) { ++ rx_mon_stats->dest_mon_stuck++; ++ ath11k_dbg(ar->ab, ATH11K_DBG_DATA, ++ "status ring ppdu_id=%d dest ring ppdu_id=%d mon_dest_ring_stuck_cnt=%d dest_mon_not_reaped=%u dest_mon_stuck=%u\n", ++ pmon->mon_ppdu_info.ppdu_id, ppdu_id, ++ dp->mon_dest_ring_stuck_cnt, ++ rx_mon_stats->dest_mon_not_reaped, ++ rx_mon_stats->dest_mon_stuck); ++ pmon->mon_ppdu_info.ppdu_id = ppdu_id; ++ continue; ++ } + + if (ppdu_id != pmon->mon_ppdu_info.ppdu_id) { + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, +- "dest_rx: new ppdu_id %x != status ppdu_id %x", +- ppdu_id, pmon->mon_ppdu_info.ppdu_id); ++ "dest_rx: new ppdu_id %x != status ppdu_id %x dest_mon_not_reaped = %u dest_mon_stuck = %u\n", ++ ppdu_id, pmon->mon_ppdu_info.ppdu_id, ++ rx_mon_stats->dest_mon_not_reaped, ++ rx_mon_stats->dest_mon_stuck); + break; + } + if (head_msdu && tail_msdu) { diff --git a/package/kernel/mac80211/patches/ath11k/0181-ath11k-add-ath11k_qmi_free_resource-for-recovery.patch b/package/kernel/mac80211/patches/ath11k/0181-ath11k-add-ath11k_qmi_free_resource-for-recovery.patch new file mode 100644 index 00000000000000..831cfb2bb8bcb3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0181-ath11k-add-ath11k_qmi_free_resource-for-recovery.patch @@ -0,0 +1,38 @@ +From 5f71968e3c769b1d3c3cb0831ef870663b3c1f89 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 14 Feb 2022 19:53:16 +0200 +Subject: [PATCH] ath11k: add ath11k_qmi_free_resource() for recovery + +ath11k_qmi_free_target_mem_chunk() and ath11k_qmi_m3_free() is static +in qmi.c, they are needed for recovery, export them in a new function. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220209060012.32478-2-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 5 +++++ + drivers/net/wireless/ath/ath11k/qmi.h | 1 + + 2 files changed, 6 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -3025,3 +3025,8 @@ void ath11k_qmi_deinit_service(struct at + } + EXPORT_SYMBOL(ath11k_qmi_deinit_service); + ++void ath11k_qmi_free_resource(struct ath11k_base *ab) ++{ ++ ath11k_qmi_free_target_mem_chunk(ab); ++ ath11k_qmi_m3_free(ab); ++} +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -492,5 +492,6 @@ void ath11k_qmi_event_work(struct work_s + void ath11k_qmi_msg_recv_work(struct work_struct *work); + void ath11k_qmi_deinit_service(struct ath11k_base *ab); + int ath11k_qmi_init_service(struct ath11k_base *ab); ++void ath11k_qmi_free_resource(struct ath11k_base *ab); + + #endif diff --git a/package/kernel/mac80211/patches/ath11k/0182-ath11k-fix-invalid-m3-buffer-address.patch b/package/kernel/mac80211/patches/ath11k/0182-ath11k-fix-invalid-m3-buffer-address.patch new file mode 100644 index 00000000000000..1c8ceb97e04779 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0182-ath11k-fix-invalid-m3-buffer-address.patch @@ -0,0 +1,29 @@ +From e52b6a02bfc00d74cee385dea60f9894121c1f2d Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 14 Feb 2022 19:53:16 +0200 +Subject: [PATCH] ath11k: fix invalid m3 buffer address + +This is to fix m3 buffer reuse issue as m3_mem->size isn't set to +ZERO in free function, which leads invalid m3 downloading to +firmware and firmware crashed. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Carl Huang +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220209060012.32478-3-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2342,6 +2342,7 @@ static void ath11k_qmi_m3_free(struct at + dma_free_coherent(ab->dev, m3_mem->size, + m3_mem->vaddr, m3_mem->paddr); + m3_mem->vaddr = NULL; ++ m3_mem->size = 0; + } + + static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) diff --git a/package/kernel/mac80211/patches/ath11k/0183-ath11k-configure-RDDM-size-to-mhi-for-recovery-by-fi.patch b/package/kernel/mac80211/patches/ath11k/0183-ath11k-configure-RDDM-size-to-mhi-for-recovery-by-fi.patch new file mode 100644 index 00000000000000..865aa9836d4654 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0183-ath11k-configure-RDDM-size-to-mhi-for-recovery-by-fi.patch @@ -0,0 +1,35 @@ +From 03e0add74212b63674ffa1304e55d6c929fb8458 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 14 Feb 2022 19:53:16 +0200 +Subject: [PATCH] ath11k: configure RDDM size to mhi for recovery by firmware + +The rddm_size is needed by firmware while mhi enter RDDM state, add it +to support recovery when ath11k receive MHI_CB_EE_RDDM message. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220209060012.32478-4-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mhi.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -13,6 +13,7 @@ + #include "pci.h" + + #define MHI_TIMEOUT_DEFAULT_MS 90000 ++#define RDDM_DUMP_SIZE 0x420000 + + static struct mhi_channel_config ath11k_mhi_channels_qca6390[] = { + { +@@ -384,6 +385,7 @@ int ath11k_mhi_register(struct ath11k_pc + mhi_ctrl->iova_stop = 0xFFFFFFFF; + } + ++ mhi_ctrl->rddm_size = RDDM_DUMP_SIZE; + mhi_ctrl->sbl_size = SZ_512K; + mhi_ctrl->seg_len = SZ_512K; + mhi_ctrl->fbc_download = true; diff --git a/package/kernel/mac80211/patches/ath11k/0184-ath11k-Replace-zero-length-arrays-with-flexible-arra.patch b/package/kernel/mac80211/patches/ath11k/0184-ath11k-Replace-zero-length-arrays-with-flexible-arra.patch new file mode 100644 index 00000000000000..3099cccf88f634 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0184-ath11k-Replace-zero-length-arrays-with-flexible-arra.patch @@ -0,0 +1,137 @@ +From e9e591686ccb51d53048dabe8e6020b5a3bba45d Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Wed, 16 Feb 2022 13:48:36 -0600 +Subject: [PATCH] ath11k: Replace zero-length arrays with flexible-array + members +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There is a regular need in the kernel to provide a way to declare +having a dynamically sized set of trailing elements in a structure. +Kernel code should always use “flexible array members”[1] for these +cases. The older style of one-element or zero-length arrays should +no longer be used[2]. + +[1] https://en.wikipedia.org/wiki/Flexible_array_member +[2] https://www.kernel.org/doc/html/v5.16/process/deprecated.html#zero-length-and-one-element-arrays + +Link: https://github.com/KSPP/linux/issues/78 +Signed-off-by: Gustavo A. R. Silva +Reviewed-by: Kees Cook +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220216194836.GA904035@embeddedor +--- + drivers/net/wireless/ath/ath11k/ce.h | 2 +- + drivers/net/wireless/ath/ath11k/core.h | 2 +- + drivers/net/wireless/ath/ath11k/dp.h | 10 +++++----- + drivers/net/wireless/ath/ath11k/rx_desc.h | 6 +++--- + drivers/net/wireless/ath/ath11k/spectral.c | 2 +- + 5 files changed, 11 insertions(+), 11 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/ce.h ++++ b/drivers/net/wireless/ath/ath11k/ce.h +@@ -145,7 +145,7 @@ struct ath11k_ce_ring { + u32 hal_ring_id; + + /* keep last */ +- struct sk_buff *skb[0]; ++ struct sk_buff *skb[]; + }; + + struct ath11k_ce_pipe { +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -815,7 +815,7 @@ struct ath11k_base { + } id; + + /* must be last */ +- u8 drv_priv[0] __aligned(sizeof(void *)); ++ u8 drv_priv[] __aligned(sizeof(void *)); + }; + + struct ath11k_fw_stats_pdev { +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -1173,12 +1173,12 @@ struct ath11k_htt_ppdu_stats_msg { + u32 ppdu_id; + u32 timestamp; + u32 rsvd; +- u8 data[0]; ++ u8 data[]; + } __packed; + + struct htt_tlv { + u32 header; +- u8 value[0]; ++ u8 value[]; + } __packed; + + #define HTT_TLV_TAG GENMASK(11, 0) +@@ -1365,7 +1365,7 @@ struct htt_ppdu_stats_usr_cmn_array { + * tx_ppdu_stats_info is variable length, with length = + * number_of_ppdu_stats * sizeof (struct htt_tx_ppdu_stats_info) + */ +- struct htt_tx_ppdu_stats_info tx_ppdu_info[0]; ++ struct htt_tx_ppdu_stats_info tx_ppdu_info[]; + } __packed; + + struct htt_ppdu_user_stats { +@@ -1427,7 +1427,7 @@ struct htt_ppdu_stats_info { + */ + struct htt_pktlog_msg { + u32 hdr; +- u8 payload[0]; ++ u8 payload[]; + }; + + /** +@@ -1648,7 +1648,7 @@ struct ath11k_htt_extd_stats_msg { + u32 info0; + u64 cookie; + u32 info1; +- u8 data[0]; ++ u8 data[]; + } __packed; + + #define HTT_MAC_ADDR_L32_0 GENMASK(7, 0) +--- a/drivers/net/wireless/ath/ath11k/rx_desc.h ++++ b/drivers/net/wireless/ath/ath11k/rx_desc.h +@@ -1445,7 +1445,7 @@ struct hal_rx_desc_ipq8074 { + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; +- u8 msdu_payload[0]; ++ u8 msdu_payload[]; + } __packed; + + struct hal_rx_desc_qcn9074 { +@@ -1464,7 +1464,7 @@ struct hal_rx_desc_qcn9074 { + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; +- u8 msdu_payload[0]; ++ u8 msdu_payload[]; + } __packed; + + struct hal_rx_desc_wcn6855 { +@@ -1483,7 +1483,7 @@ struct hal_rx_desc_wcn6855 { + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; +- u8 msdu_payload[0]; ++ u8 msdu_payload[]; + } __packed; + + struct hal_rx_desc { +--- a/drivers/net/wireless/ath/ath11k/spectral.c ++++ b/drivers/net/wireless/ath/ath11k/spectral.c +@@ -107,7 +107,7 @@ struct spectral_search_fft_report { + __le32 info1; + __le32 info2; + __le32 reserve0; +- u8 bins[0]; ++ u8 bins[]; + } __packed; + + struct ath11k_spectral_search_report { diff --git a/package/kernel/mac80211/patches/ath11k/0185-ath11k-Invalidate-cached-reo-ring-entry-before-acces.patch b/package/kernel/mac80211/patches/ath11k/0185-ath11k-Invalidate-cached-reo-ring-entry-before-acces.patch new file mode 100644 index 00000000000000..f491abc7491687 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0185-ath11k-Invalidate-cached-reo-ring-entry-before-acces.patch @@ -0,0 +1,44 @@ +From f2180ccb52b5fd0876291ad2df37e2898cac18cf Mon Sep 17 00:00:00 2001 +From: Rameshkumar Sundaram +Date: Wed, 16 Feb 2022 14:02:34 +0530 +Subject: [PATCH] ath11k: Invalidate cached reo ring entry before accessing it + +REO2SW ring descriptor is currently allocated in cacheable memory. +While reaping reo ring entries on second trial after updating head +pointer, first entry is not invalidated before accessing it. + +This results in host reaping and using cached descriptor which is +already overwritten in memory by DMA device (HW). +Since the contents of descriptor(buffer id, peer info and other information +bits) are outdated host throws errors like below while parsing corresponding +MSDU's and drops them. + +[347712.048904] ath11k_pci 0004:01:00.0: msdu_done bit in attention is not set +[349173.355503] ath11k_pci 0004:01:00.0: frame rx with invalid buf_id 962 + +Move the try_again: label above ath11k_hal_srng_access_begin() +so that first entry will be invalidated and prefetched. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Fixes: 6452f0a3d565 ("ath11k: allocate dst ring descriptors from cacheable memory") +Signed-off-by: Rameshkumar Sundaram +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1645000354-32558-1-git-send-email-quic_ramess@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2652,9 +2652,9 @@ int ath11k_dp_process_rx(struct ath11k_b + + spin_lock_bh(&srng->lock); + ++try_again: + ath11k_hal_srng_access_begin(ab, srng); + +-try_again: + while (likely(desc = + (struct hal_reo_dest_ring *)ath11k_hal_srng_dst_get_next_entry(ab, + srng))) { diff --git a/package/kernel/mac80211/patches/ath11k/0186-ath11k-Handle-failure-in-qmi-firmware-ready.patch b/package/kernel/mac80211/patches/ath11k/0186-ath11k-Handle-failure-in-qmi-firmware-ready.patch new file mode 100644 index 00000000000000..2cd2a463c0d39e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0186-ath11k-Handle-failure-in-qmi-firmware-ready.patch @@ -0,0 +1,41 @@ +From a19c0e104db95f78540982c64d78217cd9830d72 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Thu, 17 Feb 2022 11:56:35 +0530 +Subject: [PATCH] ath11k: Handle failure in qmi firmware ready + +In some scenarios like firmware crashes during init time +and hardware gets restarted after qmi firmware ready event. +During restart, ath11k_core_qmi_firmware_ready() returns timeout. +But, this failure is not handled and ATH11K_FLAG_REGISTERED is set. + +When hardware restart completed, firmware sends firmware ready event +again. Since ATH11K_FLAG_REGISTERED is already set, ath11k handles +this as core restart. Inits are not done because of previous timeout. +But ath11k_core_restart does deinit's which causes NULL pointer crash. + +Fix this by handling failure from ath11k_core_qmi_firmware_ready(). + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-00881-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1645079195-13564-1-git-send-email-quic_seevalam@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2960,7 +2960,11 @@ static void ath11k_qmi_driver_event_work + clear_bit(ATH11K_FLAG_CRASH_FLUSH, + &ab->dev_flags); + clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); +- ath11k_core_qmi_firmware_ready(ab); ++ ret = ath11k_core_qmi_firmware_ready(ab); ++ if (ret) { ++ set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); ++ break; ++ } + set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); + } + diff --git a/package/kernel/mac80211/patches/ath11k/0187-ath11k-Fix-frames-flush-failure-caused-by-deadlock.patch b/package/kernel/mac80211/patches/ath11k/0187-ath11k-Fix-frames-flush-failure-caused-by-deadlock.patch new file mode 100644 index 00000000000000..003b62406fc112 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0187-ath11k-Fix-frames-flush-failure-caused-by-deadlock.patch @@ -0,0 +1,126 @@ +From 261b07519518bd14cb168b287b17e1d195f8d0c8 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Thu, 17 Feb 2022 16:45:45 +0800 +Subject: [PATCH] ath11k: Fix frames flush failure caused by deadlock + +We are seeing below warnings: + +kernel: [25393.301506] ath11k_pci 0000:01:00.0: failed to flush mgmt transmit queue 0 +kernel: [25398.421509] ath11k_pci 0000:01:00.0: failed to flush mgmt transmit queue 0 +kernel: [25398.421831] ath11k_pci 0000:01:00.0: dropping mgmt frame for vdev 0, is_started 0 + +this means ath11k fails to flush mgmt. frames because wmi_mgmt_tx_work +has no chance to run in 5 seconds. + +By setting /proc/sys/kernel/hung_task_timeout_secs to 20 and increasing +ATH11K_FLUSH_TIMEOUT to 50 we get below warnings: + +kernel: [ 120.763160] INFO: task wpa_supplicant:924 blocked for more than 20 seconds. +kernel: [ 120.763169] Not tainted 5.10.90 #12 +kernel: [ 120.763177] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +kernel: [ 120.763186] task:wpa_supplicant state:D stack: 0 pid: 924 ppid: 1 flags:0x000043a0 +kernel: [ 120.763201] Call Trace: +kernel: [ 120.763214] __schedule+0x785/0x12fa +kernel: [ 120.763224] ? lockdep_hardirqs_on_prepare+0xe2/0x1bb +kernel: [ 120.763242] schedule+0x7e/0xa1 +kernel: [ 120.763253] schedule_timeout+0x98/0xfe +kernel: [ 120.763266] ? run_local_timers+0x4a/0x4a +kernel: [ 120.763291] ath11k_mac_flush_tx_complete+0x197/0x2b1 [ath11k 13c3a9bf37790f4ac8103b3decf7ab4008ac314a] +kernel: [ 120.763306] ? init_wait_entry+0x2e/0x2e +kernel: [ 120.763343] __ieee80211_flush_queues+0x167/0x21f [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763378] __ieee80211_recalc_idle+0x105/0x125 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763411] ieee80211_recalc_idle+0x14/0x27 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763441] ieee80211_free_chanctx+0x77/0xa2 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763473] __ieee80211_vif_release_channel+0x100/0x131 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763540] ieee80211_vif_release_channel+0x66/0x81 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763572] ieee80211_destroy_auth_data+0xa3/0xe6 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763612] ieee80211_mgd_deauth+0x178/0x29b [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.763654] cfg80211_mlme_deauth+0x1a8/0x22c [cfg80211 8945aa5bc2af5f6972336665d8ad6f9c191ad5be] +kernel: [ 120.763697] nl80211_deauthenticate+0xfa/0x123 [cfg80211 8945aa5bc2af5f6972336665d8ad6f9c191ad5be] +kernel: [ 120.763715] genl_rcv_msg+0x392/0x3c2 +kernel: [ 120.763750] ? nl80211_associate+0x432/0x432 [cfg80211 8945aa5bc2af5f6972336665d8ad6f9c191ad5be] +kernel: [ 120.763782] ? nl80211_associate+0x432/0x432 [cfg80211 8945aa5bc2af5f6972336665d8ad6f9c191ad5be] +kernel: [ 120.763802] ? genl_rcv+0x36/0x36 +kernel: [ 120.763814] netlink_rcv_skb+0x89/0xf7 +kernel: [ 120.763829] genl_rcv+0x28/0x36 +kernel: [ 120.763840] netlink_unicast+0x179/0x24b +kernel: [ 120.763854] netlink_sendmsg+0x393/0x401 +kernel: [ 120.763872] sock_sendmsg+0x72/0x76 +kernel: [ 120.763886] ____sys_sendmsg+0x170/0x1e6 +kernel: [ 120.763897] ? copy_msghdr_from_user+0x7a/0xa2 +kernel: [ 120.763914] ___sys_sendmsg+0x95/0xd1 +kernel: [ 120.763940] __sys_sendmsg+0x85/0xbf +kernel: [ 120.763956] do_syscall_64+0x43/0x55 +kernel: [ 120.763966] entry_SYSCALL_64_after_hwframe+0x44/0xa9 +kernel: [ 120.763977] RIP: 0033:0x79089f3fcc83 +kernel: [ 120.763986] RSP: 002b:00007ffe604f0508 EFLAGS: 00000246 ORIG_RAX: 000000000000002e +kernel: [ 120.763997] RAX: ffffffffffffffda RBX: 000059b40e987690 RCX: 000079089f3fcc83 +kernel: [ 120.764006] RDX: 0000000000000000 RSI: 00007ffe604f0558 RDI: 0000000000000009 +kernel: [ 120.764014] RBP: 00007ffe604f0540 R08: 0000000000000004 R09: 0000000000400000 +kernel: [ 120.764023] R10: 00007ffe604f0638 R11: 0000000000000246 R12: 000059b40ea04980 +kernel: [ 120.764032] R13: 00007ffe604f0638 R14: 000059b40e98c360 R15: 00007ffe604f0558 +... +kernel: [ 120.765230] INFO: task kworker/u32:26:4239 blocked for more than 20 seconds. +kernel: [ 120.765238] Not tainted 5.10.90 #12 +kernel: [ 120.765245] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +kernel: [ 120.765253] task:kworker/u32:26 state:D stack: 0 pid: 4239 ppid: 2 flags:0x00004080 +kernel: [ 120.765284] Workqueue: phy0 ieee80211_iface_work [mac80211] +kernel: [ 120.765295] Call Trace: +kernel: [ 120.765306] __schedule+0x785/0x12fa +kernel: [ 120.765316] ? find_held_lock+0x3d/0xb2 +kernel: [ 120.765331] schedule+0x7e/0xa1 +kernel: [ 120.765340] schedule_preempt_disabled+0x15/0x1e +kernel: [ 120.765349] __mutex_lock_common+0x561/0xc0d +kernel: [ 120.765375] ? ieee80211_sta_work+0x3e/0x1232 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.765390] mutex_lock_nested+0x20/0x26 +kernel: [ 120.765416] ieee80211_sta_work+0x3e/0x1232 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.765430] ? skb_dequeue+0x54/0x5e +kernel: [ 120.765456] ? ieee80211_iface_work+0x7b/0x339 [mac80211 335da900954f1c5ea7f1613d92088ce83342042c] +kernel: [ 120.765485] process_one_work+0x270/0x504 +kernel: [ 120.765501] worker_thread+0x215/0x376 +kernel: [ 120.765514] kthread+0x159/0x168 +kernel: [ 120.765526] ? pr_cont_work+0x5b/0x5b +kernel: [ 120.765536] ? kthread_blkcg+0x31/0x31 +kernel: [ 120.765550] ret_from_fork+0x22/0x30 +... +kernel: [ 120.765867] Showing all locks held in the system: +... +kernel: [ 120.766164] 5 locks held by wpa_supplicant/924: +kernel: [ 120.766172] #0: ffffffffb1e63eb0 (cb_lock){++++}-{3:3}, at: genl_rcv+0x19/0x36 +kernel: [ 120.766197] #1: ffffffffb1e5b1c8 (rtnl_mutex){+.+.}-{3:3}, at: nl80211_pre_doit+0x2a/0x15c [cfg80211] +kernel: [ 120.766238] #2: ffff99f08347cd08 (&wdev->mtx){+.+.}-{3:3}, at: nl80211_deauthenticate+0xde/0x123 [cfg80211] +kernel: [ 120.766279] #3: ffff99f09df12a48 (&local->mtx){+.+.}-{3:3}, at: ieee80211_destroy_auth_data+0x9b/0xe6 [mac80211] +kernel: [ 120.766321] #4: ffff99f09df12ce0 (&local->chanctx_mtx){+.+.}-{3:3}, at: ieee80211_vif_release_channel+0x5e/0x81 [mac80211] +... +kernel: [ 120.766585] 3 locks held by kworker/u32:26/4239: +kernel: [ 120.766593] #0: ffff99f04458f948 ((wq_completion)phy0){+.+.}-{0:0}, at: process_one_work+0x19a/0x504 +kernel: [ 120.766621] #1: ffffbad54b3cfe50 ((work_completion)(&sdata->work)){+.+.}-{0:0}, at: process_one_work+0x1c0/0x504 +kernel: [ 120.766649] #2: ffff99f08347cd08 (&wdev->mtx){+.+.}-{3:3}, at: ieee80211_sta_work+0x3e/0x1232 [mac80211] + +With above info the issue is clear: First wmi_mgmt_tx_work is inserted +to local->workqueue after sdata->work inserted, then wpa_supplicant +acquires wdev->mtx in nl80211_deauthenticate and finally calls +ath11k_mac_op_flush where it waits all mgmt. frames to be sent out by +wmi_mgmt_tx_work. Meanwhile, sdata->work is blocked by wdev->mtx in +ieee80211_sta_work, as a result wmi_mgmt_tx_work has no chance to run. + +Change to use ab->workqueue instead of local->workqueue to fix this issue. + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220217084545.18844-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5579,7 +5579,7 @@ static int ath11k_mac_mgmt_tx(struct ath + + skb_queue_tail(q, skb); + atomic_inc(&ar->num_pending_mgmt_tx); +- ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); ++ queue_work(ar->ab->workqueue, &ar->wmi_mgmt_tx_work); + + return 0; + } diff --git a/package/kernel/mac80211/patches/ath11k/0188-ath11k-switch-to-using-ieee80211_tx_status_ext.patch b/package/kernel/mac80211/patches/ath11k/0188-ath11k-switch-to-using-ieee80211_tx_status_ext.patch new file mode 100644 index 00000000000000..c97d625533ee69 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0188-ath11k-switch-to-using-ieee80211_tx_status_ext.patch @@ -0,0 +1,65 @@ +From 94739d45c388c5c5be49bb0a5d911f672122d378 Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Wed, 16 Feb 2022 17:21:10 -0800 +Subject: [PATCH] ath11k: switch to using ieee80211_tx_status_ext() + +This allows us to pass HE rates down into the stack. + +Co-developed-by: Miles Hu +Signed-off-by: Miles Hu +Signed-off-by: John Crispin +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220217012112.31211-2-pradeepc@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 28 ++++++++++++++++++++----- + 1 file changed, 23 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -518,9 +518,13 @@ static void ath11k_dp_tx_complete_msdu(s + struct sk_buff *msdu, + struct hal_tx_status *ts) + { ++ struct ieee80211_tx_status status = { 0 }; + struct ath11k_base *ab = ar->ab; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; ++ struct ath11k_peer *peer; ++ struct ath11k_sta *arsta; ++ struct rate_info rate; + + if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { + /* Must not happen */ +@@ -583,12 +587,26 @@ static void ath11k_dp_tx_complete_msdu(s + ath11k_dp_tx_cache_peer_stats(ar, msdu, ts); + } + +- /* NOTE: Tx rate status reporting. Tx completion status does not have +- * necessary information (for example nss) to build the tx rate. +- * Might end up reporting it out-of-band from HTT stats. +- */ ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, ts->peer_id); ++ if (!peer || !peer->sta) { ++ ath11k_dbg(ab, ATH11K_DBG_DATA, ++ "dp_tx: failed to find the peer with peer_id %d\n", ++ ts->peer_id); ++ spin_unlock_bh(&ab->base_lock); ++ dev_kfree_skb_any(msdu); ++ return; ++ } ++ arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ status.sta = peer->sta; ++ status.skb = msdu; ++ status.info = info; ++ rate = arsta->last_txrate; ++ status.rate = &rate; ++ ++ spin_unlock_bh(&ab->base_lock); + +- ieee80211_tx_status(ar->hw, msdu); ++ ieee80211_tx_status_ext(ar->hw, &status); + } + + static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, diff --git a/package/kernel/mac80211/patches/ath11k/0189-ath11k-decode-HE-status-tlv.patch b/package/kernel/mac80211/patches/ath11k/0189-ath11k-decode-HE-status-tlv.patch new file mode 100644 index 00000000000000..ae811bb52b6764 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0189-ath11k-decode-HE-status-tlv.patch @@ -0,0 +1,424 @@ +From 01d2f285e3e5b629df9c61514e7ee07a54d0eed9 Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Wed, 16 Feb 2022 17:21:11 -0800 +Subject: [PATCH] ath11k: decode HE status tlv + +Add new bitmasks and macro definitions required for parsing HE +status tlvs. Decode HE status tlvs, which will used in dumping +ppdu stats as well as updating radiotap headers. + +Co-developed-by: Miles Hu +Signed-off-by: Miles Hu +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220217012112.31211-3-pradeepc@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 100 ++++++++++++--- + drivers/net/wireless/ath/ath11k/hal_desc.h | 1 + + drivers/net/wireless/ath/ath11k/hal_rx.h | 135 ++++++++++++++++++++- + 3 files changed, 215 insertions(+), 21 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -4807,7 +4807,6 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11 + { + struct ath11k_base *ab = ar->ab; + struct sk_buff *msdu, *prev_buf; +- u32 wifi_hdr_len; + struct hal_rx_desc *rx_desc; + char *hdr_desc; + u8 *dest, decap_format; +@@ -4849,38 +4848,27 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11 + + skb_trim(prev_buf, prev_buf->len - HAL_RX_FCS_LEN); + } else if (decap_format == DP_RX_DECAP_TYPE_NATIVE_WIFI) { +- __le16 qos_field; + u8 qos_pkt = 0; + + rx_desc = (struct hal_rx_desc *)head_msdu->data; + hdr_desc = ath11k_dp_rxdesc_get_80211hdr(ab, rx_desc); + + /* Base size */ +- wifi_hdr_len = sizeof(struct ieee80211_hdr_3addr); + wh = (struct ieee80211_hdr_3addr *)hdr_desc; + +- if (ieee80211_is_data_qos(wh->frame_control)) { +- struct ieee80211_qos_hdr *qwh = +- (struct ieee80211_qos_hdr *)hdr_desc; +- +- qos_field = qwh->qos_ctrl; ++ if (ieee80211_is_data_qos(wh->frame_control)) + qos_pkt = 1; +- } ++ + msdu = head_msdu; + + while (msdu) { +- rx_desc = (struct hal_rx_desc *)msdu->data; +- hdr_desc = ath11k_dp_rxdesc_get_80211hdr(ab, rx_desc); +- ++ ath11k_dp_rx_msdus_set_payload(ar, msdu); + if (qos_pkt) { + dest = skb_push(msdu, sizeof(__le16)); + if (!dest) + goto err_merge_fail; +- memcpy(dest, hdr_desc, wifi_hdr_len); +- memcpy(dest + wifi_hdr_len, +- (u8 *)&qos_field, sizeof(__le16)); ++ memcpy(dest, hdr_desc, sizeof(struct ieee80211_qos_hdr)); + } +- ath11k_dp_rx_msdus_set_payload(ar, msdu); + prev_buf = msdu; + msdu = msdu->next; + } +@@ -4904,8 +4892,83 @@ err_merge_fail: + return NULL; + } + ++static void ++ath11k_dp_rx_update_radiotap_he(struct hal_rx_mon_ppdu_info *rx_status, ++ u8 *rtap_buf) ++{ ++ u32 rtap_len = 0; ++ ++ put_unaligned_le16(rx_status->he_data1, &rtap_buf[rtap_len]); ++ rtap_len += 2; ++ ++ put_unaligned_le16(rx_status->he_data2, &rtap_buf[rtap_len]); ++ rtap_len += 2; ++ ++ put_unaligned_le16(rx_status->he_data3, &rtap_buf[rtap_len]); ++ rtap_len += 2; ++ ++ put_unaligned_le16(rx_status->he_data4, &rtap_buf[rtap_len]); ++ rtap_len += 2; ++ ++ put_unaligned_le16(rx_status->he_data5, &rtap_buf[rtap_len]); ++ rtap_len += 2; ++ ++ put_unaligned_le16(rx_status->he_data6, &rtap_buf[rtap_len]); ++} ++ ++static void ++ath11k_dp_rx_update_radiotap_he_mu(struct hal_rx_mon_ppdu_info *rx_status, ++ u8 *rtap_buf) ++{ ++ u32 rtap_len = 0; ++ ++ put_unaligned_le16(rx_status->he_flags1, &rtap_buf[rtap_len]); ++ rtap_len += 2; ++ ++ put_unaligned_le16(rx_status->he_flags2, &rtap_buf[rtap_len]); ++ rtap_len += 2; ++ ++ rtap_buf[rtap_len] = rx_status->he_RU[0]; ++ rtap_len += 1; ++ ++ rtap_buf[rtap_len] = rx_status->he_RU[1]; ++ rtap_len += 1; ++ ++ rtap_buf[rtap_len] = rx_status->he_RU[2]; ++ rtap_len += 1; ++ ++ rtap_buf[rtap_len] = rx_status->he_RU[3]; ++} ++ ++static void ath11k_update_radiotap(struct hal_rx_mon_ppdu_info *ppduinfo, ++ struct sk_buff *mon_skb, ++ struct ieee80211_rx_status *rxs) ++{ ++ u8 *ptr = NULL; ++ ++ if (ppduinfo->he_mu_flags) { ++ rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; ++ rxs->encoding = RX_ENC_HE; ++ ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he_mu)); ++ ath11k_dp_rx_update_radiotap_he_mu(ppduinfo, ptr); ++ } ++ if (ppduinfo->he_flags) { ++ rxs->flag |= RX_FLAG_RADIOTAP_HE; ++ rxs->encoding = RX_ENC_HE; ++ ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ++ ath11k_dp_rx_update_radiotap_he(ppduinfo, ptr); ++ } ++ ++ rxs->flag |= RX_FLAG_MACTIME_START; ++ rxs->signal = ppduinfo->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; ++ rxs->nss = ppduinfo->nss; ++ ++ rxs->mactime = ppduinfo->tsft; ++} ++ + static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id, + struct sk_buff *head_msdu, ++ struct hal_rx_mon_ppdu_info *ppduinfo, + struct sk_buff *tail_msdu, + struct napi_struct *napi) + { +@@ -4940,7 +5003,7 @@ static int ath11k_dp_rx_mon_deliver(stru + } else { + rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + } +- rxs->flag |= RX_FLAG_ONLY_MONITOR; ++ ath11k_update_radiotap(ppduinfo, mon_skb, rxs); + + ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb, rxs); + mon_skb = skb_next; +@@ -5045,6 +5108,7 @@ static void ath11k_dp_rx_mon_dest_proces + } + if (head_msdu && tail_msdu) { + ath11k_dp_rx_mon_deliver(ar, dp->mac_id, head_msdu, ++ &pmon->mon_ppdu_info, + tail_msdu, napi); + rx_mon_stats->dest_mpdu_done++; + } +@@ -5114,6 +5178,7 @@ int ath11k_dp_rx_process_mon_status(stru + if (log_type != ATH11K_PKTLOG_TYPE_INVALID) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + ++ memset(ppdu_info, 0, sizeof(struct hal_rx_mon_ppdu_info)); + hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); + + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && +@@ -5374,6 +5439,7 @@ static int ath11k_dp_rx_full_mon_deliver + tail_msdu = mon_mpdu->tail; + if (head_msdu && tail_msdu) { + ret = ath11k_dp_rx_mon_deliver(ar, mac_id, head_msdu, ++ &pmon->mon_ppdu_info, + tail_msdu, napi); + rx_mon_stats->dest_mpdu_done++; + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "full mon: deliver ppdu\n"); +--- a/drivers/net/wireless/ath/ath11k/hal_desc.h ++++ b/drivers/net/wireless/ath/ath11k/hal_desc.h +@@ -474,6 +474,7 @@ enum hal_tlv_tag { + + #define HAL_TLV_HDR_TAG GENMASK(9, 1) + #define HAL_TLV_HDR_LEN GENMASK(25, 10) ++#define HAL_TLV_USR_ID GENMASK(31, 26) + + #define HAL_TLV_ALIGN 4 + +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -73,6 +73,36 @@ enum hal_rx_mon_status { + HAL_RX_MON_STATUS_BUF_DONE, + }; + ++struct hal_rx_user_status { ++ u32 mcs:4, ++ nss:3, ++ ofdma_info_valid:1, ++ dl_ofdma_ru_start_index:7, ++ dl_ofdma_ru_width:7, ++ dl_ofdma_ru_size:8; ++ u32 ul_ofdma_user_v0_word0; ++ u32 ul_ofdma_user_v0_word1; ++ u32 ast_index; ++ u32 tid; ++ u16 tcp_msdu_count; ++ u16 udp_msdu_count; ++ u16 other_msdu_count; ++ u16 frame_control; ++ u8 frame_control_info_valid; ++ u8 data_sequence_control_info_valid; ++ u16 first_data_seq_ctrl; ++ u32 preamble_type; ++ u16 ht_flags; ++ u16 vht_flags; ++ u16 he_flags; ++ u8 rs_flags; ++ u32 mpdu_cnt_fcs_ok; ++ u32 mpdu_cnt_fcs_err; ++ u32 mpdu_fcs_ok_bitmap[8]; ++ u32 mpdu_ok_byte_count; ++ u32 mpdu_err_byte_count; ++}; ++ + #define HAL_TLV_STATUS_PPDU_NOT_DONE HAL_RX_MON_STATUS_PPDU_NOT_DONE + #define HAL_TLV_STATUS_PPDU_DONE HAL_RX_MON_STATUS_PPDU_DONE + #define HAL_TLV_STATUS_BUF_DONE HAL_RX_MON_STATUS_BUF_DONE +@@ -107,6 +137,12 @@ struct hal_rx_mon_ppdu_info { + u8 mcs; + u8 nss; + u8 bw; ++ u8 vht_flag_values1; ++ u8 vht_flag_values2; ++ u8 vht_flag_values3[4]; ++ u8 vht_flag_values4; ++ u8 vht_flag_values5; ++ u16 vht_flag_values6; + u8 is_stbc; + u8 gi; + u8 ldpc; +@@ -114,10 +150,46 @@ struct hal_rx_mon_ppdu_info { + u8 rssi_comb; + u8 rssi_chain_pri20[HAL_RX_MAX_NSS]; + u8 tid; ++ u16 ht_flags; ++ u16 vht_flags; ++ u16 he_flags; ++ u16 he_mu_flags; + u8 dcm; + u8 ru_alloc; + u8 reception_type; ++ u64 tsft; + u64 rx_duration; ++ u16 frame_control; ++ u32 ast_index; ++ u8 rs_fcs_err; ++ u8 rs_flags; ++ u8 cck_flag; ++ u8 ofdm_flag; ++ u8 ulofdma_flag; ++ u8 frame_control_info_valid; ++ u16 he_per_user_1; ++ u16 he_per_user_2; ++ u8 he_per_user_position; ++ u8 he_per_user_known; ++ u16 he_flags1; ++ u16 he_flags2; ++ u8 he_RU[4]; ++ u16 he_data1; ++ u16 he_data2; ++ u16 he_data3; ++ u16 he_data4; ++ u16 he_data5; ++ u16 he_data6; ++ u32 ppdu_len; ++ u32 prev_ppdu_id; ++ u32 device_id; ++ u16 first_data_seq_ctrl; ++ u8 monitor_direct_used; ++ u8 data_sequence_control_info_valid; ++ u8 ltf_size; ++ u8 rxpcu_filter_pass; ++ char rssi_chain[8][8]; ++ struct hal_rx_user_status userstats; + }; + + #define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) +@@ -150,6 +222,9 @@ struct hal_rx_ppdu_start { + #define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP GENMASK(15, 0) + #define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_EOSP_BITMAP GENMASK(31, 16) + ++#define HAL_RX_PPDU_END_USER_STATS_RSVD2_6_MPDU_OK_BYTE_COUNT GENMASK(24, 0) ++#define HAL_RX_PPDU_END_USER_STATS_RSVD2_8_MPDU_ERR_BYTE_COUNT GENMASK(24, 0) ++ + struct hal_rx_ppdu_end_user_stats { + __le32 rsvd0[2]; + __le32 info0; +@@ -164,6 +239,16 @@ struct hal_rx_ppdu_end_user_stats { + __le32 rsvd2[11]; + } __packed; + ++struct hal_rx_ppdu_end_user_stats_ext { ++ u32 info0; ++ u32 info1; ++ u32 info2; ++ u32 info3; ++ u32 info4; ++ u32 info5; ++ u32 info6; ++} __packed; ++ + #define HAL_RX_HT_SIG_INFO_INFO0_MCS GENMASK(6, 0) + #define HAL_RX_HT_SIG_INFO_INFO0_BW BIT(7) + +@@ -212,25 +297,62 @@ enum hal_rx_vht_sig_a_gi_setting { + HAL_RX_VHT_SIG_A_SHORT_GI_AMBIGUITY = 3, + }; + ++#define HAL_RX_SU_MU_CODING_LDPC 0x01 ++ ++#define HE_GI_0_8 0 ++#define HE_GI_0_4 1 ++#define HE_GI_1_6 2 ++#define HE_GI_3_2 3 ++ ++#define HE_LTF_1_X 0 ++#define HE_LTF_2_X 1 ++#define HE_LTF_4_X 2 ++#define HE_LTF_UNKNOWN 3 ++ + #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS GENMASK(6, 3) + #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM BIT(7) + #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW GENMASK(20, 19) + #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE GENMASK(22, 21) + #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS GENMASK(25, 23) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_BSS_COLOR GENMASK(13, 8) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_SPATIAL_REUSE GENMASK(18, 15) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_FORMAT_IND BIT(0) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_BEAM_CHANGE BIT(1) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DL_UL_FLAG BIT(2) + ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXOP_DURATION GENMASK(6, 0) + #define HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING BIT(7) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_LDPC_EXTRA BIT(8) + #define HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC BIT(9) + #define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF BIT(10) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_FACTOR GENMASK(12, 11) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_PE_DISAM BIT(13) ++#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_DOPPLER_IND BIT(15) + + struct hal_rx_he_sig_a_su_info { + __le32 info0; + __le32 info1; + } __packed; + +-#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW GENMASK(17, 15) +-#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE GENMASK(24, 23) +- ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_UL_FLAG BIT(1) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_MCS_OF_SIGB GENMASK(3, 1) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DCM_OF_SIGB BIT(4) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_BSS_COLOR GENMASK(10, 5) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_SPATIAL_REUSE GENMASK(14, 11) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW GENMASK(17, 15) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_NUM_SIGB_SYMB GENMASK(21, 18) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_COMP_MODE_SIGB BIT(22) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE GENMASK(24, 23) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DOPPLER_INDICATION BIT(25) ++ ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXOP_DURATION GENMASK(6, 0) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_CODING BIT(7) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_NUM_LTF_SYMB GENMASK(10, 8) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_LDPC_EXTRA BIT(11) + #define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC BIT(12) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXBF BIT(10) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_FACTOR GENMASK(14, 13) ++#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_PE_DISAM BIT(15) + + struct hal_rx_he_sig_a_mu_dl_info { + __le32 info0; +@@ -243,6 +365,7 @@ struct hal_rx_he_sig_b1_mu_info { + __le32 info0; + } __packed; + ++#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_ID GENMASK(10, 0) + #define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS GENMASK(18, 15) + #define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING BIT(20) + #define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS GENMASK(31, 29) +@@ -251,6 +374,7 @@ struct hal_rx_he_sig_b2_mu_info { + __le32 info0; + } __packed; + ++#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_ID GENMASK(10, 0) + #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS GENMASK(13, 11) + #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF BIT(19) + #define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS GENMASK(18, 15) +@@ -279,11 +403,14 @@ struct hal_rx_phyrx_rssi_legacy_info { + + #define HAL_RX_MPDU_INFO_INFO0_PEERID GENMASK(31, 16) + #define HAL_RX_MPDU_INFO_INFO0_PEERID_WCN6855 GENMASK(15, 0) ++#define HAL_RX_MPDU_INFO_INFO1_MPDU_LEN GENMASK(13, 0) + + struct hal_rx_mpdu_info { + __le32 rsvd0; + __le32 info0; +- __le32 rsvd1[21]; ++ __le32 rsvd1[11]; ++ __le32 info1; ++ __le32 rsvd2[9]; + } __packed; + + struct hal_rx_mpdu_info_wcn6855 { diff --git a/package/kernel/mac80211/patches/ath11k/0190-ath11k-translate-HE-status-to-radiotap-format.patch b/package/kernel/mac80211/patches/ath11k/0190-ath11k-translate-HE-status-to-radiotap-format.patch new file mode 100644 index 00000000000000..5212e131569c86 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0190-ath11k-translate-HE-status-to-radiotap-format.patch @@ -0,0 +1,686 @@ +From ab0a9ef605cf527c9754c2e7b055a391a9f0233c Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Wed, 16 Feb 2022 17:21:12 -0800 +Subject: [PATCH] ath11k: translate HE status to radiotap format + +Translate HE status to radiotap format. This uses HE radiotap +definitions from include/net/ieee80211_radiotap.h. + +Co-developed-by: Miles Hu +Signed-off-by: Miles Hu +Signed-off-by: Anilkumar Kolli +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220217012112.31211-4-pradeepc@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 31 +- + drivers/net/wireless/ath/ath11k/hal_rx.c | 471 ++++++++++++++++++++--- + 2 files changed, 438 insertions(+), 64 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -4940,29 +4940,44 @@ ath11k_dp_rx_update_radiotap_he_mu(struc + rtap_buf[rtap_len] = rx_status->he_RU[3]; + } + +-static void ath11k_update_radiotap(struct hal_rx_mon_ppdu_info *ppduinfo, ++static void ath11k_update_radiotap(struct ath11k *ar, ++ struct hal_rx_mon_ppdu_info *ppduinfo, + struct sk_buff *mon_skb, + struct ieee80211_rx_status *rxs) + { ++ struct ieee80211_supported_band *sband; + u8 *ptr = NULL; + ++ rxs->flag |= RX_FLAG_MACTIME_START; ++ rxs->signal = ppduinfo->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; ++ ++ if (ppduinfo->nss) ++ rxs->nss = ppduinfo->nss; ++ + if (ppduinfo->he_mu_flags) { + rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; + rxs->encoding = RX_ENC_HE; + ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he_mu)); + ath11k_dp_rx_update_radiotap_he_mu(ppduinfo, ptr); +- } +- if (ppduinfo->he_flags) { ++ } else if (ppduinfo->he_flags) { + rxs->flag |= RX_FLAG_RADIOTAP_HE; + rxs->encoding = RX_ENC_HE; + ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); + ath11k_dp_rx_update_radiotap_he(ppduinfo, ptr); ++ rxs->rate_idx = ppduinfo->rate; ++ } else if (ppduinfo->vht_flags) { ++ rxs->encoding = RX_ENC_VHT; ++ rxs->rate_idx = ppduinfo->rate; ++ } else if (ppduinfo->ht_flags) { ++ rxs->encoding = RX_ENC_HT; ++ rxs->rate_idx = ppduinfo->rate; ++ } else { ++ rxs->encoding = RX_ENC_LEGACY; ++ sband = &ar->mac.sbands[rxs->band]; ++ rxs->rate_idx = ath11k_mac_hw_rate_to_idx(sband, ppduinfo->rate, ++ ppduinfo->cck_flag); + } + +- rxs->flag |= RX_FLAG_MACTIME_START; +- rxs->signal = ppduinfo->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; +- rxs->nss = ppduinfo->nss; +- + rxs->mactime = ppduinfo->tsft; + } + +@@ -5003,7 +5018,7 @@ static int ath11k_dp_rx_mon_deliver(stru + } else { + rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + } +- ath11k_update_radiotap(ppduinfo, mon_skb, rxs); ++ ath11k_update_radiotap(ar, ppduinfo, mon_skb, rxs); + + ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb, rxs); + mon_skb = skb_next; +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -456,10 +456,12 @@ void ath11k_hal_reo_status_queue_stats(s + desc->info0)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "pn = [%08x, %08x, %08x, %08x]\n", + desc->pn[0], desc->pn[1], desc->pn[2], desc->pn[3]); +- ath11k_dbg(ab, ATH11k_DBG_HAL, "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n", ++ ath11k_dbg(ab, ATH11k_DBG_HAL, ++ "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n", + desc->last_rx_enqueue_timestamp, + desc->last_rx_dequeue_timestamp); +- ath11k_dbg(ab, ATH11k_DBG_HAL, "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n", ++ ath11k_dbg(ab, ATH11k_DBG_HAL, ++ "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n", + desc->rx_bitmap[0], desc->rx_bitmap[1], desc->rx_bitmap[2], + desc->rx_bitmap[3], desc->rx_bitmap[4], desc->rx_bitmap[5], + desc->rx_bitmap[6], desc->rx_bitmap[7]); +@@ -803,12 +805,75 @@ void ath11k_hal_reo_init_cmd_ring(struct + } + } + ++#define HAL_MAX_UL_MU_USERS 37 ++static inline void ++ath11k_hal_rx_handle_ofdma_info(void *rx_tlv, ++ struct hal_rx_user_status *rx_user_status) ++{ ++ struct hal_rx_ppdu_end_user_stats *ppdu_end_user = ++ (struct hal_rx_ppdu_end_user_stats *)rx_tlv; ++ ++ rx_user_status->ul_ofdma_user_v0_word0 = __le32_to_cpu(ppdu_end_user->info6); ++ ++ rx_user_status->ul_ofdma_user_v0_word1 = __le32_to_cpu(ppdu_end_user->rsvd2[10]); ++} ++ ++static inline void ++ath11k_hal_rx_populate_byte_count(void *rx_tlv, void *ppduinfo, ++ struct hal_rx_user_status *rx_user_status) ++{ ++ struct hal_rx_ppdu_end_user_stats *ppdu_end_user = ++ (struct hal_rx_ppdu_end_user_stats *)rx_tlv; ++ ++ rx_user_status->mpdu_ok_byte_count = ++ FIELD_GET(HAL_RX_PPDU_END_USER_STATS_RSVD2_6_MPDU_OK_BYTE_COUNT, ++ __le32_to_cpu(ppdu_end_user->rsvd2[6])); ++ rx_user_status->mpdu_err_byte_count = ++ FIELD_GET(HAL_RX_PPDU_END_USER_STATS_RSVD2_8_MPDU_ERR_BYTE_COUNT, ++ __le32_to_cpu(ppdu_end_user->rsvd2[8])); ++} ++ ++static inline void ++ath11k_hal_rx_populate_mu_user_info(void *rx_tlv, struct hal_rx_mon_ppdu_info *ppdu_info, ++ struct hal_rx_user_status *rx_user_status) ++{ ++ rx_user_status->ast_index = ppdu_info->ast_index; ++ rx_user_status->tid = ppdu_info->tid; ++ rx_user_status->tcp_msdu_count = ++ ppdu_info->tcp_msdu_count; ++ rx_user_status->udp_msdu_count = ++ ppdu_info->udp_msdu_count; ++ rx_user_status->other_msdu_count = ++ ppdu_info->other_msdu_count; ++ rx_user_status->frame_control = ppdu_info->frame_control; ++ rx_user_status->frame_control_info_valid = ++ ppdu_info->frame_control_info_valid; ++ rx_user_status->data_sequence_control_info_valid = ++ ppdu_info->data_sequence_control_info_valid; ++ rx_user_status->first_data_seq_ctrl = ++ ppdu_info->first_data_seq_ctrl; ++ rx_user_status->preamble_type = ppdu_info->preamble_type; ++ rx_user_status->ht_flags = ppdu_info->ht_flags; ++ rx_user_status->vht_flags = ppdu_info->vht_flags; ++ rx_user_status->he_flags = ppdu_info->he_flags; ++ rx_user_status->rs_flags = ppdu_info->rs_flags; ++ ++ rx_user_status->mpdu_cnt_fcs_ok = ++ ppdu_info->num_mpdu_fcs_ok; ++ rx_user_status->mpdu_cnt_fcs_err = ++ ppdu_info->num_mpdu_fcs_err; ++ ++ ath11k_hal_rx_populate_byte_count(rx_tlv, ppdu_info, rx_user_status); ++} ++ + static enum hal_rx_mon_status + ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, +- u32 tlv_tag, u8 *tlv_data) ++ u32 tlv_tag, u8 *tlv_data, u32 userid) + { +- u32 info0, info1; ++ u32 info0, info1, value; ++ u8 he_dcm = 0, he_stbc = 0; ++ u16 he_gi = 0, he_ltf = 0; + + switch (tlv_tag) { + case HAL_RX_PPDU_START: { +@@ -829,6 +894,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + info0 = __le32_to_cpu(eu_stats->info0); + info1 = __le32_to_cpu(eu_stats->info1); + ++ ppdu_info->ast_index = ++ FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX, ++ __le32_to_cpu(eu_stats->info2)); + ppdu_info->tid = + ffs(FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP, + __le32_to_cpu(eu_stats->info6))) - 1; +@@ -852,6 +920,44 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + ppdu_info->num_mpdu_fcs_err = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR, + info0); ++ switch (ppdu_info->preamble_type) { ++ case HAL_RX_PREAMBLE_11N: ++ ppdu_info->ht_flags = 1; ++ break; ++ case HAL_RX_PREAMBLE_11AC: ++ ppdu_info->vht_flags = 1; ++ break; ++ case HAL_RX_PREAMBLE_11AX: ++ ppdu_info->he_flags = 1; ++ break; ++ default: ++ break; ++ } ++ ++ if (userid < HAL_MAX_UL_MU_USERS) { ++ struct hal_rx_user_status *rxuser_stats = ++ &ppdu_info->userstats; ++ ++ ath11k_hal_rx_handle_ofdma_info(tlv_data, rxuser_stats); ++ ath11k_hal_rx_populate_mu_user_info(tlv_data, ppdu_info, ++ rxuser_stats); ++ } ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[0] = ++ __le32_to_cpu(eu_stats->rsvd1[0]); ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[1] = ++ __le32_to_cpu(eu_stats->rsvd1[1]); ++ ++ break; ++ } ++ case HAL_RX_PPDU_END_USER_STATS_EXT: { ++ struct hal_rx_ppdu_end_user_stats_ext *eu_stats = ++ (struct hal_rx_ppdu_end_user_stats_ext *)tlv_data; ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[2] = eu_stats->info1; ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[3] = eu_stats->info2; ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[4] = eu_stats->info3; ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[5] = eu_stats->info4; ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[6] = eu_stats->info5; ++ ppdu_info->userstats.mpdu_fcs_ok_bitmap[7] = eu_stats->info6; + break; + } + case HAL_PHYRX_HT_SIG: { +@@ -950,50 +1056,151 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + else + ppdu_info->reception_type = + HAL_RX_RECEPTION_TYPE_MU_MIMO; ++ ppdu_info->vht_flag_values5 = group_id; ++ ppdu_info->vht_flag_values3[0] = (((ppdu_info->mcs) << 4) | ++ ppdu_info->nss); ++ ppdu_info->vht_flag_values2 = ppdu_info->bw; ++ ppdu_info->vht_flag_values4 = ++ FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING, info1); + break; + } + case HAL_PHYRX_HE_SIG_A_SU: { + struct hal_rx_he_sig_a_su_info *he_sig_a = + (struct hal_rx_he_sig_a_su_info *)tlv_data; +- u32 nsts, cp_ltf, dcm; + ++ ppdu_info->he_flags = 1; + info0 = __le32_to_cpu(he_sig_a->info0); + info1 = __le32_to_cpu(he_sig_a->info1); + +- ppdu_info->mcs = +- FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS, +- info0); +- ppdu_info->bw = +- FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW, +- info0); +- ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING, info0); +- ppdu_info->is_stbc = info1 & +- HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC; +- ppdu_info->beamformed = info1 & +- HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF; +- dcm = info0 & HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM; +- cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE, +- info0); +- nsts = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_FORMAT_IND, info0); + +- switch (cp_ltf) { ++ if (value == 0) ++ ppdu_info->he_data1 = IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG; ++ else ++ ppdu_info->he_data1 = IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU; ++ ++ ppdu_info->he_data1 |= ++ IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN; ++ ++ ppdu_info->he_data2 |= ++ IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN; ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_BSS_COLOR, info0); ++ ppdu_info->he_data3 = ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_BEAM_CHANGE, info0); ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_DL_UL_FLAG, info0); ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_UL_DL, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS, info0); ++ ppdu_info->mcs = value; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS, value); ++ ++ he_dcm = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM, info0); ++ ppdu_info->dcm = he_dcm; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM, he_dcm); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING, info1); ++ ppdu_info->ldpc = (value == HAL_RX_SU_MU_CODING_LDPC) ? 1 : 0; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_CODING, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_LDPC_EXTRA, info1); ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG, value); ++ he_stbc = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC, info1); ++ ppdu_info->is_stbc = he_stbc; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_STBC, he_stbc); ++ ++ /* data4 */ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_SPATIAL_REUSE, info0); ++ ppdu_info->he_data4 = ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE, value); ++ ++ /* data5 */ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW, info0); ++ ppdu_info->bw = value; ++ ppdu_info->he_data5 = ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE, info0); ++ switch (value) { + case 0: ++ he_gi = HE_GI_0_8; ++ he_ltf = HE_LTF_1_X; ++ break; + case 1: +- ppdu_info->gi = HAL_RX_GI_0_8_US; +- break; ++ he_gi = HE_GI_0_8; ++ he_ltf = HE_LTF_2_X; ++ break; + case 2: +- ppdu_info->gi = HAL_RX_GI_1_6_US; +- break; ++ he_gi = HE_GI_1_6; ++ he_ltf = HE_LTF_2_X; ++ break; + case 3: +- if (dcm && ppdu_info->is_stbc) +- ppdu_info->gi = HAL_RX_GI_0_8_US; +- else +- ppdu_info->gi = HAL_RX_GI_3_2_US; +- break; ++ if (he_dcm && he_stbc) { ++ he_gi = HE_GI_0_8; ++ he_ltf = HE_LTF_4_X; ++ } else { ++ he_gi = HE_GI_3_2; ++ he_ltf = HE_LTF_4_X; ++ } ++ break; + } ++ ppdu_info->gi = he_gi; ++ he_gi = (he_gi != 0) ? he_gi - 1 : 0; ++ ppdu_info->he_data5 |= FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_GI, he_gi); ++ ppdu_info->ltf_size = he_ltf; ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE, ++ (he_ltf == HE_LTF_4_X) ? he_ltf - 1 : he_ltf); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_FACTOR, info1); ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF, info1); ++ ppdu_info->beamformed = value; ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_TXBF, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_PE_DISAM, info1); ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG, value); ++ ++ /* data6 */ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); ++ value++; ++ ppdu_info->nss = value; ++ ppdu_info->he_data6 = ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_NSTS, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_DOPPLER_IND, info1); ++ ppdu_info->he_data6 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_DOPPLER, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXOP_DURATION, info1); ++ ppdu_info->he_data6 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_TXOP, value); + +- ppdu_info->nss = nsts + 1; +- ppdu_info->dcm = dcm; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } +@@ -1001,29 +1208,142 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_mu_dl = + (struct hal_rx_he_sig_a_mu_dl_info *)tlv_data; + +- u32 cp_ltf; +- + info0 = __le32_to_cpu(he_sig_a_mu_dl->info0); + info1 = __le32_to_cpu(he_sig_a_mu_dl->info1); + +- ppdu_info->bw = +- FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, +- info0); +- cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE, +- info0); ++ ppdu_info->he_mu_flags = 1; + +- switch (cp_ltf) { ++ ppdu_info->he_data1 = IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU; ++ ppdu_info->he_data1 |= ++ IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN; ++ ++ ppdu_info->he_data2 = ++ IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN; ++ ++ /*data3*/ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_BSS_COLOR, info0); ++ ppdu_info->he_data3 = ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_UL_FLAG, info0); ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_UL_DL, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_LDPC_EXTRA, info1); ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC, info1); ++ he_stbc = value; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_STBC, value); ++ ++ /*data4*/ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_SPATIAL_REUSE, info0); ++ ppdu_info->he_data4 = ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE, value); ++ ++ /*data5*/ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, info0); ++ ppdu_info->bw = value; ++ ppdu_info->he_data5 = ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE, info0); ++ switch (value) { + case 0: ++ he_gi = HE_GI_0_8; ++ he_ltf = HE_LTF_4_X; ++ break; + case 1: +- ppdu_info->gi = HAL_RX_GI_0_8_US; ++ he_gi = HE_GI_0_8; ++ he_ltf = HE_LTF_2_X; + break; + case 2: +- ppdu_info->gi = HAL_RX_GI_1_6_US; ++ he_gi = HE_GI_1_6; ++ he_ltf = HE_LTF_2_X; + break; + case 3: +- ppdu_info->gi = HAL_RX_GI_3_2_US; ++ he_gi = HE_GI_3_2; ++ he_ltf = HE_LTF_4_X; + break; + } ++ ppdu_info->gi = he_gi; ++ he_gi = (he_gi != 0) ? he_gi - 1 : 0; ++ ppdu_info->he_data5 |= FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_GI, he_gi); ++ ppdu_info->ltf_size = he_ltf; ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE, ++ (he_ltf == HE_LTF_4_X) ? he_ltf - 1 : he_ltf); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_NUM_LTF_SYMB, info1); ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_FACTOR, ++ info1); ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_PE_DISAM, ++ info1); ++ ppdu_info->he_data5 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG, value); ++ ++ /*data6*/ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DOPPLER_INDICATION, ++ info0); ++ ppdu_info->he_data6 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_DOPPLER, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXOP_DURATION, info1); ++ ppdu_info->he_data6 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_TXOP, value); ++ ++ /* HE-MU Flags */ ++ /* HE-MU-flags1 */ ++ ppdu_info->he_flags1 = ++ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | ++ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | ++ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN | ++ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | ++ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN; ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_MCS_OF_SIGB, info0); ++ ppdu_info->he_flags1 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN, ++ value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DCM_OF_SIGB, info0); ++ ppdu_info->he_flags1 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN, ++ value); ++ ++ /* HE-MU-flags2 */ ++ ppdu_info->he_flags2 = ++ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN; ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, info0); ++ ppdu_info->he_flags2 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW, ++ value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_COMP_MODE_SIGB, info0); ++ ppdu_info->he_flags2 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP, value); ++ value = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_NUM_SIGB_SYMB, info0); ++ value = value - 1; ++ ppdu_info->he_flags2 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS, ++ value); + + ppdu_info->is_stbc = info1 & + HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC; +@@ -1041,7 +1361,7 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + info0); + ppdu_info->ru_alloc = + ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(ru_tones); +- ++ ppdu_info->he_RU[0] = ru_tones; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } +@@ -1051,14 +1371,25 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + + info0 = __le32_to_cpu(he_sig_b2_mu->info0); + ++ ppdu_info->he_data1 |= IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN; ++ + ppdu_info->mcs = +- FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS, +- info0); ++ FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS, info0); ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS, ppdu_info->mcs); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING, info0); ++ ppdu_info->ldpc = value; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_CODING, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_ID, info0); ++ ppdu_info->he_data4 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID, value); ++ + ppdu_info->nss = +- FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS, +- info0) + 1; +- ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING, +- info0); ++ FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS, info0) + 1; + break; + } + case HAL_PHYRX_HE_SIG_B2_OFDMA: { +@@ -1067,17 +1398,40 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + + info0 = __le32_to_cpu(he_sig_b2_ofdma->info0); + ++ ppdu_info->he_data1 |= ++ IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | ++ IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN; ++ ++ /* HE-data2 */ ++ ppdu_info->he_data2 |= IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN; ++ + ppdu_info->mcs = + FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS, + info0); ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS, ppdu_info->mcs); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_DCM, info0); ++ he_dcm = value; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM, value); ++ ++ value = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING, info0); ++ ppdu_info->ldpc = value; ++ ppdu_info->he_data3 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_CODING, value); ++ ++ /* HE-data4 */ ++ value = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_ID, info0); ++ ppdu_info->he_data4 |= ++ FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID, value); ++ + ppdu_info->nss = + FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS, + info0) + 1; + ppdu_info->beamformed = +- info0 & +- HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF; +- ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING, +- info0); ++ info0 & HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; + break; + } +@@ -1119,6 +1473,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + ppdu_info->rx_duration = + FIELD_GET(HAL_RX_PPDU_END_DURATION, + __le32_to_cpu(ppdu_rx_duration->info0)); ++ ppdu_info->tsft = __le32_to_cpu(ppdu_rx_duration->rsvd0[1]); ++ ppdu_info->tsft = (ppdu_info->tsft << 32) | ++ __le32_to_cpu(ppdu_rx_duration->rsvd0[0]); + break; + } + case HAL_DUMMY: +@@ -1142,12 +1499,14 @@ ath11k_hal_rx_parse_mon_status(struct at + enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE; + u16 tlv_tag; + u16 tlv_len; ++ u32 tlv_userid = 0; + u8 *ptr = skb->data; + + do { + tlv = (struct hal_tlv_hdr *)ptr; + tlv_tag = FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl); + tlv_len = FIELD_GET(HAL_TLV_HDR_LEN, tlv->tl); ++ tlv_userid = FIELD_GET(HAL_TLV_USR_ID, tlv->tl); + ptr += sizeof(*tlv); + + /* The actual length of PPDU_END is the combined length of many PHY +@@ -1159,7 +1518,7 @@ ath11k_hal_rx_parse_mon_status(struct at + tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); + + hal_status = ath11k_hal_rx_parse_mon_status_tlv(ab, ppdu_info, +- tlv_tag, ptr); ++ tlv_tag, ptr, tlv_userid); + ptr += tlv_len; + ptr = PTR_ALIGN(ptr, HAL_TLV_ALIGN); + diff --git a/package/kernel/mac80211/patches/ath11k/0191-ath11k-add-dbring-debug-support.patch b/package/kernel/mac80211/patches/ath11k/0191-ath11k-add-dbring-debug-support.patch new file mode 100644 index 00000000000000..bcece4c98cc426 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0191-ath11k-add-dbring-debug-support.patch @@ -0,0 +1,509 @@ +From 691425b4a41fe5843ea424a93ee373f29b1040a5 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Sun, 20 Feb 2022 19:37:39 +0530 +Subject: [PATCH] ath11k: add dbring debug support + +Target copies spectral report and CFR report through dbring to +host for further processing. This mechanism involves ring and +buffer management in the Host, FW, and uCode, where improper +tail pointer update issues are seen. + +This dbring debug support help to debug such issues by tracking +head and tail pointer movement along with the timestamp at which +each buffer is received and replenished. + +Provide a debugfs interface to enalbe/disable dbring debug +support and dump the dbring debug entries. + +Also introduced a new hardware param to add dbring debugfs support +for few hardwares which are using dbings. + +Usage: + +echo > /sys/kernel/debug/ath11k/ipq8074_2/ +mac0/enable_dbr_debug + +dbr_id: 0 for spectral and 1 for CFR +val: 0 - disable, 1 - enable. + +Tested-on: IPQ8074 WLAN.HK.2.4.0.1-01467-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1645366059-11798-1-git-send-email-quic_vnaralas@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 6 + + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/dbring.c | 19 +- + drivers/net/wireless/ath/ath11k/debugfs.c | 221 ++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/debugfs.h | 41 ++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 6 files changed, 283 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -99,6 +99,7 @@ static const struct ath11k_hw_params ath + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, + .current_cc_support = false, ++ .dbr_debug_support = true, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -164,6 +165,7 @@ static const struct ath11k_hw_params ath + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, + .current_cc_support = false, ++ .dbr_debug_support = true, + }, + { + .name = "qca6390 hw2.0", +@@ -228,6 +230,7 @@ static const struct ath11k_hw_params ath + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, ++ .dbr_debug_support = false, + }, + { + .name = "qcn9074 hw1.0", +@@ -292,6 +295,7 @@ static const struct ath11k_hw_params ath + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, + .current_cc_support = false, ++ .dbr_debug_support = true, + }, + { + .name = "wcn6855 hw2.0", +@@ -356,6 +360,7 @@ static const struct ath11k_hw_params ath + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, ++ .dbr_debug_support = false, + }, + { + .name = "wcn6855 hw2.1", +@@ -419,6 +424,7 @@ static const struct ath11k_hw_params ath + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, ++ .dbr_debug_support = false, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -461,6 +461,7 @@ struct ath11k_debug { + u32 rx_filter; + u32 mem_offset; + u32 module_id_bitmap[MAX_MODULE_ID_BITMAP_WORDS]; ++ struct ath11k_debug_dbr *dbr_debug[WMI_DIRECT_BUF_MAX]; + }; + + struct ath11k_per_peer_tx_stats { +--- a/drivers/net/wireless/ath/ath11k/dbring.c ++++ b/drivers/net/wireless/ath/ath11k/dbring.c +@@ -37,7 +37,8 @@ static void ath11k_dbring_fill_magic_val + + static int ath11k_dbring_bufs_replenish(struct ath11k *ar, + struct ath11k_dbring *ring, +- struct ath11k_dbring_element *buff) ++ struct ath11k_dbring_element *buff, ++ enum wmi_direct_buffer_module id) + { + struct ath11k_base *ab = ar->ab; + struct hal_srng *srng; +@@ -84,6 +85,7 @@ static int ath11k_dbring_bufs_replenish( + + ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, 0); + ++ ath11k_debugfs_add_dbring_entry(ar, id, ATH11K_DBG_DBR_EVENT_REPLENISH, srng); + ath11k_hal_srng_access_end(ab, srng); + + return 0; +@@ -101,7 +103,8 @@ err: + } + + static int ath11k_dbring_fill_bufs(struct ath11k *ar, +- struct ath11k_dbring *ring) ++ struct ath11k_dbring *ring, ++ enum wmi_direct_buffer_module id) + { + struct ath11k_dbring_element *buff; + struct hal_srng *srng; +@@ -129,7 +132,7 @@ static int ath11k_dbring_fill_bufs(struc + kfree(buff); + break; + } +- ret = ath11k_dbring_bufs_replenish(ar, ring, buff); ++ ret = ath11k_dbring_bufs_replenish(ar, ring, buff, id); + if (ret) { + ath11k_warn(ar->ab, "failed to replenish db ring num_remain %d req_ent %d\n", + num_remain, req_entries); +@@ -210,7 +213,7 @@ int ath11k_dbring_buf_setup(struct ath11 + ring->hp_addr = ath11k_hal_srng_get_hp_addr(ar->ab, srng); + ring->tp_addr = ath11k_hal_srng_get_tp_addr(ar->ab, srng); + +- ret = ath11k_dbring_fill_bufs(ar, ring); ++ ret = ath11k_dbring_fill_bufs(ar, ring, db_cap->id); + + return ret; + } +@@ -270,7 +273,7 @@ int ath11k_dbring_buffer_release_event(s + struct ath11k_buffer_addr desc; + u8 *vaddr_unalign; + u32 num_entry, num_buff_reaped; +- u8 pdev_idx, rbm; ++ u8 pdev_idx, rbm, module_id; + u32 cookie; + int buf_id; + int size; +@@ -278,6 +281,7 @@ int ath11k_dbring_buffer_release_event(s + int ret = 0; + + pdev_idx = ev->fixed.pdev_id; ++ module_id = ev->fixed.module_id; + + if (pdev_idx >= ab->num_radios) { + ath11k_warn(ab, "Invalid pdev id %d\n", pdev_idx); +@@ -346,6 +350,9 @@ int ath11k_dbring_buffer_release_event(s + dma_unmap_single(ab->dev, buff->paddr, ring->buf_sz, + DMA_FROM_DEVICE); + ++ ath11k_debugfs_add_dbring_entry(ar, module_id, ++ ATH11K_DBG_DBR_EVENT_RX, srng); ++ + if (ring->handler) { + vaddr_unalign = buff->payload; + handler_data.data = PTR_ALIGN(vaddr_unalign, +@@ -357,7 +364,7 @@ int ath11k_dbring_buffer_release_event(s + + buff->paddr = 0; + memset(buff->payload, 0, size); +- ath11k_dbring_bufs_replenish(ar, ring, buff); ++ ath11k_dbring_bufs_replenish(ar, ring, buff, module_id); + } + + spin_unlock_bh(&srng->lock); +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -52,6 +52,45 @@ static const char *htt_bp_lmac_ring[HTT_ + "MONITOR_DEST_RING", + }; + ++void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, ++ enum wmi_direct_buffer_module id, ++ enum ath11k_dbg_dbr_event event, ++ struct hal_srng *srng) ++{ ++ struct ath11k_debug_dbr *dbr_debug; ++ struct ath11k_dbg_dbr_data *dbr_data; ++ struct ath11k_dbg_dbr_entry *entry; ++ ++ if (id >= WMI_DIRECT_BUF_MAX || event >= ATH11K_DBG_DBR_EVENT_MAX) ++ return; ++ ++ dbr_debug = ar->debug.dbr_debug[id]; ++ if (!dbr_debug) ++ return; ++ ++ if (!dbr_debug->dbr_debug_enabled) ++ return; ++ ++ dbr_data = &dbr_debug->dbr_dbg_data; ++ ++ spin_lock_bh(&dbr_data->lock); ++ ++ if (dbr_data->entries) { ++ entry = &dbr_data->entries[dbr_data->dbr_debug_idx]; ++ entry->hp = srng->u.src_ring.hp; ++ entry->tp = *srng->u.src_ring.tp_addr; ++ entry->timestamp = jiffies; ++ entry->event = event; ++ ++ dbr_data->dbr_debug_idx++; ++ if (dbr_data->dbr_debug_idx == ++ dbr_data->num_ring_debug_entries) ++ dbr_data->dbr_debug_idx = 0; ++ } ++ ++ spin_unlock_bh(&dbr_data->lock); ++} ++ + static void ath11k_fw_stats_pdevs_free(struct list_head *head) + { + struct ath11k_fw_stats_pdev *i, *tmp; +@@ -1176,6 +1215,169 @@ static const struct file_operations fops + .open = simple_open + }; + ++static ssize_t ath11k_debug_dump_dbr_entries(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_dbg_dbr_data *dbr_dbg_data = file->private_data; ++ static const char * const event_id_to_string[] = {"empty", "Rx", "Replenish"}; ++ int size = ATH11K_DEBUG_DBR_ENTRIES_MAX * 100; ++ char *buf; ++ int i, ret; ++ int len = 0; ++ ++ buf = kzalloc(size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ len += scnprintf(buf + len, size - len, ++ "-----------------------------------------\n"); ++ len += scnprintf(buf + len, size - len, ++ "| idx | hp | tp | timestamp | event |\n"); ++ len += scnprintf(buf + len, size - len, ++ "-----------------------------------------\n"); ++ ++ spin_lock_bh(&dbr_dbg_data->lock); ++ ++ for (i = 0; i < dbr_dbg_data->num_ring_debug_entries; i++) { ++ len += scnprintf(buf + len, size - len, ++ "|%4u|%8u|%8u|%11llu|%8s|\n", i, ++ dbr_dbg_data->entries[i].hp, ++ dbr_dbg_data->entries[i].tp, ++ dbr_dbg_data->entries[i].timestamp, ++ event_id_to_string[dbr_dbg_data->entries[i].event]); ++ } ++ ++ spin_unlock_bh(&dbr_dbg_data->lock); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); ++ kfree(buf); ++ ++ return ret; ++} ++ ++static const struct file_operations fops_debug_dump_dbr_entries = { ++ .read = ath11k_debug_dump_dbr_entries, ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static void ath11k_debugfs_dbr_dbg_destroy(struct ath11k *ar, int dbr_id) ++{ ++ struct ath11k_debug_dbr *dbr_debug; ++ struct ath11k_dbg_dbr_data *dbr_dbg_data; ++ ++ if (!ar->debug.dbr_debug[dbr_id]) ++ return; ++ ++ dbr_debug = ar->debug.dbr_debug[dbr_id]; ++ dbr_dbg_data = &dbr_debug->dbr_dbg_data; ++ ++ debugfs_remove_recursive(dbr_debug->dbr_debugfs); ++ kfree(dbr_dbg_data->entries); ++ kfree(dbr_debug); ++ ar->debug.dbr_debug[dbr_id] = NULL; ++} ++ ++static int ath11k_debugfs_dbr_dbg_init(struct ath11k *ar, int dbr_id) ++{ ++ struct ath11k_debug_dbr *dbr_debug; ++ struct ath11k_dbg_dbr_data *dbr_dbg_data; ++ static const char * const dbr_id_to_str[] = {"spectral", "CFR"}; ++ ++ if (ar->debug.dbr_debug[dbr_id]) ++ return 0; ++ ++ ar->debug.dbr_debug[dbr_id] = kzalloc(sizeof(*dbr_debug), ++ GFP_KERNEL); ++ ++ if (!ar->debug.dbr_debug[dbr_id]) ++ return -ENOMEM; ++ ++ dbr_debug = ar->debug.dbr_debug[dbr_id]; ++ dbr_dbg_data = &dbr_debug->dbr_dbg_data; ++ ++ if (dbr_debug->dbr_debugfs) ++ return 0; ++ ++ dbr_debug->dbr_debugfs = debugfs_create_dir(dbr_id_to_str[dbr_id], ++ ar->debug.debugfs_pdev); ++ if (IS_ERR_OR_NULL(dbr_debug->dbr_debugfs)) { ++ if (IS_ERR(dbr_debug->dbr_debugfs)) ++ return PTR_ERR(dbr_debug->dbr_debugfs); ++ return -ENOMEM; ++ } ++ ++ dbr_debug->dbr_debug_enabled = true; ++ dbr_dbg_data->num_ring_debug_entries = ATH11K_DEBUG_DBR_ENTRIES_MAX; ++ dbr_dbg_data->dbr_debug_idx = 0; ++ dbr_dbg_data->entries = kcalloc(ATH11K_DEBUG_DBR_ENTRIES_MAX, ++ sizeof(struct ath11k_dbg_dbr_entry), ++ GFP_KERNEL); ++ if (!dbr_dbg_data->entries) ++ return -ENOMEM; ++ ++ spin_lock_init(&dbr_dbg_data->lock); ++ ++ debugfs_create_file("dump_dbr_debug", 0444, dbr_debug->dbr_debugfs, ++ dbr_dbg_data, &fops_debug_dump_dbr_entries); ++ ++ return 0; ++} ++ ++static ssize_t ath11k_debugfs_write_enable_dbr_dbg(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k *ar = file->private_data; ++ char buf[32] = {0}; ++ u32 dbr_id, enable; ++ int ret; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ if (ar->state != ATH11K_STATE_ON) { ++ ret = -ENETDOWN; ++ goto out; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); ++ if (ret < 0) ++ goto out; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%u %u", &dbr_id, &enable); ++ if (ret != 2 || dbr_id > 1 || enable > 1) { ++ ret = -EINVAL; ++ ath11k_warn(ar->ab, "usage: echo dbr_id:0-Spectral 1-CFR val:0-disable 1-enable\n"); ++ goto out; ++ } ++ ++ if (enable) { ++ ret = ath11k_debugfs_dbr_dbg_init(ar, dbr_id); ++ if (ret) { ++ ath11k_warn(ar->ab, "db ring module debugfs init failed: %d\n", ++ ret); ++ goto out; ++ } ++ } else { ++ ath11k_debugfs_dbr_dbg_destroy(ar, dbr_id); ++ } ++ ++ ret = count; ++out: ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ ++static const struct file_operations fops_dbr_debug = { ++ .write = ath11k_debugfs_write_enable_dbr_dbg, ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ + int ath11k_debugfs_register(struct ath11k *ar) + { + struct ath11k_base *ab = ar->ab; +@@ -1218,11 +1420,30 @@ int ath11k_debugfs_register(struct ath11 + &ar->dfs_block_radar_events); + } + ++ if (ab->hw_params.dbr_debug_support) ++ debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev, ++ ar, &fops_dbr_debug); ++ + return 0; + } + + void ath11k_debugfs_unregister(struct ath11k *ar) + { ++ struct ath11k_debug_dbr *dbr_debug; ++ struct ath11k_dbg_dbr_data *dbr_dbg_data; ++ int i; ++ ++ for (i = 0; i < WMI_DIRECT_BUF_MAX; i++) { ++ dbr_debug = ar->debug.dbr_debug[i]; ++ if (!dbr_debug) ++ continue; ++ ++ dbr_dbg_data = &dbr_debug->dbr_dbg_data; ++ kfree(dbr_dbg_data->entries); ++ debugfs_remove_recursive(dbr_debug->dbr_debugfs); ++ kfree(dbr_debug); ++ ar->debug.dbr_debug[i] = NULL; ++ } + } + + static ssize_t ath11k_write_twt_add_dialog(struct file *file, +--- a/drivers/net/wireless/ath/ath11k/debugfs.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs.h +@@ -47,6 +47,36 @@ enum ath11k_dbg_htt_ext_stats_type { + ATH11K_DBG_HTT_NUM_EXT_STATS, + }; + ++#define ATH11K_DEBUG_DBR_ENTRIES_MAX 512 ++ ++enum ath11k_dbg_dbr_event { ++ ATH11K_DBG_DBR_EVENT_INVALID, ++ ATH11K_DBG_DBR_EVENT_RX, ++ ATH11K_DBG_DBR_EVENT_REPLENISH, ++ ATH11K_DBG_DBR_EVENT_MAX, ++}; ++ ++struct ath11k_dbg_dbr_entry { ++ u32 hp; ++ u32 tp; ++ u64 timestamp; ++ enum ath11k_dbg_dbr_event event; ++}; ++ ++struct ath11k_dbg_dbr_data { ++ /* protects ath11k_db_ring_debug data */ ++ spinlock_t lock; ++ struct ath11k_dbg_dbr_entry *entries; ++ u32 dbr_debug_idx; ++ u32 num_ring_debug_entries; ++}; ++ ++struct ath11k_debug_dbr { ++ struct ath11k_dbg_dbr_data dbr_dbg_data; ++ struct dentry *dbr_debugfs; ++ bool dbr_debug_enabled; ++}; ++ + struct debug_htt_stats_req { + bool done; + u8 pdev_id; +@@ -278,6 +308,10 @@ static inline int ath11k_debugfs_rx_filt + + int ath11k_debugfs_add_interface(struct ath11k_vif *arvif); + void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif); ++void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, ++ enum wmi_direct_buffer_module id, ++ enum ath11k_dbg_dbr_event event, ++ struct hal_srng *srng); + + #else + static inline int ath11k_debugfs_soc_create(struct ath11k_base *ab) +@@ -361,6 +395,13 @@ static inline void ath11k_debugfs_remove + { + } + ++static inline void ++ath11k_debugfs_add_dbring_entry(struct ath11k *ar, ++ enum wmi_direct_buffer_module id, ++ enum ath11k_dbg_dbr_event event, ++ struct hal_srng *srng) ++{ ++} + #endif /* CPTCFG_ATH11K_DEBUGFS*/ + + #endif /* _ATH11K_DEBUGFS_H_ */ +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -193,6 +193,7 @@ struct ath11k_hw_params { + bool supports_rssi_stats; + bool fw_wmi_diag_event; + bool current_cc_support; ++ bool dbr_debug_support; + }; + + struct ath11k_hw_ops { diff --git a/package/kernel/mac80211/patches/ath11k/0192-ath11k-remove-unneeded-flush_workqueue.patch b/package/kernel/mac80211/patches/ath11k/0192-ath11k-remove-unneeded-flush_workqueue.patch new file mode 100644 index 00000000000000..d5623f722d364e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0192-ath11k-remove-unneeded-flush_workqueue.patch @@ -0,0 +1,26 @@ +From 57fe207f752a95e1929e242dfdb21c6dac741e0d Mon Sep 17 00:00:00 2001 +From: "Lv Ruyi (CGEL ZTE)" +Date: Tue, 1 Mar 2022 01:32:46 +0000 +Subject: [PATCH] ath11k: remove unneeded flush_workqueue + +All work currently pending will be done first by calling destroy_workqueue, +so there is no need to flush it explicitly. + +Reported-by: Zeal Robot +Signed-off-by: Lv Ruyi (CGEL ZTE) +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220301013246.2052570-1-lv.ruyi@zte.com.cn +--- + drivers/net/wireless/ath/ath11k/core.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1417,7 +1417,6 @@ EXPORT_SYMBOL(ath11k_core_deinit); + + void ath11k_core_free(struct ath11k_base *ab) + { +- flush_workqueue(ab->workqueue); + destroy_workqueue(ab->workqueue); + + kfree(ab); diff --git a/package/kernel/mac80211/patches/ath11k/0193-ath11k-Add-basic-WoW-functionalities.patch b/package/kernel/mac80211/patches/ath11k/0193-ath11k-Add-basic-WoW-functionalities.patch new file mode 100644 index 00000000000000..7185b0cf786ecb --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0193-ath11k-Add-basic-WoW-functionalities.patch @@ -0,0 +1,1021 @@ +From ba9177fcef21fa98406e73c472b5ac2eb4ec5f31 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 14 Mar 2022 07:18:15 +0200 +Subject: [PATCH] ath11k: Add basic WoW functionalities + +Implement basic WoW functionalities such as magic-packet, disconnect +and pattern. The logic is very similar to ath10k. + +When WoW is configured, ath11k_core_suspend and ath11k_core_resume +are skipped as WoW configuration and hif suspend/resume are done in +ath11k_wow_op_suspend() and ath11k_wow_op_resume(). + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Baochen Qiang +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644308006-22784-2-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/Makefile | 4 +- + drivers/net/wireless/ath/ath11k/core.c | 33 ++ + drivers/net/wireless/ath/ath11k/core.h | 4 + + drivers/net/wireless/ath/ath11k/htc.c | 6 + + drivers/net/wireless/ath/ath11k/mac.c | 59 +++- + drivers/net/wireless/ath/ath11k/mac.h | 1 + + drivers/net/wireless/ath/ath11k/wmi.c | 158 +++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 76 ++++- + drivers/net/wireless/ath/ath11k/wow.c | 414 +++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wow.h | 45 +++ + 10 files changed, 781 insertions(+), 19 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/Makefile ++++ b/drivers/net/wireless/ath/ath11k/Makefile +@@ -16,14 +16,14 @@ ath11k-y += core.o \ + ce.o \ + peer.o \ + dbring.o \ +- hw.o \ +- wow.o ++ hw.o + + ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o + ath11k-$(CPTCFG_NL80211_TESTMODE) += testmode.o + ath11k-$(CPTCFG_ATH11K_TRACING) += trace.o + ath11k-$(CONFIG_THERMAL) += thermal.o + ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o ++ath11k-$(CONFIG_PM) += wow.o + + obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o + ath11k_ahb-y += ahb.o +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -428,13 +428,30 @@ static const struct ath11k_hw_params ath + }, + }; + ++static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) ++{ ++ WARN_ON(!ab->hw_params.single_pdev_only); ++ ++ return &ab->pdevs[0]; ++} ++ + int ath11k_core_suspend(struct ath11k_base *ab) + { + int ret; ++ struct ath11k_pdev *pdev; ++ struct ath11k *ar; + + if (!ab->hw_params.supports_suspend) + return -EOPNOTSUPP; + ++ /* so far single_pdev_only chips have supports_suspend as true ++ * and only the first pdev is valid. ++ */ ++ pdev = ath11k_core_get_single_pdev(ab); ++ ar = pdev->ar; ++ if (!ar || ar->state != ATH11K_STATE_OFF) ++ return 0; ++ + /* TODO: there can frames in queues so for now add delay as a hack. + * Need to implement to handle and remove this delay. + */ +@@ -447,6 +464,12 @@ int ath11k_core_suspend(struct ath11k_ba + return ret; + } + ++ ret = ath11k_mac_wait_tx_complete(ar); ++ if (ret) { ++ ath11k_warn(ab, "failed to wait tx complete: %d\n", ret); ++ return ret; ++ } ++ + ret = ath11k_wow_enable(ab); + if (ret) { + ath11k_warn(ab, "failed to enable wow during suspend: %d\n", ret); +@@ -479,10 +502,20 @@ EXPORT_SYMBOL(ath11k_core_suspend); + int ath11k_core_resume(struct ath11k_base *ab) + { + int ret; ++ struct ath11k_pdev *pdev; ++ struct ath11k *ar; + + if (!ab->hw_params.supports_suspend) + return -EOPNOTSUPP; + ++ /* so far signle_pdev_only chips have supports_suspend as true ++ * and only the first pdev is valid. ++ */ ++ pdev = ath11k_core_get_single_pdev(ab); ++ ar = pdev->ar; ++ if (!ar || ar->state != ATH11K_STATE_OFF) ++ return 0; ++ + ret = ath11k_hif_resume(ab); + if (ret) { + ath11k_warn(ab, "failed to resume hif during resume: %d\n", ret); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -23,6 +23,7 @@ + #include "thermal.h" + #include "dbring.h" + #include "spectral.h" ++#include "wow.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +@@ -590,6 +591,9 @@ struct ath11k { + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + ++ struct ath11k_wow wow; ++ struct completion target_suspend; ++ bool target_suspend_ack; + struct ath11k_per_peer_tx_stats peer_tx_stats; + struct list_head ppdu_stats_info; + u32 ppdu_stat_list_depth; +--- a/drivers/net/wireless/ath/ath11k/htc.c ++++ b/drivers/net/wireless/ath/ath11k/htc.c +@@ -272,6 +272,11 @@ void ath11k_htc_tx_completion_handler(st + ep_tx_complete(htc->ab, skb); + } + ++static void ath11k_htc_wakeup_from_suspend(struct ath11k_base *ab) ++{ ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot wakeup from suspend is received\n"); ++} ++ + void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, + struct sk_buff *skb) + { +@@ -376,6 +381,7 @@ void ath11k_htc_rx_completion_handler(st + ath11k_htc_suspend_complete(ab, false); + break; + case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID: ++ ath11k_htc_wakeup_from_suspend(ab); + break; + default: + ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n", +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -16,6 +16,8 @@ + #include "testmode.h" + #include "peer.h" + #include "debugfs_sta.h" ++#include "hif.h" ++#include "wow.h" + + #define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ +@@ -7258,31 +7260,47 @@ static int ath11k_mac_op_set_frag_thresh + return -EOPNOTSUPP; + } + +-static void ath11k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +- u32 queues, bool drop) ++static int ath11k_mac_flush_tx_complete(struct ath11k *ar) + { +- struct ath11k *ar = hw->priv; + long time_left; +- +- if (drop) +- return; ++ int ret = 0; + + time_left = wait_event_timeout(ar->dp.tx_empty_waitq, + (atomic_read(&ar->dp.num_tx_pending) == 0), + ATH11K_FLUSH_TIMEOUT); +- if (time_left == 0) +- ath11k_warn(ar->ab, "failed to flush transmit queue %ld\n", time_left); ++ if (time_left == 0) { ++ ath11k_warn(ar->ab, "failed to flush transmit queue, data pkts pending %d\n", ++ atomic_read(&ar->dp.num_tx_pending)); ++ ret = -ETIMEDOUT; ++ } + + time_left = wait_event_timeout(ar->txmgmt_empty_waitq, + (atomic_read(&ar->num_pending_mgmt_tx) == 0), + ATH11K_FLUSH_TIMEOUT); +- if (time_left == 0) +- ath11k_warn(ar->ab, "failed to flush mgmt transmit queue %ld\n", +- time_left); ++ if (time_left == 0) { ++ ath11k_warn(ar->ab, "failed to flush mgmt transmit queue, mgmt pkts pending %d\n", ++ atomic_read(&ar->num_pending_mgmt_tx)); ++ ret = -ETIMEDOUT; ++ } + +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, +- "mac mgmt tx flush mgmt pending %d\n", +- atomic_read(&ar->num_pending_mgmt_tx)); ++ return ret; ++} ++ ++int ath11k_mac_wait_tx_complete(struct ath11k *ar) ++{ ++ ath11k_mac_drain_tx(ar); ++ return ath11k_mac_flush_tx_complete(ar); ++} ++ ++static void ath11k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ u32 queues, bool drop) ++{ ++ struct ath11k *ar = hw->priv; ++ ++ if (drop) ++ return; ++ ++ ath11k_mac_flush_tx_complete(ar); + } + + static int +@@ -8104,6 +8122,13 @@ static const struct ieee80211_ops ath11k + .flush = ath11k_mac_op_flush, + .sta_statistics = ath11k_mac_op_sta_statistics, + CFG80211_TESTMODE_CMD(ath11k_tm_cmd) ++ ++#ifdef CONFIG_PM ++ .suspend = ath11k_wow_op_suspend, ++ .resume = ath11k_wow_op_resume, ++ .set_wakeup = ath11k_wow_op_set_wakeup, ++#endif ++ + #ifdef CPTCFG_ATH11K_DEBUGFS + .sta_add_debugfs = ath11k_debugfs_sta_op_add, + #endif +@@ -8473,6 +8498,12 @@ static int __ath11k_mac_register(struct + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + } + ++ ret = ath11k_wow_init(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to init wow: %d\n", ret); ++ goto err_free_if_combs; ++ } ++ + ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; + ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -172,4 +172,5 @@ enum hal_encrypt_type ath11k_dp_tx_get_e + void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb); + void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id); + void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif); ++int ath11k_mac_wait_tx_complete(struct ath11k *ar); + #endif +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -8235,3 +8235,161 @@ int ath11k_wmi_scan_prob_req_oui(struct + + return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID); + } ++ ++int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id, ++ enum wmi_wow_wakeup_event event, ++ u32 enable) ++{ ++ struct wmi_wow_add_del_event_cmd *cmd; ++ struct sk_buff *skb; ++ size_t len; ++ ++ len = sizeof(*cmd); ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_wow_add_del_event_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_WOW_ADD_DEL_EVT_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = vdev_id; ++ cmd->is_add = enable; ++ cmd->event_bitmap = (1 << event); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv wow add wakeup event %s enable %d vdev_id %d\n", ++ wow_wakeup_event(event), enable, vdev_id); ++ ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); ++} ++ ++int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id, ++ const u8 *pattern, const u8 *mask, ++ int pattern_len, int pattern_offset) ++{ ++ struct wmi_wow_add_pattern_cmd *cmd; ++ struct wmi_wow_bitmap_pattern *bitmap; ++ struct wmi_tlv *tlv; ++ struct sk_buff *skb; ++ u8 *ptr; ++ size_t len; ++ ++ len = sizeof(*cmd) + ++ sizeof(*tlv) + /* array struct */ ++ sizeof(*bitmap) + /* bitmap */ ++ sizeof(*tlv) + /* empty ipv4 sync */ ++ sizeof(*tlv) + /* empty ipv6 sync */ ++ sizeof(*tlv) + /* empty magic */ ++ sizeof(*tlv) + /* empty info timeout */ ++ sizeof(*tlv) + sizeof(u32); /* ratelimit interval */ ++ ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ /* cmd */ ++ ptr = (u8 *)skb->data; ++ cmd = (struct wmi_wow_add_pattern_cmd *)ptr; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_WOW_ADD_PATTERN_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = vdev_id; ++ cmd->pattern_id = pattern_id; ++ cmd->pattern_type = WOW_BITMAP_PATTERN; ++ ++ ptr += sizeof(*cmd); ++ ++ /* bitmap */ ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*bitmap)); ++ ++ ptr += sizeof(*tlv); ++ ++ bitmap = (struct wmi_wow_bitmap_pattern *)ptr; ++ bitmap->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_WOW_BITMAP_PATTERN_T) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*bitmap) - TLV_HDR_SIZE); ++ ++ memcpy(bitmap->patternbuf, pattern, pattern_len); ++ ath11k_ce_byte_swap(bitmap->patternbuf, roundup(pattern_len, 4)); ++ memcpy(bitmap->bitmaskbuf, mask, pattern_len); ++ ath11k_ce_byte_swap(bitmap->bitmaskbuf, roundup(pattern_len, 4)); ++ bitmap->pattern_offset = pattern_offset; ++ bitmap->pattern_len = pattern_len; ++ bitmap->bitmask_len = pattern_len; ++ bitmap->pattern_id = pattern_id; ++ ++ ptr += sizeof(*bitmap); ++ ++ /* ipv4 sync */ ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, 0); ++ ++ ptr += sizeof(*tlv); ++ ++ /* ipv6 sync */ ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, 0); ++ ++ ptr += sizeof(*tlv); ++ ++ /* magic */ ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, 0); ++ ++ ptr += sizeof(*tlv); ++ ++ /* pattern info timeout */ ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_ARRAY_UINT32) | ++ FIELD_PREP(WMI_TLV_LEN, 0); ++ ++ ptr += sizeof(*tlv); ++ ++ /* ratelimit interval */ ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_ARRAY_UINT32) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(u32)); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv wow add pattern vdev_id %d pattern_id %d pattern_offset %d\n", ++ vdev_id, pattern_id, pattern_offset); ++ ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); ++} ++ ++int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id) ++{ ++ struct wmi_wow_del_pattern_cmd *cmd; ++ struct sk_buff *skb; ++ size_t len; ++ ++ len = sizeof(*cmd); ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_wow_del_pattern_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_WOW_DEL_PATTERN_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = vdev_id; ++ cmd->pattern_id = pattern_id; ++ cmd->pattern_type = WOW_BITMAP_PATTERN; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv wow del pattern vdev_id %d pattern_id %d\n", ++ vdev_id, pattern_id); ++ ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); ++} +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5534,6 +5534,45 @@ static inline const char *wow_reason(enu + + #undef C2S + ++struct wmi_wow_ev_arg { ++ u32 vdev_id; ++ u32 flag; ++ enum wmi_wow_wake_reason wake_reason; ++ u32 data_len; ++}; ++ ++enum wmi_tlv_pattern_type { ++ WOW_PATTERN_MIN = 0, ++ WOW_BITMAP_PATTERN = WOW_PATTERN_MIN, ++ WOW_IPV4_SYNC_PATTERN, ++ WOW_IPV6_SYNC_PATTERN, ++ WOW_WILD_CARD_PATTERN, ++ WOW_TIMER_PATTERN, ++ WOW_MAGIC_PATTERN, ++ WOW_IPV6_RA_PATTERN, ++ WOW_IOAC_PKT_PATTERN, ++ WOW_IOAC_TMR_PATTERN, ++ WOW_PATTERN_MAX ++}; ++ ++#define WOW_DEFAULT_BITMAP_PATTERN_SIZE 148 ++#define WOW_DEFAULT_BITMASK_SIZE 148 ++ ++#define WOW_MIN_PATTERN_SIZE 1 ++#define WOW_MAX_PATTERN_SIZE 148 ++#define WOW_MAX_PKT_OFFSET 128 ++#define WOW_HDR_LEN (sizeof(struct ieee80211_hdr_3addr) + \ ++ sizeof(struct rfc1042_hdr)) ++#define WOW_MAX_REDUCE (WOW_HDR_LEN - sizeof(struct ethhdr) - \ ++ offsetof(struct ieee80211_hdr_3addr, addr1)) ++ ++struct wmi_wow_add_del_event_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 is_add; ++ u32 event_bitmap; ++} __packed; ++ + struct wmi_wow_enable_cmd { + u32 tlv_header; + u32 enable; +@@ -5546,12 +5585,36 @@ struct wmi_wow_host_wakeup_ind { + u32 reserved; + } __packed; + +-struct wmi_wow_ev_arg { ++struct wmi_tlv_wow_event_info { + u32 vdev_id; + u32 flag; +- enum wmi_wow_wake_reason wake_reason; ++ u32 wake_reason; + u32 data_len; +-}; ++} __packed; ++ ++struct wmi_wow_bitmap_pattern { ++ u32 tlv_header; ++ u8 patternbuf[WOW_DEFAULT_BITMAP_PATTERN_SIZE]; ++ u8 bitmaskbuf[WOW_DEFAULT_BITMASK_SIZE]; ++ u32 pattern_offset; ++ u32 pattern_len; ++ u32 bitmask_len; ++ u32 pattern_id; ++} __packed; ++ ++struct wmi_wow_add_pattern_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 pattern_id; ++ u32 pattern_type; ++} __packed; ++ ++struct wmi_wow_del_pattern_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 pattern_id; ++ u32 pattern_type; ++} __packed; + + int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); +@@ -5714,4 +5777,11 @@ int ath11k_wmi_scan_prob_req_oui(struct + const u8 mac_addr[ETH_ALEN]); + int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap, + struct ath11k_fw_dbglog *dbglog); ++int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id); ++int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id, ++ const u8 *pattern, const u8 *mask, ++ int pattern_len, int pattern_offset); ++int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id, ++ enum wmi_wow_wakeup_event event, ++ u32 enable); + #endif +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -6,12 +6,22 @@ + #include + + #include "mac.h" ++ ++#include + #include "core.h" + #include "hif.h" + #include "debug.h" + #include "wmi.h" + #include "wow.h" + ++static const struct wiphy_wowlan_support ath11k_wowlan_support = { ++ .flags = WIPHY_WOWLAN_DISCONNECT | ++ WIPHY_WOWLAN_MAGIC_PKT, ++ .pattern_min_len = WOW_MIN_PATTERN_SIZE, ++ .pattern_max_len = WOW_MAX_PATTERN_SIZE, ++ .max_pkt_offset = WOW_MAX_PKT_OFFSET, ++}; ++ + int ath11k_wow_enable(struct ath11k_base *ab) + { + struct ath11k *ar = ath11k_ab_to_ar(ab, 0); +@@ -71,3 +81,407 @@ int ath11k_wow_wakeup(struct ath11k_base + + return 0; + } ++ ++static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif) ++{ ++ struct ath11k *ar = arvif->ar; ++ int i, ret; ++ ++ for (i = 0; i < WOW_EVENT_MAX; i++) { ++ ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n", ++ wow_wakeup_event(i), arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ for (i = 0; i < ar->wow.max_num_patterns; i++) { ++ ret = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n", ++ i, arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ath11k_wow_cleanup(struct ath11k *ar) ++{ ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ ret = ath11k_wow_vif_cleanup(arvif); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Convert a 802.3 format to a 802.11 format. ++ * +------------+-----------+--------+----------------+ ++ * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | ++ * +------------+-----------+--------+----------------+ ++ * |__ |_______ |____________ |________ ++ * | | | | ++ * +--+------------+----+-----------+---------------+-----------+ ++ * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | ++ * +--+------------+----+-----------+---------------+-----------+ ++ */ ++static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, ++ const struct cfg80211_pkt_pattern *old) ++{ ++ u8 hdr_8023_pattern[ETH_HLEN] = {}; ++ u8 hdr_8023_bit_mask[ETH_HLEN] = {}; ++ u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; ++ u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; ++ ++ int total_len = old->pkt_offset + old->pattern_len; ++ int hdr_80211_end_offset; ++ ++ struct ieee80211_hdr_3addr *new_hdr_pattern = ++ (struct ieee80211_hdr_3addr *)hdr_80211_pattern; ++ struct ieee80211_hdr_3addr *new_hdr_mask = ++ (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; ++ struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; ++ struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; ++ int hdr_len = sizeof(*new_hdr_pattern); ++ ++ struct rfc1042_hdr *new_rfc_pattern = ++ (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); ++ struct rfc1042_hdr *new_rfc_mask = ++ (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); ++ int rfc_len = sizeof(*new_rfc_pattern); ++ ++ memcpy(hdr_8023_pattern + old->pkt_offset, ++ old->pattern, ETH_HLEN - old->pkt_offset); ++ memcpy(hdr_8023_bit_mask + old->pkt_offset, ++ old->mask, ETH_HLEN - old->pkt_offset); ++ ++ /* Copy destination address */ ++ memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); ++ memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); ++ ++ /* Copy source address */ ++ memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); ++ memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); ++ ++ /* Copy logic link type */ ++ memcpy(&new_rfc_pattern->snap_type, ++ &old_hdr_pattern->h_proto, ++ sizeof(old_hdr_pattern->h_proto)); ++ memcpy(&new_rfc_mask->snap_type, ++ &old_hdr_mask->h_proto, ++ sizeof(old_hdr_mask->h_proto)); ++ ++ /* Compute new pkt_offset */ ++ if (old->pkt_offset < ETH_ALEN) ++ new->pkt_offset = old->pkt_offset + ++ offsetof(struct ieee80211_hdr_3addr, addr1); ++ else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) ++ new->pkt_offset = old->pkt_offset + ++ offsetof(struct ieee80211_hdr_3addr, addr3) - ++ offsetof(struct ethhdr, h_source); ++ else ++ new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; ++ ++ /* Compute new hdr end offset */ ++ if (total_len > ETH_HLEN) ++ hdr_80211_end_offset = hdr_len + rfc_len; ++ else if (total_len > offsetof(struct ethhdr, h_proto)) ++ hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; ++ else if (total_len > ETH_ALEN) ++ hdr_80211_end_offset = total_len - ETH_ALEN + ++ offsetof(struct ieee80211_hdr_3addr, addr3); ++ else ++ hdr_80211_end_offset = total_len + ++ offsetof(struct ieee80211_hdr_3addr, addr1); ++ ++ new->pattern_len = hdr_80211_end_offset - new->pkt_offset; ++ ++ memcpy((u8 *)new->pattern, ++ hdr_80211_pattern + new->pkt_offset, ++ new->pattern_len); ++ memcpy((u8 *)new->mask, ++ hdr_80211_bit_mask + new->pkt_offset, ++ new->pattern_len); ++ ++ if (total_len > ETH_HLEN) { ++ /* Copy frame body */ ++ memcpy((u8 *)new->pattern + new->pattern_len, ++ (void *)old->pattern + ETH_HLEN - old->pkt_offset, ++ total_len - ETH_HLEN); ++ memcpy((u8 *)new->mask + new->pattern_len, ++ (void *)old->mask + ETH_HLEN - old->pkt_offset, ++ total_len - ETH_HLEN); ++ ++ new->pattern_len += total_len - ETH_HLEN; ++ } ++} ++ ++static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif, ++ struct cfg80211_wowlan *wowlan) ++{ ++ int ret, i; ++ unsigned long wow_mask = 0; ++ struct ath11k *ar = arvif->ar; ++ const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; ++ int pattern_id = 0; ++ ++ /* Setup requested WOW features */ ++ switch (arvif->vdev_type) { ++ case WMI_VDEV_TYPE_IBSS: ++ __set_bit(WOW_BEACON_EVENT, &wow_mask); ++ fallthrough; ++ case WMI_VDEV_TYPE_AP: ++ __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); ++ __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); ++ __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); ++ __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); ++ __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); ++ __set_bit(WOW_HTT_EVENT, &wow_mask); ++ __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); ++ break; ++ case WMI_VDEV_TYPE_STA: ++ if (wowlan->disconnect) { ++ __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); ++ __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); ++ __set_bit(WOW_BMISS_EVENT, &wow_mask); ++ __set_bit(WOW_CSA_IE_EVENT, &wow_mask); ++ } ++ ++ if (wowlan->magic_pkt) ++ __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); ++ break; ++ default: ++ break; ++ } ++ ++ for (i = 0; i < wowlan->n_patterns; i++) { ++ u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; ++ u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; ++ u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; ++ struct cfg80211_pkt_pattern new_pattern = {}; ++ struct cfg80211_pkt_pattern old_pattern = patterns[i]; ++ int j; ++ ++ new_pattern.pattern = ath_pattern; ++ new_pattern.mask = ath_bitmask; ++ if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) ++ continue; ++ /* convert bytemask to bitmask */ ++ for (j = 0; j < patterns[i].pattern_len; j++) ++ if (patterns[i].mask[j / 8] & BIT(j % 8)) ++ bitmask[j] = 0xff; ++ old_pattern.mask = bitmask; ++ ++ if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode == ++ ATH11K_HW_TXRX_NATIVE_WIFI) { ++ if (patterns[i].pkt_offset < ETH_HLEN) { ++ u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {}; ++ ++ memcpy(pattern_ext, old_pattern.pattern, ++ old_pattern.pattern_len); ++ old_pattern.pattern = pattern_ext; ++ ath11k_wow_convert_8023_to_80211(&new_pattern, ++ &old_pattern); ++ } else { ++ new_pattern = old_pattern; ++ new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; ++ } ++ } ++ ++ if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) ++ return -EINVAL; ++ ++ ret = ath11k_wmi_wow_add_pattern(ar, arvif->vdev_id, ++ pattern_id, ++ new_pattern.pattern, ++ new_pattern.mask, ++ new_pattern.pattern_len, ++ new_pattern.pkt_offset); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n", ++ pattern_id, ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ ++ pattern_id++; ++ __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); ++ } ++ ++ for (i = 0; i < WOW_EVENT_MAX; i++) { ++ if (!test_bit(i, &wow_mask)) ++ continue; ++ ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n", ++ wow_wakeup_event(i), arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ath11k_wow_set_wakeups(struct ath11k *ar, ++ struct cfg80211_wowlan *wowlan) ++{ ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ ret = ath11k_vif_wow_set_wakeups(arvif, wowlan); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++int ath11k_wow_op_suspend(struct ieee80211_hw *hw, ++ struct cfg80211_wowlan *wowlan) ++{ ++ struct ath11k *ar = hw->priv; ++ int ret; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ ret = ath11k_wow_cleanup(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n", ++ ret); ++ goto exit; ++ } ++ ++ ret = ath11k_wow_set_wakeups(ar, wowlan); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n", ++ ret); ++ goto cleanup; ++ } ++ ++ ret = ath11k_mac_wait_tx_complete(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret); ++ goto cleanup; ++ } ++ ++ ret = ath11k_wow_enable(ar->ab); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start wow: %d\n", ret); ++ goto cleanup; ++ } ++ ++ ath11k_ce_stop_shadow_timers(ar->ab); ++ ath11k_dp_stop_shadow_timers(ar->ab); ++ ++ ath11k_hif_irq_disable(ar->ab); ++ ath11k_hif_ce_irq_disable(ar->ab); ++ ++ ret = ath11k_hif_suspend(ar->ab); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret); ++ goto wakeup; ++ } ++ ++ goto exit; ++ ++wakeup: ++ ath11k_wow_wakeup(ar->ab); ++ ++cleanup: ++ ath11k_wow_cleanup(ar); ++ ++exit: ++ mutex_unlock(&ar->conf_mutex); ++ return ret ? 1 : 0; ++} ++ ++void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) ++{ ++ struct ath11k *ar = hw->priv; ++ ++ mutex_lock(&ar->conf_mutex); ++ device_set_wakeup_enable(ar->ab->dev, enabled); ++ mutex_unlock(&ar->conf_mutex); ++} ++ ++int ath11k_wow_op_resume(struct ieee80211_hw *hw) ++{ ++ struct ath11k *ar = hw->priv; ++ int ret; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ ret = ath11k_hif_resume(ar->ab); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret); ++ goto exit; ++ } ++ ++ ath11k_hif_ce_irq_enable(ar->ab); ++ ath11k_hif_irq_enable(ar->ab); ++ ++ ret = ath11k_wow_wakeup(ar->ab); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); ++ ++exit: ++ if (ret) { ++ switch (ar->state) { ++ case ATH11K_STATE_ON: ++ ar->state = ATH11K_STATE_RESTARTING; ++ ret = 1; ++ break; ++ case ATH11K_STATE_OFF: ++ case ATH11K_STATE_RESTARTING: ++ case ATH11K_STATE_RESTARTED: ++ case ATH11K_STATE_WEDGED: ++ ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", ++ ar->state); ++ ret = -EIO; ++ break; ++ } ++ } ++ ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ ++int ath11k_wow_init(struct ath11k *ar) ++{ ++ if (WARN_ON(!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))) ++ return -EINVAL; ++ ++ ar->wow.wowlan_support = ath11k_wowlan_support; ++ ++ if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode == ++ ATH11K_HW_TXRX_NATIVE_WIFI) { ++ ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; ++ ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; ++ } ++ ++ ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS; ++ ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; ++ ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; ++ ++ device_set_wakeup_capable(ar->ab->dev, true); ++ ++ return 0; ++} +--- a/drivers/net/wireless/ath/ath11k/wow.h ++++ b/drivers/net/wireless/ath/ath11k/wow.h +@@ -3,8 +3,53 @@ + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + ++#ifndef _WOW_H_ ++#define _WOW_H_ ++ ++struct ath11k_wow { ++ u32 max_num_patterns; ++ struct completion wakeup_completed; ++ struct wiphy_wowlan_support wowlan_support; ++}; ++ ++struct rfc1042_hdr { ++ u8 llc_dsap; ++ u8 llc_ssap; ++ u8 llc_ctrl; ++ u8 snap_oui[3]; ++ __be16 snap_type; ++} __packed; ++ + #define ATH11K_WOW_RETRY_NUM 3 + #define ATH11K_WOW_RETRY_WAIT_MS 200 ++#define ATH11K_WOW_PATTERNS 22 + ++#ifdef CONFIG_PM ++ ++int ath11k_wow_init(struct ath11k *ar); ++int ath11k_wow_op_suspend(struct ieee80211_hw *hw, ++ struct cfg80211_wowlan *wowlan); ++int ath11k_wow_op_resume(struct ieee80211_hw *hw); ++void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled); + int ath11k_wow_enable(struct ath11k_base *ab); + int ath11k_wow_wakeup(struct ath11k_base *ab); ++ ++#else ++ ++static inline int ath11k_wow_init(struct ath11k *ar) ++{ ++ return 0; ++} ++ ++static inline int ath11k_wow_enable(struct ath11k_base *ab) ++{ ++ return 0; ++} ++ ++static inline int ath11k_wow_wakeup(struct ath11k_base *ab) ++{ ++ return 0; ++} ++ ++#endif /* CONFIG_PM */ ++#endif /* _WOW_H_ */ diff --git a/package/kernel/mac80211/patches/ath11k/0194-ath11k-Add-WoW-net-detect-functionality.patch b/package/kernel/mac80211/patches/ath11k/0194-ath11k-Add-WoW-net-detect-functionality.patch new file mode 100644 index 00000000000000..8ba83227edd8b8 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0194-ath11k-Add-WoW-net-detect-functionality.patch @@ -0,0 +1,614 @@ +From fec4b898f369a9b9d516f7bfc459eb4a8c5ceb2c Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 14 Mar 2022 07:18:16 +0200 +Subject: [PATCH] ath11k: Add WoW net-detect functionality + +Implement net-detect feature by setting flag +WIPHY_WOWLAN_NET_DETECT if firmware supports this +feature. Driver sets the related PNO configuration +to firmware before entering WoW and firmware then +scans periodically and wakes up host if a specific +SSID is found. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644308006-22784-3-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 12 ++ + drivers/net/wireless/ath/ath11k/wmi.c | 154 ++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 169 ++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wow.c | 175 ++++++++++++++++++++++++- + 5 files changed, 510 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -617,6 +617,7 @@ struct ath11k { + bool regdom_set_by_user; + int hw_rate_code; + u8 twt_enabled; ++ bool nlo_enabled; + }; + + struct ath11k_band_cap { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8498,6 +8498,18 @@ static int __ath11k_mac_register(struct + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + } + ++ if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) { ++ ar->hw->wiphy->max_sched_scan_ssids = WMI_PNO_MAX_SUPP_NETWORKS; ++ ar->hw->wiphy->max_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; ++ ar->hw->wiphy->max_sched_scan_ie_len = WMI_PNO_MAX_IE_LENGTH; ++ ar->hw->wiphy->max_sched_scan_plans = WMI_PNO_MAX_SCHED_SCAN_PLANS; ++ ar->hw->wiphy->max_sched_scan_plan_interval = ++ WMI_PNO_MAX_SCHED_SCAN_PLAN_INT; ++ ar->hw->wiphy->max_sched_scan_plan_iterations = ++ WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS; ++ ar->hw->wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; ++ } ++ + ret = ath11k_wow_init(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to init wow: %d\n", ret); +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -8393,3 +8393,157 @@ int ath11k_wmi_wow_del_pattern(struct at + + return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); + } ++ ++static struct sk_buff * ++ath11k_wmi_op_gen_config_pno_start(struct ath11k *ar, ++ u32 vdev_id, ++ struct wmi_pno_scan_req *pno) ++{ ++ struct nlo_configured_parameters *nlo_list; ++ struct wmi_wow_nlo_config_cmd *cmd; ++ struct wmi_tlv *tlv; ++ struct sk_buff *skb; ++ u32 *channel_list; ++ size_t len, nlo_list_len, channel_list_len; ++ u8 *ptr; ++ u32 i; ++ ++ len = sizeof(*cmd) + ++ sizeof(*tlv) + ++ /* TLV place holder for array of structures ++ * nlo_configured_parameters(nlo_list) ++ */ ++ sizeof(*tlv); ++ /* TLV place holder for array of uint32 channel_list */ ++ ++ channel_list_len = sizeof(u32) * pno->a_networks[0].channel_count; ++ len += channel_list_len; ++ ++ nlo_list_len = sizeof(*nlo_list) * pno->uc_networks_count; ++ len += nlo_list_len; ++ ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return ERR_PTR(-ENOMEM); ++ ++ ptr = (u8 *)skb->data; ++ cmd = (struct wmi_wow_nlo_config_cmd *)ptr; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_NLO_CONFIG_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = pno->vdev_id; ++ cmd->flags = WMI_NLO_CONFIG_START | WMI_NLO_CONFIG_SSID_HIDE_EN; ++ ++ /* current FW does not support min-max range for dwell time */ ++ cmd->active_dwell_time = pno->active_max_time; ++ cmd->passive_dwell_time = pno->passive_max_time; ++ ++ if (pno->do_passive_scan) ++ cmd->flags |= WMI_NLO_CONFIG_SCAN_PASSIVE; ++ ++ cmd->fast_scan_period = pno->fast_scan_period; ++ cmd->slow_scan_period = pno->slow_scan_period; ++ cmd->fast_scan_max_cycles = pno->fast_scan_max_cycles; ++ cmd->delay_start_time = pno->delay_start_time; ++ ++ if (pno->enable_pno_scan_randomization) { ++ cmd->flags |= WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ | ++ WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ; ++ ether_addr_copy(cmd->mac_addr.addr, pno->mac_addr); ++ ether_addr_copy(cmd->mac_mask.addr, pno->mac_addr_mask); ++ ath11k_ce_byte_swap(cmd->mac_addr.addr, 8); ++ ath11k_ce_byte_swap(cmd->mac_mask.addr, 8); ++ } ++ ++ ptr += sizeof(*cmd); ++ ++ /* nlo_configured_parameters(nlo_list) */ ++ cmd->no_of_ssids = pno->uc_networks_count; ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, nlo_list_len); ++ ++ ptr += sizeof(*tlv); ++ nlo_list = (struct nlo_configured_parameters *)ptr; ++ for (i = 0; i < cmd->no_of_ssids; i++) { ++ tlv = (struct wmi_tlv *)(&nlo_list[i].tlv_header); ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*nlo_list) - sizeof(*tlv)); ++ ++ nlo_list[i].ssid.valid = true; ++ nlo_list[i].ssid.ssid.ssid_len = pno->a_networks[i].ssid.ssid_len; ++ memcpy(nlo_list[i].ssid.ssid.ssid, ++ pno->a_networks[i].ssid.ssid, ++ nlo_list[i].ssid.ssid.ssid_len); ++ ath11k_ce_byte_swap(nlo_list[i].ssid.ssid.ssid, ++ roundup(nlo_list[i].ssid.ssid.ssid_len, 4)); ++ ++ if (pno->a_networks[i].rssi_threshold && ++ pno->a_networks[i].rssi_threshold > -300) { ++ nlo_list[i].rssi_cond.valid = true; ++ nlo_list[i].rssi_cond.rssi = ++ pno->a_networks[i].rssi_threshold; ++ } ++ ++ nlo_list[i].bcast_nw_type.valid = true; ++ nlo_list[i].bcast_nw_type.bcast_nw_type = ++ pno->a_networks[i].bcast_nw_type; ++ } ++ ++ ptr += nlo_list_len; ++ cmd->num_of_channels = pno->a_networks[0].channel_count; ++ tlv = (struct wmi_tlv *)ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | ++ FIELD_PREP(WMI_TLV_LEN, channel_list_len); ++ ptr += sizeof(*tlv); ++ channel_list = (u32 *)ptr; ++ for (i = 0; i < cmd->num_of_channels; i++) ++ channel_list[i] = pno->a_networks[0].channels[i]; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv start pno config vdev_id %d\n", ++ vdev_id); ++ ++ return skb; ++} ++ ++static struct sk_buff *ath11k_wmi_op_gen_config_pno_stop(struct ath11k *ar, ++ u32 vdev_id) ++{ ++ struct wmi_wow_nlo_config_cmd *cmd; ++ struct sk_buff *skb; ++ size_t len; ++ ++ len = sizeof(*cmd); ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return ERR_PTR(-ENOMEM); ++ ++ cmd = (struct wmi_wow_nlo_config_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_NLO_CONFIG_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = vdev_id; ++ cmd->flags = WMI_NLO_CONFIG_STOP; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi tlv stop pno config vdev_id %d\n", vdev_id); ++ return skb; ++} ++ ++int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id, ++ struct wmi_pno_scan_req *pno_scan) ++{ ++ struct sk_buff *skb; ++ ++ if (pno_scan->enable) ++ skb = ath11k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan); ++ else ++ skb = ath11k_wmi_op_gen_config_pno_stop(ar, vdev_id); ++ ++ if (IS_ERR_OR_NULL(skb)) ++ return -ENOMEM; ++ ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); ++} ++ +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5616,6 +5616,173 @@ struct wmi_wow_del_pattern_cmd { + u32 pattern_type; + } __packed; + ++#define WMI_PNO_MAX_SCHED_SCAN_PLANS 2 ++#define WMI_PNO_MAX_SCHED_SCAN_PLAN_INT 7200 ++#define WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS 100 ++#define WMI_PNO_MAX_NETW_CHANNELS 26 ++#define WMI_PNO_MAX_NETW_CHANNELS_EX 60 ++#define WMI_PNO_MAX_SUPP_NETWORKS WLAN_SCAN_PARAMS_MAX_SSID ++#define WMI_PNO_MAX_IE_LENGTH WLAN_SCAN_PARAMS_MAX_IE_LEN ++ ++/* size based of dot11 declaration without extra IEs as we will not carry those for PNO */ ++#define WMI_PNO_MAX_PB_REQ_SIZE 450 ++ ++#define WMI_PNO_24G_DEFAULT_CH 1 ++#define WMI_PNO_5G_DEFAULT_CH 36 ++ ++#define WMI_ACTIVE_MAX_CHANNEL_TIME 40 ++#define WMI_PASSIVE_MAX_CHANNEL_TIME 110 ++ ++/* SSID broadcast type */ ++enum wmi_ssid_bcast_type { ++ BCAST_UNKNOWN = 0, ++ BCAST_NORMAL = 1, ++ BCAST_HIDDEN = 2, ++}; ++ ++#define WMI_NLO_MAX_SSIDS 16 ++#define WMI_NLO_MAX_CHAN 48 ++ ++#define WMI_NLO_CONFIG_STOP BIT(0) ++#define WMI_NLO_CONFIG_START BIT(1) ++#define WMI_NLO_CONFIG_RESET BIT(2) ++#define WMI_NLO_CONFIG_SLOW_SCAN BIT(4) ++#define WMI_NLO_CONFIG_FAST_SCAN BIT(5) ++#define WMI_NLO_CONFIG_SSID_HIDE_EN BIT(6) ++ ++/* This bit is used to indicate if EPNO or supplicant PNO is enabled. ++ * Only one of them can be enabled at a given time ++ */ ++#define WMI_NLO_CONFIG_ENLO BIT(7) ++#define WMI_NLO_CONFIG_SCAN_PASSIVE BIT(8) ++#define WMI_NLO_CONFIG_ENLO_RESET BIT(9) ++#define WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ BIT(10) ++#define WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ BIT(11) ++#define WMI_NLO_CONFIG_ENABLE_IE_WHITELIST_IN_PROBE_REQ BIT(12) ++#define WMI_NLO_CONFIG_ENABLE_CNLO_RSSI_CONFIG BIT(13) ++ ++struct wmi_nlo_ssid_param { ++ u32 valid; ++ struct wmi_ssid ssid; ++} __packed; ++ ++struct wmi_nlo_enc_param { ++ u32 valid; ++ u32 enc_type; ++} __packed; ++ ++struct wmi_nlo_auth_param { ++ u32 valid; ++ u32 auth_type; ++} __packed; ++ ++struct wmi_nlo_bcast_nw_param { ++ u32 valid; ++ u32 bcast_nw_type; ++} __packed; ++ ++struct wmi_nlo_rssi_param { ++ u32 valid; ++ s32 rssi; ++} __packed; ++ ++struct nlo_configured_parameters { ++ /* TLV tag and len;*/ ++ u32 tlv_header; ++ struct wmi_nlo_ssid_param ssid; ++ struct wmi_nlo_enc_param enc_type; ++ struct wmi_nlo_auth_param auth_type; ++ struct wmi_nlo_rssi_param rssi_cond; ++ ++ /* indicates if the SSID is hidden or not */ ++ struct wmi_nlo_bcast_nw_param bcast_nw_type; ++} __packed; ++ ++struct wmi_network_type { ++ struct wmi_ssid ssid; ++ u32 authentication; ++ u32 encryption; ++ u32 bcast_nw_type; ++ u8 channel_count; ++ u16 channels[WMI_PNO_MAX_NETW_CHANNELS_EX]; ++ s32 rssi_threshold; ++}; ++ ++struct wmi_pno_scan_req { ++ u8 enable; ++ u8 vdev_id; ++ u8 uc_networks_count; ++ struct wmi_network_type a_networks[WMI_PNO_MAX_SUPP_NETWORKS]; ++ u32 fast_scan_period; ++ u32 slow_scan_period; ++ u8 fast_scan_max_cycles; ++ ++ bool do_passive_scan; ++ ++ u32 delay_start_time; ++ u32 active_min_time; ++ u32 active_max_time; ++ u32 passive_min_time; ++ u32 passive_max_time; ++ ++ /* mac address randomization attributes */ ++ u32 enable_pno_scan_randomization; ++ u8 mac_addr[ETH_ALEN]; ++ u8 mac_addr_mask[ETH_ALEN]; ++}; ++ ++struct wmi_wow_nlo_config_cmd { ++ u32 tlv_header; ++ u32 flags; ++ u32 vdev_id; ++ u32 fast_scan_max_cycles; ++ u32 active_dwell_time; ++ u32 passive_dwell_time; ++ u32 probe_bundle_size; ++ ++ /* ART = IRT */ ++ u32 rest_time; ++ ++ /* Max value that can be reached after SBM */ ++ u32 max_rest_time; ++ ++ /* SBM */ ++ u32 scan_backoff_multiplier; ++ ++ /* SCBM */ ++ u32 fast_scan_period; ++ ++ /* specific to windows */ ++ u32 slow_scan_period; ++ ++ u32 no_of_ssids; ++ ++ u32 num_of_channels; ++ ++ /* NLO scan start delay time in milliseconds */ ++ u32 delay_start_time; ++ ++ /* MAC Address to use in Probe Req as SA */ ++ struct wmi_mac_addr mac_addr; ++ ++ /* Mask on which MAC has to be randomized */ ++ struct wmi_mac_addr mac_mask; ++ ++ /* IE bitmap to use in Probe Req */ ++ u32 ie_bitmap[8]; ++ ++ /* Number of vendor OUIs. In the TLV vendor_oui[] */ ++ u32 num_vendor_oui; ++ ++ /* Number of connected NLO band preferences */ ++ u32 num_cnlo_band_pref; ++ ++ /* The TLVs will follow. ++ * nlo_configured_parameters nlo_list[]; ++ * u32 channel_list[num_of_channels]; ++ */ ++} __packed; ++ + int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); + struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); +@@ -5777,6 +5944,8 @@ int ath11k_wmi_scan_prob_req_oui(struct + const u8 mac_addr[ETH_ALEN]); + int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap, + struct ath11k_fw_dbglog *dbglog); ++int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id, ++ struct wmi_pno_scan_req *pno_scan); + int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id); + int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id, + const u8 *pattern, const u8 *mask, +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -228,6 +228,101 @@ static void ath11k_wow_convert_8023_to_8 + } + } + ++static int ath11k_wmi_pno_check_and_convert(struct ath11k *ar, u32 vdev_id, ++ struct cfg80211_sched_scan_request *nd_config, ++ struct wmi_pno_scan_req *pno) ++{ ++ int i, j; ++ u8 ssid_len; ++ ++ pno->enable = 1; ++ pno->vdev_id = vdev_id; ++ pno->uc_networks_count = nd_config->n_match_sets; ++ ++ if (!pno->uc_networks_count || ++ pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) ++ return -EINVAL; ++ ++ if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) ++ return -EINVAL; ++ ++ /* Filling per profile params */ ++ for (i = 0; i < pno->uc_networks_count; i++) { ++ ssid_len = nd_config->match_sets[i].ssid.ssid_len; ++ ++ if (ssid_len == 0 || ssid_len > 32) ++ return -EINVAL; ++ ++ pno->a_networks[i].ssid.ssid_len = ssid_len; ++ ++ memcpy(pno->a_networks[i].ssid.ssid, ++ nd_config->match_sets[i].ssid.ssid, ++ nd_config->match_sets[i].ssid.ssid_len); ++ pno->a_networks[i].authentication = 0; ++ pno->a_networks[i].encryption = 0; ++ pno->a_networks[i].bcast_nw_type = 0; ++ ++ /* Copying list of valid channel into request */ ++ pno->a_networks[i].channel_count = nd_config->n_channels; ++ pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; ++ ++ for (j = 0; j < nd_config->n_channels; j++) { ++ pno->a_networks[i].channels[j] = ++ nd_config->channels[j]->center_freq; ++ } ++ } ++ ++ /* set scan to passive if no SSIDs are specified in the request */ ++ if (nd_config->n_ssids == 0) ++ pno->do_passive_scan = true; ++ else ++ pno->do_passive_scan = false; ++ ++ for (i = 0; i < nd_config->n_ssids; i++) { ++ j = 0; ++ while (j < pno->uc_networks_count) { ++ if (pno->a_networks[j].ssid.ssid_len == ++ nd_config->ssids[i].ssid_len && ++ (memcmp(pno->a_networks[j].ssid.ssid, ++ nd_config->ssids[i].ssid, ++ pno->a_networks[j].ssid.ssid_len) == 0)) { ++ pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; ++ break; ++ } ++ j++; ++ } ++ } ++ ++ if (nd_config->n_scan_plans == 2) { ++ pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; ++ pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; ++ pno->slow_scan_period = ++ nd_config->scan_plans[1].interval * MSEC_PER_SEC; ++ } else if (nd_config->n_scan_plans == 1) { ++ pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; ++ pno->fast_scan_max_cycles = 1; ++ pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; ++ } else { ++ ath11k_warn(ar->ab, "Invalid number of scan plans %d !!", ++ nd_config->n_scan_plans); ++ } ++ ++ if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { ++ /* enable mac randomization */ ++ pno->enable_pno_scan_randomization = 1; ++ memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); ++ memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); ++ } ++ ++ pno->delay_start_time = nd_config->delay; ++ ++ /* Current FW does not support min-max range for dwell time */ ++ pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; ++ pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; ++ ++ return 0; ++} ++ + static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif, + struct cfg80211_wowlan *wowlan) + { +@@ -261,6 +356,26 @@ static int ath11k_vif_wow_set_wakeups(st + + if (wowlan->magic_pkt) + __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); ++ ++ if (wowlan->nd_config) { ++ struct wmi_pno_scan_req *pno; ++ int ret; ++ ++ pno = kzalloc(sizeof(*pno), GFP_KERNEL); ++ if (!pno) ++ return -ENOMEM; ++ ++ ar->nlo_enabled = true; ++ ++ ret = ath11k_wmi_pno_check_and_convert(ar, arvif->vdev_id, ++ wowlan->nd_config, pno); ++ if (!ret) { ++ ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); ++ __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); ++ } ++ ++ kfree(pno); ++ } + break; + default: + break; +@@ -354,6 +469,51 @@ static int ath11k_wow_set_wakeups(struct + return 0; + } + ++static int ath11k_vif_wow_clean_nlo(struct ath11k_vif *arvif) ++{ ++ int ret = 0; ++ struct ath11k *ar = arvif->ar; ++ ++ switch (arvif->vdev_type) { ++ case WMI_VDEV_TYPE_STA: ++ if (ar->nlo_enabled) { ++ struct wmi_pno_scan_req *pno; ++ ++ pno = kzalloc(sizeof(*pno), GFP_KERNEL); ++ if (!pno) ++ return -ENOMEM; ++ ++ pno->enable = 0; ++ ar->nlo_enabled = false; ++ ret = ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); ++ kfree(pno); ++ } ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static int ath11k_wow_nlo_cleanup(struct ath11k *ar) ++{ ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ ret = ath11k_vif_wow_clean_nlo(arvif); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ + int ath11k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) + { +@@ -439,8 +599,16 @@ int ath11k_wow_op_resume(struct ieee8021 + ath11k_hif_irq_enable(ar->ab); + + ret = ath11k_wow_wakeup(ar->ab); +- if (ret) ++ if (ret) { + ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); ++ goto exit; ++ } ++ ++ ret = ath11k_wow_nlo_cleanup(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret); ++ goto exit; ++ } + + exit: + if (ret) { +@@ -477,6 +645,11 @@ int ath11k_wow_init(struct ath11k *ar) + ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; + } + ++ if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) { ++ ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; ++ ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; ++ } ++ + ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS; + ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; + ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; diff --git a/package/kernel/mac80211/patches/ath11k/0195-ath11k-implement-hardware-data-filter.patch b/package/kernel/mac80211/patches/ath11k/0195-ath11k-implement-hardware-data-filter.patch new file mode 100644 index 00000000000000..1d0ea5641da09e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0195-ath11k-implement-hardware-data-filter.patch @@ -0,0 +1,171 @@ +From c417b247ba042161ddfb34f26a42ec67edc5c378 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 14 Mar 2022 07:18:16 +0200 +Subject: [PATCH] ath11k: implement hardware data filter + +Host needs to set hardware data filter before entering WoW to +let firmware drop needless broadcast/mulitcast frames to avoid +frequent wakeup. Host clears hardware data filter when leaving WoW. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644308006-22784-4-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 33 ++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 15 +++++++ + drivers/net/wireless/ath/ath11k/wow.c | 57 +++++++++++++++++++++++++++ + 3 files changed, 105 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -8165,6 +8165,39 @@ void ath11k_wmi_detach(struct ath11k_bas + ath11k_wmi_free_dbring_caps(ab); + } + ++int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id, ++ u32 filter_bitmap, bool enable) ++{ ++ struct wmi_hw_data_filter_cmd *cmd; ++ struct sk_buff *skb; ++ int len; ++ ++ len = sizeof(*cmd); ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_hw_data_filter_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_HW_DATA_FILTER_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = vdev_id; ++ cmd->enable = enable; ++ ++ /* Set all modes in case of disable */ ++ if (cmd->enable) ++ cmd->hw_filter_bitmap = filter_bitmap; ++ else ++ cmd->hw_filter_bitmap = ((u32)~0U); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi hw data filter enable %d filter_bitmap 0x%x\n", ++ enable, filter_bitmap); ++ ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); ++} ++ + int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + { + struct wmi_wow_host_wakeup_ind *cmd; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5390,6 +5390,19 @@ struct ath11k_wmi_base { + struct ath11k_targ_cap *targ_cap; + }; + ++/* Definition of HW data filtering */ ++enum hw_data_filter_type { ++ WMI_HW_DATA_FILTER_DROP_NON_ARP_BC = BIT(0), ++ WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC = BIT(1), ++}; ++ ++struct wmi_hw_data_filter_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 enable; ++ u32 hw_filter_bitmap; ++} __packed; ++ + /* WOW structures */ + enum wmi_wow_wakeup_event { + WOW_BMISS_EVENT = 0, +@@ -5953,4 +5966,6 @@ int ath11k_wmi_wow_add_pattern(struct at + int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id, + enum wmi_wow_wakeup_event event, + u32 enable); ++int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id, ++ u32 filter_bitmap, bool enable); + #endif +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -514,6 +514,50 @@ static int ath11k_wow_nlo_cleanup(struct + return 0; + } + ++static int ath11k_wow_set_hw_filter(struct ath11k *ar) ++{ ++ struct ath11k_vif *arvif; ++ u32 bitmap; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC | ++ WMI_HW_DATA_FILTER_DROP_NON_ARP_BC; ++ ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, ++ bitmap, ++ true); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ath11k_wow_clear_hw_filter(struct ath11k *ar) ++{ ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false); ++ ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ + int ath11k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) + { +@@ -542,6 +586,13 @@ int ath11k_wow_op_suspend(struct ieee802 + goto cleanup; + } + ++ ret = ath11k_wow_set_hw_filter(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set hw filter: %d\n", ++ ret); ++ goto cleanup; ++ } ++ + ret = ath11k_wow_enable(ar->ab); + if (ret) { + ath11k_warn(ar->ab, "failed to start wow: %d\n", ret); +@@ -610,6 +661,12 @@ int ath11k_wow_op_resume(struct ieee8021 + goto exit; + } + ++ ret = ath11k_wow_clear_hw_filter(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret); ++ goto exit; ++ } ++ + exit: + if (ret) { + switch (ar->state) { diff --git a/package/kernel/mac80211/patches/ath11k/0196-ath11k-purge-rx-pktlog-when-entering-WoW.patch b/package/kernel/mac80211/patches/ath11k/0196-ath11k-purge-rx-pktlog-when-entering-WoW.patch new file mode 100644 index 00000000000000..f76533cace5b84 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0196-ath11k-purge-rx-pktlog-when-entering-WoW.patch @@ -0,0 +1,72 @@ +From 90bf5c8d0f7ecddf96fc1cd9434af4e157b51970 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 14 Mar 2022 07:18:16 +0200 +Subject: [PATCH] ath11k: purge rx pktlog when entering WoW + +This change is to purge rx pktlog when entering WoW and reap +the mon_status buffer to keep it empty. When leaving WoW, host +restarts the reap timer. In WoW state, it's not allowed to feed +into mon_status rings per firmware team's recommendation. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644308006-22784-5-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wow.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -13,6 +13,7 @@ + #include "debug.h" + #include "wmi.h" + #include "wow.h" ++#include "dp_rx.h" + + static const struct wiphy_wowlan_support ath11k_wowlan_support = { + .flags = WIPHY_WOWLAN_DISCONNECT | +@@ -566,6 +567,14 @@ int ath11k_wow_op_suspend(struct ieee802 + + mutex_lock(&ar->conf_mutex); + ++ ret = ath11k_dp_rx_pktlog_stop(ar->ab, true); ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to stop dp rx (and timer) pktlog during wow suspend: %d\n", ++ ret); ++ goto exit; ++ } ++ + ret = ath11k_wow_cleanup(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n", +@@ -599,6 +608,14 @@ int ath11k_wow_op_suspend(struct ieee802 + goto cleanup; + } + ++ ret = ath11k_dp_rx_pktlog_stop(ar->ab, false); ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to stop dp rx pktlog during wow suspend: %d\n", ++ ret); ++ goto cleanup; ++ } ++ + ath11k_ce_stop_shadow_timers(ar->ab); + ath11k_dp_stop_shadow_timers(ar->ab); + +@@ -649,6 +666,12 @@ int ath11k_wow_op_resume(struct ieee8021 + ath11k_hif_ce_irq_enable(ar->ab); + ath11k_hif_irq_enable(ar->ab); + ++ ret = ath11k_dp_rx_pktlog_start(ar->ab); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start rx pktlog from wow: %d\n", ret); ++ return ret; ++ } ++ + ret = ath11k_wow_wakeup(ar->ab); + if (ret) { + ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); diff --git a/package/kernel/mac80211/patches/ath11k/0197-ath11k-support-ARP-and-NS-offload.patch b/package/kernel/mac80211/patches/ath11k/0197-ath11k-support-ARP-and-NS-offload.patch new file mode 100644 index 00000000000000..f5fe9a4dd05e1a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0197-ath11k-support-ARP-and-NS-offload.patch @@ -0,0 +1,519 @@ +From c3c36bfe998b3ad14aa87a57037a05d861889ac8 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 14 Mar 2022 07:18:16 +0200 +Subject: [PATCH] ath11k: support ARP and NS offload + +Support ARP and NS offload in WoW state. + +Tested this way: put machine A with QCA6390 to WoW state, +ping/ping6 machine A from another machine B, check sniffer to see +any ARP response and Neighbour advertisement from machine A. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644308006-22784-6-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 19 +++ + drivers/net/wireless/ath/ath11k/mac.c | 118 +++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 155 +++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 47 ++++++++ + drivers/net/wireless/ath/ath11k/wow.c | 52 +++++++++ + 5 files changed, 391 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -213,6 +213,23 @@ enum ath11k_monitor_flags { + ATH11K_FLAG_MONITOR_VDEV_CREATED, + }; + ++#define ATH11K_IPV6_UC_TYPE 0 ++#define ATH11K_IPV6_AC_TYPE 1 ++ ++#define ATH11K_IPV6_MAX_COUNT 16 ++#define ATH11K_IPV4_MAX_COUNT 2 ++ ++struct ath11k_arp_ns_offload { ++ u8 ipv4_addr[ATH11K_IPV4_MAX_COUNT][4]; ++ u32 ipv4_count; ++ u32 ipv6_count; ++ u8 ipv6_addr[ATH11K_IPV6_MAX_COUNT][16]; ++ u8 self_ipv6_addr[ATH11K_IPV6_MAX_COUNT][16]; ++ u8 ipv6_type[ATH11K_IPV6_MAX_COUNT]; ++ bool ipv6_valid[ATH11K_IPV6_MAX_COUNT]; ++ u8 mac_addr[ETH_ALEN]; ++}; ++ + struct ath11k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; +@@ -264,6 +281,8 @@ struct ath11k_vif { + bool bcca_zero_sent; + bool do_not_send_tmpl; + struct ieee80211_chanctx_conf chanctx; ++ struct ath11k_arp_ns_offload arp_ns_offload; ++ + #ifdef CPTCFG_ATH11K_DEBUGFS + struct dentry *debugfs_twt; + #endif /* CPTCFG_ATH11K_DEBUGFS */ +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6,6 +6,11 @@ + + #include + #include ++#include ++#include ++#include ++#include ++ + #include "mac.h" + #include "core.h" + #include "debug.h" +@@ -3095,6 +3100,7 @@ static void ath11k_mac_op_bss_info_chang + int ret = 0; + u8 rateidx; + u32 rate; ++ u32 ipv4_cnt; + + mutex_lock(&ar->conf_mutex); + +@@ -3387,6 +3393,18 @@ static void ath11k_mac_op_bss_info_chang + changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP) + ath11k_mac_fils_discovery(arvif, info); + ++ if (changed & BSS_CHANGED_ARP_FILTER) { ++ ipv4_cnt = min(info->arp_addr_cnt, ATH11K_IPV4_MAX_COUNT); ++ memcpy(arvif->arp_ns_offload.ipv4_addr, info->arp_addr_list, ++ ipv4_cnt * sizeof(u32)); ++ memcpy(arvif->arp_ns_offload.mac_addr, vif->addr, ETH_ALEN); ++ arvif->arp_ns_offload.ipv4_count = ipv4_cnt; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac arp_addr_cnt %d vif->addr %pM, offload_addr %pI4\n", ++ info->arp_addr_cnt, ++ vif->addr, arvif->arp_ns_offload.ipv4_addr); ++ } ++ + mutex_unlock(&ar->conf_mutex); + } + +@@ -8087,6 +8105,101 @@ static void ath11k_mac_op_sta_statistics + } + } + ++static void ath11k_generate_ns_mc_addr(struct ath11k *ar, ++ struct ath11k_arp_ns_offload *offload) ++{ ++ int i; ++ ++ for (i = 0; i < offload->ipv6_count; i++) { ++ offload->self_ipv6_addr[i][0] = 0xff; ++ offload->self_ipv6_addr[i][1] = 0x02; ++ offload->self_ipv6_addr[i][11] = 0x01; ++ offload->self_ipv6_addr[i][12] = 0xff; ++ offload->self_ipv6_addr[i][13] = ++ offload->ipv6_addr[i][13]; ++ offload->self_ipv6_addr[i][14] = ++ offload->ipv6_addr[i][14]; ++ offload->self_ipv6_addr[i][15] = ++ offload->ipv6_addr[i][15]; ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "NS solicited addr %pI6\n", ++ offload->self_ipv6_addr[i]); ++ } ++} ++ ++static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct inet6_dev *idev) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_arp_ns_offload *offload; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ struct inet6_ifaddr *ifa6; ++ struct ifacaddr6 *ifaca6; ++ struct list_head *p; ++ u32 count, scope; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac op ipv6 changed\n"); ++ ++ offload = &arvif->arp_ns_offload; ++ count = 0; ++ ++ read_lock_bh(&idev->lock); ++ ++ memset(offload->ipv6_addr, 0, sizeof(offload->ipv6_addr)); ++ memset(offload->self_ipv6_addr, 0, sizeof(offload->self_ipv6_addr)); ++ memcpy(offload->mac_addr, vif->addr, ETH_ALEN); ++ ++ /* get unicast address */ ++ list_for_each(p, &idev->addr_list) { ++ if (count >= ATH11K_IPV6_MAX_COUNT) ++ goto generate; ++ ++ ifa6 = list_entry(p, struct inet6_ifaddr, if_list); ++ if (ifa6->flags & IFA_F_DADFAILED) ++ continue; ++ scope = ipv6_addr_src_scope(&ifa6->addr); ++ if (scope == IPV6_ADDR_SCOPE_LINKLOCAL || ++ scope == IPV6_ADDR_SCOPE_GLOBAL) { ++ memcpy(offload->ipv6_addr[count], &ifa6->addr.s6_addr, ++ sizeof(ifa6->addr.s6_addr)); ++ offload->ipv6_type[count] = ATH11K_IPV6_UC_TYPE; ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac count %d ipv6 uc %pI6 scope %d\n", ++ count, offload->ipv6_addr[count], ++ scope); ++ count++; ++ } else { ++ ath11k_warn(ar->ab, "Unsupported ipv6 scope: %d\n", scope); ++ } ++ } ++ ++ /* get anycast address */ ++ for (ifaca6 = idev->ac_list; ifaca6; ifaca6 = ifaca6->aca_next) { ++ if (count >= ATH11K_IPV6_MAX_COUNT) ++ goto generate; ++ ++ scope = ipv6_addr_src_scope(&ifaca6->aca_addr); ++ if (scope == IPV6_ADDR_SCOPE_LINKLOCAL || ++ scope == IPV6_ADDR_SCOPE_GLOBAL) { ++ memcpy(offload->ipv6_addr[count], &ifaca6->aca_addr, ++ sizeof(ifaca6->aca_addr)); ++ offload->ipv6_type[count] = ATH11K_IPV6_AC_TYPE; ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac count %d ipv6 ac %pI6 scope %d\n", ++ count, offload->ipv6_addr[count], ++ scope); ++ count++; ++ } else { ++ ath11k_warn(ar->ab, "Unsupported ipv scope: %d\n", scope); ++ } ++ } ++ ++generate: ++ offload->ipv6_count = count; ++ read_unlock_bh(&idev->lock); ++ ++ /* generate ns multicast address */ ++ ath11k_generate_ns_mc_addr(ar, offload); ++} ++ + static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .start = ath11k_mac_op_start, +@@ -8132,6 +8245,11 @@ static const struct ieee80211_ops ath11k + #ifdef CPTCFG_ATH11K_DEBUGFS + .sta_add_debugfs = ath11k_debugfs_sta_op_add, + #endif ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ .ipv6_addr_change = ath11k_mac_op_ipv6_changed, ++#endif ++ + }; + + static void ath11k_mac_update_ch_list(struct ath11k *ar, +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -8580,3 +8580,158 @@ int ath11k_wmi_wow_config_pno(struct ath + return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); + } + ++static void ath11k_wmi_fill_ns_offload(struct ath11k *ar, ++ struct ath11k_arp_ns_offload *offload, ++ u8 **ptr, ++ bool enable, ++ bool ext) ++{ ++ struct wmi_ns_offload_tuple *ns; ++ struct wmi_tlv *tlv; ++ u8 *buf_ptr = *ptr; ++ u32 ns_cnt, ns_ext_tuples; ++ int i, max_offloads; ++ ++ ns_cnt = offload->ipv6_count; ++ ++ tlv = (struct wmi_tlv *)buf_ptr; ++ ++ if (ext) { ++ ns_ext_tuples = offload->ipv6_count - WMI_MAX_NS_OFFLOADS; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, ns_ext_tuples * sizeof(*ns)); ++ i = WMI_MAX_NS_OFFLOADS; ++ max_offloads = offload->ipv6_count; ++ } else { ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, WMI_MAX_NS_OFFLOADS * sizeof(*ns)); ++ i = 0; ++ max_offloads = WMI_MAX_NS_OFFLOADS; ++ } ++ ++ buf_ptr += sizeof(*tlv); ++ ++ for (; i < max_offloads; i++) { ++ ns = (struct wmi_ns_offload_tuple *)buf_ptr; ++ ns->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_NS_OFFLOAD_TUPLE) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*ns) - TLV_HDR_SIZE); ++ ++ if (enable) { ++ if (i < ns_cnt) ++ ns->flags |= WMI_NSOL_FLAGS_VALID; ++ ++ memcpy(ns->target_ipaddr[0], offload->ipv6_addr[i], 16); ++ memcpy(ns->solicitation_ipaddr, offload->self_ipv6_addr[i], 16); ++ ath11k_ce_byte_swap(ns->target_ipaddr[0], 16); ++ ath11k_ce_byte_swap(ns->solicitation_ipaddr, 16); ++ ++ if (offload->ipv6_type[i]) ++ ns->flags |= WMI_NSOL_FLAGS_IS_IPV6_ANYCAST; ++ ++ memcpy(ns->target_mac.addr, offload->mac_addr, ETH_ALEN); ++ ath11k_ce_byte_swap(ns->target_mac.addr, 8); ++ ++ if (ns->target_mac.word0 != 0 || ++ ns->target_mac.word1 != 0) { ++ ns->flags |= WMI_NSOL_FLAGS_MAC_VALID; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi index %d ns_solicited %pI6 target %pI6", ++ i, ns->solicitation_ipaddr, ++ ns->target_ipaddr[0]); ++ } ++ ++ buf_ptr += sizeof(*ns); ++ } ++ ++ *ptr = buf_ptr; ++} ++ ++static void ath11k_wmi_fill_arp_offload(struct ath11k *ar, ++ struct ath11k_arp_ns_offload *offload, ++ u8 **ptr, ++ bool enable) ++{ ++ struct wmi_arp_offload_tuple *arp; ++ struct wmi_tlv *tlv; ++ u8 *buf_ptr = *ptr; ++ int i; ++ ++ /* fill arp tuple */ ++ tlv = (struct wmi_tlv *)buf_ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | ++ FIELD_PREP(WMI_TLV_LEN, WMI_MAX_ARP_OFFLOADS * sizeof(*arp)); ++ buf_ptr += sizeof(*tlv); ++ ++ for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) { ++ arp = (struct wmi_arp_offload_tuple *)buf_ptr; ++ arp->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARP_OFFLOAD_TUPLE) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*arp) - TLV_HDR_SIZE); ++ ++ if (enable && i < offload->ipv4_count) { ++ /* Copy the target ip addr and flags */ ++ arp->flags = WMI_ARPOL_FLAGS_VALID; ++ memcpy(arp->target_ipaddr, offload->ipv4_addr[i], 4); ++ ath11k_ce_byte_swap(arp->target_ipaddr, 4); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi arp offload address %pI4", ++ arp->target_ipaddr); ++ } ++ ++ buf_ptr += sizeof(*arp); ++ } ++ ++ *ptr = buf_ptr; ++} ++ ++int ath11k_wmi_arp_ns_offload(struct ath11k *ar, ++ struct ath11k_vif *arvif, bool enable) ++{ ++ struct ath11k_arp_ns_offload *offload; ++ struct wmi_set_arp_ns_offload_cmd *cmd; ++ struct wmi_tlv *tlv; ++ struct sk_buff *skb; ++ u8 *buf_ptr; ++ size_t len; ++ u8 ns_cnt, ns_ext_tuples = 0; ++ ++ offload = &arvif->arp_ns_offload; ++ ns_cnt = offload->ipv6_count; ++ ++ len = sizeof(*cmd) + ++ sizeof(*tlv) + ++ WMI_MAX_NS_OFFLOADS * sizeof(struct wmi_ns_offload_tuple) + ++ sizeof(*tlv) + ++ WMI_MAX_ARP_OFFLOADS * sizeof(struct wmi_arp_offload_tuple); ++ ++ if (ns_cnt > WMI_MAX_NS_OFFLOADS) { ++ ns_ext_tuples = ns_cnt - WMI_MAX_NS_OFFLOADS; ++ len += sizeof(*tlv) + ++ ns_ext_tuples * sizeof(struct wmi_ns_offload_tuple); ++ } ++ ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ buf_ptr = skb->data; ++ cmd = (struct wmi_set_arp_ns_offload_cmd *)buf_ptr; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_SET_ARP_NS_OFFLOAD_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->flags = 0; ++ cmd->vdev_id = arvif->vdev_id; ++ cmd->num_ns_ext_tuples = ns_ext_tuples; ++ ++ buf_ptr += sizeof(*cmd); ++ ++ ath11k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 0); ++ ath11k_wmi_fill_arp_offload(ar, offload, &buf_ptr, enable); ++ ++ if (ns_ext_tuples) ++ ath11k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1); ++ ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); ++} +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -13,6 +13,7 @@ struct ath11k_base; + struct ath11k; + struct ath11k_fw_stats; + struct ath11k_fw_dbglog; ++struct ath11k_vif; + + #define PSOC_HOST_MAX_NUM_SS (8) + +@@ -5796,6 +5797,49 @@ struct wmi_wow_nlo_config_cmd { + */ + } __packed; + ++#define WMI_MAX_NS_OFFLOADS 2 ++#define WMI_MAX_ARP_OFFLOADS 2 ++ ++#define WMI_ARPOL_FLAGS_VALID BIT(0) ++#define WMI_ARPOL_FLAGS_MAC_VALID BIT(1) ++#define WMI_ARPOL_FLAGS_REMOTE_IP_VALID BIT(2) ++ ++struct wmi_arp_offload_tuple { ++ u32 tlv_header; ++ u32 flags; ++ u8 target_ipaddr[4]; ++ u8 remote_ipaddr[4]; ++ struct wmi_mac_addr target_mac; ++} __packed; ++ ++#define WMI_NSOL_FLAGS_VALID BIT(0) ++#define WMI_NSOL_FLAGS_MAC_VALID BIT(1) ++#define WMI_NSOL_FLAGS_REMOTE_IP_VALID BIT(2) ++#define WMI_NSOL_FLAGS_IS_IPV6_ANYCAST BIT(3) ++ ++#define WMI_NSOL_MAX_TARGET_IPS 2 ++ ++struct wmi_ns_offload_tuple { ++ u32 tlv_header; ++ u32 flags; ++ u8 target_ipaddr[WMI_NSOL_MAX_TARGET_IPS][16]; ++ u8 solicitation_ipaddr[16]; ++ u8 remote_ipaddr[16]; ++ struct wmi_mac_addr target_mac; ++} __packed; ++ ++struct wmi_set_arp_ns_offload_cmd { ++ u32 tlv_header; ++ u32 flags; ++ u32 vdev_id; ++ u32 num_ns_ext_tuples; ++ /* The TLVs follow: ++ * wmi_ns_offload_tuple ns_tuples[WMI_MAX_NS_OFFLOADS]; ++ * wmi_arp_offload_tuple arp_tuples[WMI_MAX_ARP_OFFLOADS]; ++ * wmi_ns_offload_tuple ns_ext_tuples[num_ns_ext_tuples]; ++ */ ++} __packed; ++ + int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); + struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); +@@ -5968,4 +6012,7 @@ int ath11k_wmi_wow_add_wakeup_event(stru + u32 enable); + int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id, + u32 filter_bitmap, bool enable); ++int ath11k_wmi_arp_ns_offload(struct ath11k *ar, ++ struct ath11k_vif *arvif, bool enable); ++ + #endif +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -559,6 +559,43 @@ static int ath11k_wow_clear_hw_filter(st + return 0; + } + ++static int ath11k_wow_arp_ns_offload(struct ath11k *ar, bool enable) ++{ ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ if (arvif->vdev_type != WMI_VDEV_TYPE_STA) ++ continue; ++ ++ ret = ath11k_wmi_arp_ns_offload(ar, arvif, enable); ++ ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n", ++ arvif->vdev_id, enable, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable) ++{ ++ int ret; ++ ++ ret = ath11k_wow_arp_ns_offload(ar, enable); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to offload ARP and NS %d %d\n", ++ enable, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ + int ath11k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) + { +@@ -589,6 +626,14 @@ int ath11k_wow_op_suspend(struct ieee802 + goto cleanup; + } + ++ ret = ath11k_wow_protocol_offload(ar, true); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set wow protocol offload events: %d\n", ++ ret); ++ goto cleanup; ++ } ++ ++ ath11k_mac_drain_tx(ar); + ret = ath11k_mac_wait_tx_complete(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret); +@@ -690,6 +735,13 @@ int ath11k_wow_op_resume(struct ieee8021 + goto exit; + } + ++ ret = ath11k_wow_protocol_offload(ar, false); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n", ++ ret); ++ goto exit; ++ } ++ + exit: + if (ret) { + switch (ar->state) { diff --git a/package/kernel/mac80211/patches/ath11k/0198-ath11k-support-GTK-rekey-offload.patch b/package/kernel/mac80211/patches/ath11k/0198-ath11k-support-GTK-rekey-offload.patch new file mode 100644 index 00000000000000..7cc33abaf6d0e9 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0198-ath11k-support-GTK-rekey-offload.patch @@ -0,0 +1,384 @@ +From a16d9b50cfbaf112401b8e5ccfa852709f498cd4 Mon Sep 17 00:00:00 2001 +From: Carl Huang +Date: Mon, 14 Mar 2022 07:18:16 +0200 +Subject: [PATCH] ath11k: support GTK rekey offload + +Host sets GTK related info to firmware before WoW is enabled, and +gets rekey replay_count and then disables GTK rekey when WoW quits. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Carl Huang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644308006-22784-7-git-send-email-quic_cjhuang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 8 ++ + drivers/net/wireless/ath/ath11k/mac.c | 37 ++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 119 +++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 49 ++++++++++ + drivers/net/wireless/ath/ath11k/wow.c | 46 +++++++++- + 5 files changed, 258 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -230,6 +230,13 @@ struct ath11k_arp_ns_offload { + u8 mac_addr[ETH_ALEN]; + }; + ++struct ath11k_rekey_data { ++ u8 kck[NL80211_KCK_LEN]; ++ u8 kek[NL80211_KCK_LEN]; ++ u64 replay_ctr; ++ bool enable_offload; ++}; ++ + struct ath11k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; +@@ -282,6 +289,7 @@ struct ath11k_vif { + bool do_not_send_tmpl; + struct ieee80211_chanctx_conf chanctx; + struct ath11k_arp_ns_offload arp_ns_offload; ++ struct ath11k_rekey_data rekey_data; + + #ifdef CPTCFG_ATH11K_DEBUGFS + struct dentry *debugfs_twt; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -2757,6 +2757,7 @@ static void ath11k_bss_assoc(struct ieee + } + + arvif->is_up = true; ++ arvif->rekey_data.enable_offload = false; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac vdev %d up (associated) bssid %pM aid %d\n", +@@ -2814,6 +2815,8 @@ static void ath11k_bss_disassoc(struct i + + arvif->is_up = false; + ++ memset(&arvif->rekey_data, 0, sizeof(arvif->rekey_data)); ++ + cancel_delayed_work_sync(&arvif->connection_loss_work); + } + +@@ -8200,6 +8203,39 @@ generate: + ath11k_generate_ns_mc_addr(ar, offload); + } + ++static void ath11k_mac_op_set_rekey_data(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct cfg80211_gtk_rekey_data *data) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ struct ath11k_rekey_data *rekey_data = &arvif->rekey_data; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac set rekey data vdev %d\n", ++ arvif->vdev_id); ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ memcpy(rekey_data->kck, data->kck, NL80211_KCK_LEN); ++ memcpy(rekey_data->kek, data->kek, NL80211_KEK_LEN); ++ ++ /* The supplicant works on big-endian, the firmware expects it on ++ * little endian. ++ */ ++ rekey_data->replay_ctr = get_unaligned_be64(data->replay_ctr); ++ ++ arvif->rekey_data.enable_offload = true; ++ ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_MAC, "kck", NULL, ++ rekey_data->kck, NL80211_KCK_LEN); ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_MAC, "kek", NULL, ++ rekey_data->kck, NL80211_KEK_LEN); ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_MAC, "replay ctr", NULL, ++ &rekey_data->replay_ctr, sizeof(rekey_data->replay_ctr)); ++ ++ mutex_unlock(&ar->conf_mutex); ++} ++ + static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .start = ath11k_mac_op_start, +@@ -8214,6 +8250,7 @@ static const struct ieee80211_ops ath11k + .hw_scan = ath11k_mac_op_hw_scan, + .cancel_hw_scan = ath11k_mac_op_cancel_hw_scan, + .set_key = ath11k_mac_op_set_key, ++ .set_rekey_data = ath11k_mac_op_set_rekey_data, + .sta_state = ath11k_mac_op_sta_state, + .sta_set_4addr = ath11k_mac_op_sta_set_4addr, + .sta_set_txpwr = ath11k_mac_op_sta_set_txpwr, +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7765,6 +7765,56 @@ exit: + kfree(tb); + } + ++static void ath11k_wmi_gtk_offload_status_event(struct ath11k_base *ab, ++ struct sk_buff *skb) ++{ ++ const void **tb; ++ const struct wmi_gtk_offload_status_event *ev; ++ struct ath11k_vif *arvif; ++ __be64 replay_ctr_be; ++ u64 replay_ctr; ++ int ret; ++ ++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); ++ if (IS_ERR(tb)) { ++ ret = PTR_ERR(tb); ++ ath11k_warn(ab, "failed to parse tlv: %d\n", ret); ++ return; ++ } ++ ++ ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT]; ++ if (!ev) { ++ ath11k_warn(ab, "failed to fetch gtk offload status ev"); ++ kfree(tb); ++ return; ++ } ++ ++ arvif = ath11k_mac_get_arvif_by_vdev_id(ab, ev->vdev_id); ++ if (!arvif) { ++ ath11k_warn(ab, "failed to get arvif for vdev_id:%d\n", ++ ev->vdev_id); ++ kfree(tb); ++ return; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi gtk offload event refresh_cnt %d\n", ++ ev->refresh_cnt); ++ ath11k_dbg_dump(ab, ATH11K_DBG_WMI, "replay_cnt", ++ NULL, ev->replay_ctr.counter, GTK_REPLAY_COUNTER_BYTES); ++ ++ replay_ctr = ev->replay_ctr.word1; ++ replay_ctr = (replay_ctr << 32) | ev->replay_ctr.word0; ++ arvif->rekey_data.replay_ctr = replay_ctr; ++ ++ /* supplicant expects big-endian replay counter */ ++ replay_ctr_be = cpu_to_be64(replay_ctr); ++ ++ ieee80211_gtk_rekey_notify(arvif->vif, arvif->bssid, ++ (void *)&replay_ctr_be, GFP_KERNEL); ++ ++ kfree(tb); ++} ++ + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) + { + struct wmi_cmd_hdr *cmd_hdr; +@@ -7896,6 +7946,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_DIAG_EVENTID: + ath11k_wmi_diag_event(ab, skb); + break; ++ case WMI_GTK_OFFLOAD_STATUS_EVENTID: ++ ath11k_wmi_gtk_offload_status_event(ab, skb); ++ break; + /* TODO: Add remaining events */ + default: + ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id); +@@ -8735,3 +8788,69 @@ int ath11k_wmi_arp_ns_offload(struct ath + + return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); + } ++ ++int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar, ++ struct ath11k_vif *arvif, bool enable) ++{ ++ struct wmi_gtk_rekey_offload_cmd *cmd; ++ struct ath11k_rekey_data *rekey_data = &arvif->rekey_data; ++ int len; ++ struct sk_buff *skb; ++ __le64 replay_ctr; ++ ++ len = sizeof(*cmd); ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_GTK_OFFLOAD_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = arvif->vdev_id; ++ ++ if (enable) { ++ cmd->flags = GTK_OFFLOAD_ENABLE_OPCODE; ++ ++ /* the length in rekey_data and cmd is equal */ ++ memcpy(cmd->kck, rekey_data->kck, sizeof(cmd->kck)); ++ ath11k_ce_byte_swap(cmd->kck, GTK_OFFLOAD_KEK_BYTES); ++ memcpy(cmd->kek, rekey_data->kek, sizeof(cmd->kek)); ++ ath11k_ce_byte_swap(cmd->kek, GTK_OFFLOAD_KEK_BYTES); ++ ++ replay_ctr = cpu_to_le64(rekey_data->replay_ctr); ++ memcpy(cmd->replay_ctr, &replay_ctr, ++ sizeof(replay_ctr)); ++ ath11k_ce_byte_swap(cmd->replay_ctr, GTK_REPLAY_COUNTER_BYTES); ++ } else { ++ cmd->flags = GTK_OFFLOAD_DISABLE_OPCODE; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "offload gtk rekey vdev: %d %d\n", ++ arvif->vdev_id, enable); ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); ++} ++ ++int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar, ++ struct ath11k_vif *arvif) ++{ ++ struct wmi_gtk_rekey_offload_cmd *cmd; ++ int len; ++ struct sk_buff *skb; ++ ++ len = sizeof(*cmd); ++ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_GTK_OFFLOAD_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->vdev_id = arvif->vdev_id; ++ cmd->flags = GTK_OFFLOAD_REQUEST_STATUS_OPCODE; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "get gtk rekey vdev_id: %d\n", ++ arvif->vdev_id); ++ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); ++} +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5840,6 +5840,51 @@ struct wmi_set_arp_ns_offload_cmd { + */ + } __packed; + ++#define GTK_OFFLOAD_OPCODE_MASK 0xFF000000 ++#define GTK_OFFLOAD_ENABLE_OPCODE 0x01000000 ++#define GTK_OFFLOAD_DISABLE_OPCODE 0x02000000 ++#define GTK_OFFLOAD_REQUEST_STATUS_OPCODE 0x04000000 ++ ++#define GTK_OFFLOAD_KEK_BYTES 16 ++#define GTK_OFFLOAD_KCK_BYTES 16 ++#define GTK_REPLAY_COUNTER_BYTES 8 ++#define WMI_MAX_KEY_LEN 32 ++#define IGTK_PN_SIZE 6 ++ ++struct wmi_replayc_cnt { ++ union { ++ u8 counter[GTK_REPLAY_COUNTER_BYTES]; ++ struct { ++ u32 word0; ++ u32 word1; ++ } __packed; ++ } __packed; ++} __packed; ++ ++struct wmi_gtk_offload_status_event { ++ u32 vdev_id; ++ u32 flags; ++ u32 refresh_cnt; ++ struct wmi_replayc_cnt replay_ctr; ++ u8 igtk_key_index; ++ u8 igtk_key_length; ++ u8 igtk_key_rsc[IGTK_PN_SIZE]; ++ u8 igtk_key[WMI_MAX_KEY_LEN]; ++ u8 gtk_key_index; ++ u8 gtk_key_length; ++ u8 gtk_key_rsc[GTK_REPLAY_COUNTER_BYTES]; ++ u8 gtk_key[WMI_MAX_KEY_LEN]; ++} __packed; ++ ++struct wmi_gtk_rekey_offload_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 flags; ++ u8 kek[GTK_OFFLOAD_KEK_BYTES]; ++ u8 kck[GTK_OFFLOAD_KCK_BYTES]; ++ u8 replay_ctr[GTK_REPLAY_COUNTER_BYTES]; ++} __packed; ++ + int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); + struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); +@@ -6014,5 +6059,9 @@ int ath11k_wmi_hw_data_filter_cmd(struct + u32 filter_bitmap, bool enable); + int ath11k_wmi_arp_ns_offload(struct ath11k *ar, + struct ath11k_vif *arvif, bool enable); ++int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar, ++ struct ath11k_vif *arvif, bool enable); ++int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar, ++ struct ath11k_vif *arvif); + + #endif +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -17,7 +17,9 @@ + + static const struct wiphy_wowlan_support ath11k_wowlan_support = { + .flags = WIPHY_WOWLAN_DISCONNECT | +- WIPHY_WOWLAN_MAGIC_PKT, ++ WIPHY_WOWLAN_MAGIC_PKT | ++ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | ++ WIPHY_WOWLAN_GTK_REKEY_FAILURE, + .pattern_min_len = WOW_MIN_PATTERN_SIZE, + .pattern_max_len = WOW_MAX_PATTERN_SIZE, + .max_pkt_offset = WOW_MAX_PKT_OFFSET, +@@ -582,6 +584,41 @@ static int ath11k_wow_arp_ns_offload(str + return 0; + } + ++static int ath11k_gtk_rekey_offload(struct ath11k *ar, bool enable) ++{ ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ if (arvif->vdev_type != WMI_VDEV_TYPE_STA || ++ !arvif->is_up || ++ !arvif->rekey_data.enable_offload) ++ continue; ++ ++ /* get rekey info before disable rekey offload */ ++ if (!enable) { ++ ret = ath11k_wmi_gtk_rekey_getinfo(ar, arvif); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } ++ ++ ret = ath11k_wmi_gtk_rekey_offload(ar, arvif, enable); ++ ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n", ++ arvif->vdev_id, enable, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ + static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable) + { + int ret; +@@ -592,6 +629,13 @@ static int ath11k_wow_protocol_offload(s + enable, ret); + return ret; + } ++ ++ ret = ath11k_gtk_rekey_offload(ar, enable); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to offload gtk rekey %d %d\n", ++ enable, ret); ++ return ret; ++ } + + return 0; + } diff --git a/package/kernel/mac80211/patches/ath11k/0199-ath11k-Refactor-the-peer-delete.patch b/package/kernel/mac80211/patches/ath11k/0199-ath11k-Refactor-the-peer-delete.patch new file mode 100644 index 00000000000000..c86839b0965173 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0199-ath11k-Refactor-the-peer-delete.patch @@ -0,0 +1,103 @@ +From 997dc60f0855b39aec0400511b37d65781da9255 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Mon, 28 Feb 2022 10:24:39 +0530 +Subject: [PATCH] ath11k: Refactor the peer delete + +Introduce new helper function for peer delete to reuse this logic +in all peer cleanup procedures. Found this in code review. +Also this change is applicable for all the platform. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01067-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1646024079-26391-1-git-send-email-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 16 +++---------- + drivers/net/wireless/ath/ath11k/peer.c | 31 +++++++++++++------------- + 2 files changed, 19 insertions(+), 28 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6387,22 +6387,12 @@ static int ath11k_mac_op_add_interface(s + + err_peer_del: + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { +- reinit_completion(&ar->peer_delete_done); +- +- fbret = ath11k_wmi_send_peer_delete_cmd(ar, vif->addr, +- arvif->vdev_id); ++ fbret = ath11k_peer_delete(ar, arvif->vdev_id, vif->addr); + if (fbret) { +- ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", +- arvif->vdev_id, vif->addr); ++ ath11k_warn(ar->ab, "fallback fail to delete peer addr %pM vdev_id %d ret %d\n", ++ vif->addr, arvif->vdev_id, fbret); + goto err; + } +- +- fbret = ath11k_wait_for_peer_delete_done(ar, arvif->vdev_id, +- vif->addr); +- if (fbret) +- goto err; +- +- ar->num_peers--; + } + + err_vdev_del: +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -217,7 +217,7 @@ int ath11k_wait_for_peer_delete_done(str + return 0; + } + +-int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) ++static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr) + { + int ret; + +@@ -237,6 +237,19 @@ int ath11k_peer_delete(struct ath11k *ar + if (ret) + return ret; + ++ return 0; ++} ++ ++int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) ++{ ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ ret = __ath11k_peer_delete(ar, vdev_id, addr); ++ if (ret) ++ return ret; ++ + ar->num_peers--; + + return 0; +@@ -323,22 +336,10 @@ int ath11k_peer_create(struct ath11k *ar + return 0; + + cleanup: +- reinit_completion(&ar->peer_delete_done); +- +- fbret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, +- param->vdev_id); +- if (fbret) { +- ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", +- param->vdev_id, param->peer_addr); +- goto exit; +- } +- +- fbret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, +- param->peer_addr); ++ fbret = __ath11k_peer_delete(ar, param->vdev_id, param->peer_addr); + if (fbret) +- ath11k_warn(ar->ab, "failed wait for peer %pM delete done id %d fallback ret %d\n", ++ ath11k_warn(ar->ab, "failed peer %pM delete vdev_id %d fallback ret %d\n", + param->peer_addr, param->vdev_id, fbret); + +-exit: + return ret; + } diff --git a/package/kernel/mac80211/patches/ath11k/0200-ath11k-change-fw-build-id-format-in-driver-init-log.patch b/package/kernel/mac80211/patches/ath11k/0200-ath11k-change-fw-build-id-format-in-driver-init-log.patch new file mode 100644 index 00000000000000..85c1380490774e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0200-ath11k-change-fw-build-id-format-in-driver-init-log.patch @@ -0,0 +1,65 @@ +From 72a9bff386545d3f8e9c432cb8e036272ad4e1fa Mon Sep 17 00:00:00 2001 +From: Hari Chandrakanthan +Date: Wed, 9 Mar 2022 16:54:25 +0530 +Subject: [PATCH] ath11k: change fw build id format in driver init log + +Currently fw build id is printed during init as follows. + +fw_version 0x250684a5 fw_build_timestamp 2021-07-13 10:57 +fw_build_id QC_IMAGE_VERSION_STRING=WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +The string "QC_IMAGE_VERSION_STRING=" is removed from the log +to improve readability. + +With this patch the fw build id is printed during init as follows. +fw_version 0x250684a5 fw_build_timestamp 2021-07-13 10:57 +fw_build_id WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Hari Chandrakanthan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1646825065-7736-1-git-send-email-quic_haric@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -16,6 +16,8 @@ + #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 + #define HOST_CSTATE_BIT 0x04 + ++#define FW_BUILD_ID_MASK "QC_IMAGE_VERSION_STRING=" ++ + bool ath11k_cold_boot_cal = 1; + EXPORT_SYMBOL(ath11k_cold_boot_cal); + module_param_named(cold_boot_cal, ath11k_cold_boot_cal, bool, 0644); +@@ -2008,6 +2010,8 @@ static int ath11k_qmi_request_target_cap + struct qmi_txn txn; + int ret = 0; + int r; ++ char *fw_build_id; ++ int fw_build_id_mask_len; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); +@@ -2073,6 +2077,11 @@ static int ath11k_qmi_request_target_cap + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi cal data supported from eeprom\n"); + } + ++ fw_build_id = ab->qmi.target.fw_build_id; ++ fw_build_id_mask_len = strlen(FW_BUILD_ID_MASK); ++ if (!strncmp(fw_build_id, FW_BUILD_ID_MASK, fw_build_id_mask_len)) ++ fw_build_id = fw_build_id + fw_build_id_mask_len; ++ + ath11k_info(ab, "chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x\n", + ab->qmi.target.chip_id, ab->qmi.target.chip_family, + ab->qmi.target.board_id, ab->qmi.target.soc_id); +@@ -2080,7 +2089,7 @@ static int ath11k_qmi_request_target_cap + ath11k_info(ab, "fw_version 0x%x fw_build_timestamp %s fw_build_id %s", + ab->qmi.target.fw_version, + ab->qmi.target.fw_build_timestamp, +- ab->qmi.target.fw_build_id); ++ fw_build_id); + + r = ath11k_core_check_dt(ab); + if (r) diff --git a/package/kernel/mac80211/patches/ath11k/0201-ath11k-acquire-ab-base_lock-in-unassign-when-finding.patch b/package/kernel/mac80211/patches/ath11k/0201-ath11k-acquire-ab-base_lock-in-unassign-when-finding.patch new file mode 100644 index 00000000000000..98d06d6d6fc941 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0201-ath11k-acquire-ab-base_lock-in-unassign-when-finding.patch @@ -0,0 +1,54 @@ +From 2db80f93869d491be57cbc2b36f30d0d3a0e5bde Mon Sep 17 00:00:00 2001 +From: Niels Dossche +Date: Mon, 21 Mar 2022 12:58:23 +0200 +Subject: [PATCH] ath11k: acquire ab->base_lock in unassign when finding the + peer by addr + +ath11k_peer_find_by_addr states via lockdep that ab->base_lock must be +held when calling that function in order to protect the list. All +callers except ath11k_mac_op_unassign_vif_chanctx have that lock +acquired when calling ath11k_peer_find_by_addr. That lock is also not +transitively held by a path towards ath11k_mac_op_unassign_vif_chanctx. +The solution is to acquire the lock when calling +ath11k_peer_find_by_addr inside ath11k_mac_op_unassign_vif_chanctx. + +I am currently working on a static analyser to detect missing locks and +this was a reported case. I manually verified the report by looking at +the code, but I do not have real hardware so this is compile tested +only. + +Fixes: 701e48a43e15 ("ath11k: add packet log support for QCA6390") +Signed-off-by: Niels Dossche +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220314215253.92658-1-dossche.niels@gmail.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7133,6 +7133,7 @@ ath11k_mac_op_unassign_vif_chanctx(struc + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; ++ struct ath11k_peer *peer; + int ret; + + mutex_lock(&ar->conf_mutex); +@@ -7144,9 +7145,13 @@ ath11k_mac_op_unassign_vif_chanctx(struc + WARN_ON(!arvif->is_started); + + if (ab->hw_params.vdev_start_delay && +- arvif->vdev_type == WMI_VDEV_TYPE_MONITOR && +- ath11k_peer_find_by_addr(ab, ar->mac_addr)) +- ath11k_peer_delete(ar, arvif->vdev_id, ar->mac_addr); ++ arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_addr(ab, ar->mac_addr); ++ spin_unlock_bh(&ab->base_lock); ++ if (peer) ++ ath11k_peer_delete(ar, arvif->vdev_id, ar->mac_addr); ++ } + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ret = ath11k_mac_monitor_stop(ar); diff --git a/package/kernel/mac80211/patches/ath11k/0202-ath11k-remove-unused-ATH11K_BD_IE_BOARD_EXT.patch b/package/kernel/mac80211/patches/ath11k/0202-ath11k-remove-unused-ATH11K_BD_IE_BOARD_EXT.patch new file mode 100644 index 00000000000000..5a8927002d7605 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0202-ath11k-remove-unused-ATH11K_BD_IE_BOARD_EXT.patch @@ -0,0 +1,26 @@ +From 7fb376ad7d3f200575b9f9374e21b39d30b57267 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 21 Mar 2022 13:03:23 +0200 +Subject: [PATCH] ath11k: remove unused ATH11K_BD_IE_BOARD_EXT + +Currently ATH11K_BD_IE_BOARD_EXT is not used, so remove it. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220319023543.14288-2-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/hw.h | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -293,7 +293,6 @@ enum ath11k_bd_ie_board_type { + enum ath11k_bd_ie_type { + /* contains sub IEs of enum ath11k_bd_ie_board_type */ + ATH11K_BD_IE_BOARD = 0, +- ATH11K_BD_IE_BOARD_EXT = 1, + }; + + struct ath11k_hw_regs { diff --git a/package/kernel/mac80211/patches/ath11k/0203-ath11k-disable-regdb-support-for-QCA6390.patch b/package/kernel/mac80211/patches/ath11k/0203-ath11k-disable-regdb-support-for-QCA6390.patch new file mode 100644 index 00000000000000..174e99284f1466 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0203-ath11k-disable-regdb-support-for-QCA6390.patch @@ -0,0 +1,28 @@ +From 0c104b6163e344e972dbbd255ca2441c171a8a87 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 21 Mar 2022 13:03:29 +0200 +Subject: [PATCH] ath11k: disable regdb support for QCA6390 + +Currently it does not have regdb files for QCA6390, so disable its regdb +support feature now. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-05266-QCAHSTSWPLZ_V2_TO_X86-1 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220319023543.14288-3-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -219,7 +219,7 @@ static const struct ath11k_hw_params ath + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), +- .supports_regdb = true, ++ .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, diff --git a/package/kernel/mac80211/patches/ath11k/0204-ath11k-add-support-for-device-recovery-for-QCA6390-W.patch b/package/kernel/mac80211/patches/ath11k/0204-ath11k-add-support-for-device-recovery-for-QCA6390-W.patch new file mode 100644 index 00000000000000..ddca64a1c5420b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0204-ath11k-add-support-for-device-recovery-for-QCA6390-W.patch @@ -0,0 +1,280 @@ +From 13da397f884d9c9a3fb6616206eeb6c6ab097287 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 28 Feb 2022 01:46:03 -0500 +Subject: [PATCH] ath11k: add support for device recovery for QCA6390/WCN6855 + +Currently ath11k has device recovery logic, it is introduced by this +patch "ath11k: Add support for subsystem recovery" which is upstream +by https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/commit/?h=ath11k-bringup&id=3a7b4838b6f6f234239f263ef3dc02e612a083ad. + +The patch is for AHB devices such as IPQ8074, it has remote proc module +which is used to download the firmware and boots the processor which +firmware is running on. If firmware crashed, remote proc module will +detect it and download and boot firmware again. Below command will +trigger a firmware crash, and then user can test feature of device +recovery. + +Test command: +echo assert > /sys/kernel/debug/ath11k/qca6390\ hw2.0/simulate_fw_crash +echo assert > /sys/kernel/debug/ath11k/wcn6855\ hw2.0/simulate_fw_crash + +Unfortunately, QCA6390 is PCIe bus, it does not have the remote proc +module, it use mhi module to communicate between firmware and ath11k. +So ath11k does not support device recovery for QCA6390 currently. + +This patch is to add the extra logic which is different for QCA6390. +When firmware crashed, MHI_CB_EE_RDDM event will be indicate by +firmware and then ath11k_mhi_op_status_cb which is the callback of +mhi_controller will receive the MHI_CB_EE_RDDM event, then ath11k +will start to do recovery process, ath11k_core_reset() calls +ath11k_hif_power_down()/ath11k_hif_power_up(), then the mhi/ath11k +will start to download and boot firmware. There are some logic to +avoid deadloop recovery and two simultaneous recovery operations. +And because it has muti-radios for the soc, so it add some logic +in ath11k_mac_op_reconfig_complete() to make sure all radios has +reconfig complete and then complete the device recovery. + +Also it add workqueue_aux, because ab->workqueue is used when receive +ATH11K_QMI_EVENT_FW_READY in recovery process(queue_work(ab->workqueue, +&ab->restart_work)), and ath11k_core_reset will wait for max +ATH11K_RESET_TIMEOUT_HZ for the previous restart_work finished, if +ath11k_core_reset also queued in ab->workqueue, then it will delay +restart_work of previous recovery and lead previous recovery fail. + +ath11k recovery success for QCA6390/WCN6855 after apply this patch. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220228064606.8981-2-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 68 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 13 +++++ + drivers/net/wireless/ath/ath11k/mac.c | 18 +++++++ + drivers/net/wireless/ath/ath11k/mhi.c | 33 +++++++++++++ + 4 files changed, 132 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1381,6 +1381,65 @@ static void ath11k_core_restart(struct w + complete(&ab->driver_recovery); + } + ++static void ath11k_core_reset(struct work_struct *work) ++{ ++ struct ath11k_base *ab = container_of(work, struct ath11k_base, reset_work); ++ int reset_count, fail_cont_count; ++ long time_left; ++ ++ if (!(test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))) { ++ ath11k_warn(ab, "ignore reset dev flags 0x%lx\n", ab->dev_flags); ++ return; ++ } ++ ++ /* Sometimes the recovery will fail and then the next all recovery fail, ++ * this is to avoid infinite recovery since it can not recovery success. ++ */ ++ fail_cont_count = atomic_read(&ab->fail_cont_count); ++ ++ if (fail_cont_count >= ATH11K_RESET_MAX_FAIL_COUNT_FINAL) ++ return; ++ ++ if (fail_cont_count >= ATH11K_RESET_MAX_FAIL_COUNT_FIRST && ++ time_before(jiffies, ab->reset_fail_timeout)) ++ return; ++ ++ reset_count = atomic_inc_return(&ab->reset_count); ++ ++ if (reset_count > 1) { ++ /* Sometimes it happened another reset worker before the previous one ++ * completed, then the second reset worker will destroy the previous one, ++ * thus below is to avoid that. ++ */ ++ ath11k_warn(ab, "already reseting count %d\n", reset_count); ++ ++ reinit_completion(&ab->reset_complete); ++ time_left = wait_for_completion_timeout(&ab->reset_complete, ++ ATH11K_RESET_TIMEOUT_HZ); ++ ++ if (time_left) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "to skip reset\n"); ++ atomic_dec(&ab->reset_count); ++ return; ++ } ++ ++ ab->reset_fail_timeout = jiffies + ATH11K_RESET_FAIL_TIMEOUT_HZ; ++ /* Record the continuous recovery fail count when recovery failed*/ ++ atomic_inc(&ab->fail_cont_count); ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset starting\n"); ++ ++ ab->is_reset = true; ++ atomic_set(&ab->recovery_count, 0); ++ ++ ath11k_hif_power_down(ab); ++ ath11k_qmi_free_resource(ab); ++ ath11k_hif_power_up(ab); ++ ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n"); ++} ++ + static int ath11k_init_hw_params(struct ath11k_base *ab) + { + const struct ath11k_hw_params *hw_params = NULL; +@@ -1450,6 +1509,7 @@ EXPORT_SYMBOL(ath11k_core_deinit); + + void ath11k_core_free(struct ath11k_base *ab) + { ++ destroy_workqueue(ab->workqueue_aux); + destroy_workqueue(ab->workqueue); + + kfree(ab); +@@ -1472,9 +1532,14 @@ struct ath11k_base *ath11k_core_alloc(st + if (!ab->workqueue) + goto err_sc_free; + ++ ab->workqueue_aux = create_singlethread_workqueue("ath11k_aux_wq"); ++ if (!ab->workqueue_aux) ++ goto err_free_wq; ++ + mutex_init(&ab->core_lock); + spin_lock_init(&ab->base_lock); + mutex_init(&ab->vdev_id_11d_lock); ++ init_completion(&ab->reset_complete); + + INIT_LIST_HEAD(&ab->peers); + init_waitqueue_head(&ab->peer_mapping_wq); +@@ -1483,6 +1548,7 @@ struct ath11k_base *ath11k_core_alloc(st + INIT_WORK(&ab->restart_work, ath11k_core_restart); + INIT_WORK(&ab->update_11d_work, ath11k_update_11d); + INIT_WORK(&ab->rfkill_work, ath11k_rfkill_work); ++ INIT_WORK(&ab->reset_work, ath11k_core_reset); + timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); + init_completion(&ab->htc_suspend); + init_completion(&ab->wow.wakeup_completed); +@@ -1493,6 +1559,8 @@ struct ath11k_base *ath11k_core_alloc(st + + return ab; + ++err_free_wq: ++ destroy_workqueue(ab->workqueue); + err_sc_free: + kfree(ab); + return NULL; +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -40,6 +40,10 @@ + extern unsigned int ath11k_frame_mode; + + #define ATH11K_MON_TIMER_INTERVAL 10 ++#define ATH11K_RESET_TIMEOUT_HZ (20 * HZ) ++#define ATH11K_RESET_MAX_FAIL_COUNT_FIRST 3 ++#define ATH11K_RESET_MAX_FAIL_COUNT_FINAL 5 ++#define ATH11K_RESET_FAIL_TIMEOUT_HZ (20 * HZ) + + enum ath11k_supported_bw { + ATH11K_BW_20 = 0, +@@ -820,6 +824,15 @@ struct ath11k_base { + struct work_struct restart_work; + struct work_struct update_11d_work; + u8 new_alpha2[3]; ++ struct workqueue_struct *workqueue_aux; ++ struct work_struct reset_work; ++ atomic_t reset_count; ++ atomic_t recovery_count; ++ bool is_reset; ++ struct completion reset_complete; ++ /* continuous recovery fail count */ ++ atomic_t fail_cont_count; ++ unsigned long reset_fail_timeout; + struct { + /* protected by data_lock */ + u32 fw_crash_counter; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7915,6 +7915,8 @@ ath11k_mac_op_reconfig_complete(struct i + enum ieee80211_reconfig_type reconfig_type) + { + struct ath11k *ar = hw->priv; ++ struct ath11k_base *ab = ar->ab; ++ int recovery_count; + + if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) + return; +@@ -7926,6 +7928,22 @@ ath11k_mac_op_reconfig_complete(struct i + ar->pdev->pdev_id); + ar->state = ATH11K_STATE_ON; + ieee80211_wake_queues(ar->hw); ++ ++ if (ab->is_reset) { ++ recovery_count = atomic_inc_return(&ab->recovery_count); ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "recovery count %d\n", recovery_count); ++ /* When there are multiple radios in an SOC, ++ * the recovery has to be done for each radio ++ */ ++ if (recovery_count == ab->num_radios) { ++ atomic_dec(&ab->reset_count); ++ complete(&ab->reset_complete); ++ ab->is_reset = false; ++ atomic_set(&ab->fail_cont_count, 0); ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n"); ++ } ++ } + } + + mutex_unlock(&ar->conf_mutex); +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -292,15 +292,48 @@ static void ath11k_mhi_op_runtime_put(st + { + } + ++static char *ath11k_mhi_op_callback_to_str(enum mhi_callback reason) ++{ ++ switch (reason) { ++ case MHI_CB_IDLE: ++ return "MHI_CB_IDLE"; ++ case MHI_CB_PENDING_DATA: ++ return "MHI_CB_PENDING_DATA"; ++ case MHI_CB_LPM_ENTER: ++ return "MHI_CB_LPM_ENTER"; ++ case MHI_CB_LPM_EXIT: ++ return "MHI_CB_LPM_EXIT"; ++ case MHI_CB_EE_RDDM: ++ return "MHI_CB_EE_RDDM"; ++ case MHI_CB_EE_MISSION_MODE: ++ return "MHI_CB_EE_MISSION_MODE"; ++ case MHI_CB_SYS_ERROR: ++ return "MHI_CB_SYS_ERROR"; ++ case MHI_CB_FATAL_ERROR: ++ return "MHI_CB_FATAL_ERROR"; ++ case MHI_CB_BW_REQ: ++ return "MHI_CB_BW_REQ"; ++ default: ++ return "UNKNOWN"; ++ } ++}; ++ + static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, + enum mhi_callback cb) + { + struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev); + ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "mhi notify status reason %s\n", ++ ath11k_mhi_op_callback_to_str(cb)); ++ + switch (cb) { + case MHI_CB_SYS_ERROR: + ath11k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n"); + break; ++ case MHI_CB_EE_RDDM: ++ if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))) ++ queue_work(ab->workqueue_aux, &ab->reset_work); ++ break; + default: + break; + } diff --git a/package/kernel/mac80211/patches/ath11k/0205-ath11k-add-synchronization-operation-between-reconfi.patch b/package/kernel/mac80211/patches/ath11k/0205-ath11k-add-synchronization-operation-between-reconfi.patch new file mode 100644 index 00000000000000..17e41733920e8b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0205-ath11k-add-synchronization-operation-between-reconfi.patch @@ -0,0 +1,182 @@ +From 38194f3a605e4a961f28bc38a73a4f4d43123968 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 21 Mar 2022 13:16:57 +0200 +Subject: [PATCH] ath11k: add synchronization operation between reconfigure of + mac80211 and ath11k_base + +ieee80211_reconfig() of mac80211 is the main function for recovery of +each ieee80211_hw and ath11k, and ath11k_core_reconfigure_on_crash() +is the main function for recovery of ath11k_base, it has more than +one ieee80211_hw and ath11k for each ath11k_base, so it need to add +synchronization between them, otherwise it has many issue. + +For example, when ath11k_core_reconfigure_on_crash() is not complete, +mac80211 send a hw scan request to ath11k, it leads firmware crash, +because firmware has not been initialized at that moment, firmware +is only finished downloaded and loaded, it can not receive scan +command. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220228064606.8981-3-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 51 ++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/core.h | 5 +++ + drivers/net/wireless/ath/ath11k/mac.c | 22 +++++++++++ + 3 files changed, 70 insertions(+), 8 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1302,12 +1302,11 @@ static void ath11k_update_11d(struct wor + } + } + +-static void ath11k_core_restart(struct work_struct *work) ++static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) + { +- struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); + struct ath11k *ar; + struct ath11k_pdev *pdev; +- int i, ret = 0; ++ int i; + + spin_lock_bh(&ab->base_lock); + ab->stats.fw_crash_counter++; +@@ -1340,12 +1339,13 @@ static void ath11k_core_restart(struct w + + wake_up(&ab->wmi_ab.tx_credits_wq); + wake_up(&ab->peer_mapping_wq); ++} + +- ret = ath11k_core_reconfigure_on_crash(ab); +- if (ret) { +- ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); +- return; +- } ++static void ath11k_core_post_reconfigure_recovery(struct ath11k_base *ab) ++{ ++ struct ath11k *ar; ++ struct ath11k_pdev *pdev; ++ int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; +@@ -1381,6 +1381,27 @@ static void ath11k_core_restart(struct w + complete(&ab->driver_recovery); + } + ++static void ath11k_core_restart(struct work_struct *work) ++{ ++ struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); ++ int ret; ++ ++ if (!ab->is_reset) ++ ath11k_core_pre_reconfigure_recovery(ab); ++ ++ ret = ath11k_core_reconfigure_on_crash(ab); ++ if (ret) { ++ ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); ++ return; ++ } ++ ++ if (ab->is_reset) ++ complete_all(&ab->reconfigure_complete); ++ ++ if (!ab->is_reset) ++ ath11k_core_post_reconfigure_recovery(ab); ++} ++ + static void ath11k_core_reset(struct work_struct *work) + { + struct ath11k_base *ab = container_of(work, struct ath11k_base, reset_work); +@@ -1432,6 +1453,18 @@ static void ath11k_core_reset(struct wor + + ab->is_reset = true; + atomic_set(&ab->recovery_count, 0); ++ reinit_completion(&ab->recovery_start); ++ atomic_set(&ab->recovery_start_count, 0); ++ ++ ath11k_core_pre_reconfigure_recovery(ab); ++ ++ reinit_completion(&ab->reconfigure_complete); ++ ath11k_core_post_reconfigure_recovery(ab); ++ ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "waiting recovery start...\n"); ++ ++ time_left = wait_for_completion_timeout(&ab->recovery_start, ++ ATH11K_RECOVER_START_TIMEOUT_HZ); + + ath11k_hif_power_down(ab); + ath11k_qmi_free_resource(ab); +@@ -1540,6 +1573,8 @@ struct ath11k_base *ath11k_core_alloc(st + spin_lock_init(&ab->base_lock); + mutex_init(&ab->vdev_id_11d_lock); + init_completion(&ab->reset_complete); ++ init_completion(&ab->reconfigure_complete); ++ init_completion(&ab->recovery_start); + + INIT_LIST_HEAD(&ab->peers); + init_waitqueue_head(&ab->peer_mapping_wq); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -44,6 +44,8 @@ extern unsigned int ath11k_frame_mode; + #define ATH11K_RESET_MAX_FAIL_COUNT_FIRST 3 + #define ATH11K_RESET_MAX_FAIL_COUNT_FINAL 5 + #define ATH11K_RESET_FAIL_TIMEOUT_HZ (20 * HZ) ++#define ATH11K_RECONFIGURE_TIMEOUT_HZ (10 * HZ) ++#define ATH11K_RECOVER_START_TIMEOUT_HZ (20 * HZ) + + enum ath11k_supported_bw { + ATH11K_BW_20 = 0, +@@ -828,8 +830,11 @@ struct ath11k_base { + struct work_struct reset_work; + atomic_t reset_count; + atomic_t recovery_count; ++ atomic_t recovery_start_count; + bool is_reset; + struct completion reset_complete; ++ struct completion reconfigure_complete; ++ struct completion recovery_start; + /* continuous recovery fail count */ + atomic_t fail_cont_count; + unsigned long reset_fail_timeout; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5749,6 +5749,27 @@ static int ath11k_mac_config_mon_status_ + return ret; + } + ++static void ath11k_mac_wait_reconfigure(struct ath11k_base *ab) ++{ ++ int recovery_start_count; ++ ++ if (!ab->is_reset) ++ return; ++ ++ recovery_start_count = atomic_inc_return(&ab->recovery_start_count); ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "recovery start count %d\n", recovery_start_count); ++ ++ if (recovery_start_count == ab->num_radios) { ++ complete(&ab->recovery_start); ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "recovery started success\n"); ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "waiting reconfigure...\n"); ++ ++ wait_for_completion_timeout(&ab->reconfigure_complete, ++ ATH11K_RECONFIGURE_TIMEOUT_HZ); ++} ++ + static int ath11k_mac_op_start(struct ieee80211_hw *hw) + { + struct ath11k *ar = hw->priv; +@@ -5765,6 +5786,7 @@ static int ath11k_mac_op_start(struct ie + break; + case ATH11K_STATE_RESTARTING: + ar->state = ATH11K_STATE_RESTARTED; ++ ath11k_mac_wait_reconfigure(ab); + break; + case ATH11K_STATE_RESTARTED: + case ATH11K_STATE_WEDGED: diff --git a/package/kernel/mac80211/patches/ath11k/0206-ath11k-Add-hw-restart-option-to-simulate_fw_crash.patch b/package/kernel/mac80211/patches/ath11k/0206-ath11k-Add-hw-restart-option-to-simulate_fw_crash.patch new file mode 100644 index 00000000000000..886261d1d640fd --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0206-ath11k-Add-hw-restart-option-to-simulate_fw_crash.patch @@ -0,0 +1,36 @@ +From 78e3e6094220a71504e7136c42b49fc8ed3a72b4 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 21 Mar 2022 13:17:03 +0200 +Subject: [PATCH] ath11k: Add hw-restart option to simulate_fw_crash + +Add hw-restart to directly restart wlan. Like UTF mode start it will +restart hardware and download firmware again. + +Usage: +1. Run command: + echo hw-restart > /sys/kernel/debug/ath11k/qca6390\ hw2.0/simulate_fw_crash + echo hw-restart > /sys/kernel/debug/ath11k/wcn6855\ hw2.0/simulate_fw_crash +2. wlan will be restart and do recovery process and success. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220228064606.8981-4-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/debugfs.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -596,6 +596,10 @@ static ssize_t ath11k_write_simulate_fw_ + ret = ath11k_wmi_force_fw_hang_cmd(ar, + ATH11K_WMI_FW_HANG_ASSERT_TYPE, + ATH11K_WMI_FW_HANG_DELAY); ++ } else if (!strcmp(buf, "hw-restart")) { ++ ath11k_info(ab, "user requested hw restart\n"); ++ queue_work(ab->workqueue_aux, &ab->reset_work); ++ ret = 0; + } else { + ret = -EINVAL; + goto exit; diff --git a/package/kernel/mac80211/patches/ath11k/0207-ath11k-fix-the-warning-of-dev_wake-in-mhi_pm_disable.patch b/package/kernel/mac80211/patches/ath11k/0207-ath11k-fix-the-warning-of-dev_wake-in-mhi_pm_disable.patch new file mode 100644 index 00000000000000..b583cf74d81937 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0207-ath11k-fix-the-warning-of-dev_wake-in-mhi_pm_disable.patch @@ -0,0 +1,124 @@ +From 0d7a8a6204ea9271f1d0a8c66a9fd2f54d2e3cbc Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 21 Mar 2022 13:17:08 +0200 +Subject: [PATCH] ath11k: fix the warning of dev_wake in + mhi_pm_disable_transition() + +When test device recovery with below command, it has warning in message +as below. +echo assert > /sys/kernel/debug/ath11k/wcn6855\ hw2.0/simulate_fw_crash +echo assert > /sys/kernel/debug/ath11k/qca6390\ hw2.0/simulate_fw_crash + +warning message: +[ 1965.642121] ath11k_pci 0000:06:00.0: simulating firmware assert crash +[ 1968.471364] ieee80211 phy0: Hardware restart was requested +[ 1968.511305] ------------[ cut here ]------------ +[ 1968.511368] WARNING: CPU: 3 PID: 1546 at drivers/bus/mhi/core/pm.c:505 mhi_pm_disable_transition+0xb37/0xda0 [mhi] +[ 1968.511443] Modules linked in: ath11k_pci ath11k mac80211 libarc4 cfg80211 qmi_helpers qrtr_mhi mhi qrtr nvme nvme_core +[ 1968.511563] CPU: 3 PID: 1546 Comm: kworker/u17:0 Kdump: loaded Tainted: G W 5.17.0-rc3-wt-ath+ #579 +[ 1968.511629] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0067.2021.0528.1339 05/28/2021 +[ 1968.511704] Workqueue: mhi_hiprio_wq mhi_pm_st_worker [mhi] +[ 1968.511787] RIP: 0010:mhi_pm_disable_transition+0xb37/0xda0 [mhi] +[ 1968.511870] Code: a9 fe ff ff 4c 89 ff 44 89 04 24 e8 03 46 f6 e5 44 8b 04 24 41 83 f8 01 0f 84 21 fe ff ff e9 4c fd ff ff 0f 0b e9 af f8 ff ff <0f> 0b e9 5c f8 ff ff 48 89 df e8 da 9e ee e3 e9 12 fd ff ff 4c 89 +[ 1968.511923] RSP: 0018:ffffc900024efbf0 EFLAGS: 00010286 +[ 1968.511969] RAX: 00000000ffffffff RBX: ffff88811d241250 RCX: ffffffffc0176922 +[ 1968.512014] RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffff888118a90a24 +[ 1968.512059] RBP: ffff888118a90800 R08: 0000000000000000 R09: ffff888118a90a27 +[ 1968.512102] R10: ffffed1023152144 R11: 0000000000000001 R12: ffff888118a908ac +[ 1968.512229] R13: ffff888118a90928 R14: dffffc0000000000 R15: ffff888118a90a24 +[ 1968.512310] FS: 0000000000000000(0000) GS:ffff888234200000(0000) knlGS:0000000000000000 +[ 1968.512405] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 1968.512493] CR2: 00007f5538f443a8 CR3: 000000016dc28001 CR4: 00000000003706e0 +[ 1968.512587] Call Trace: +[ 1968.512672] +[ 1968.512751] ? _raw_spin_unlock_irq+0x1f/0x40 +[ 1968.512859] mhi_pm_st_worker+0x3ac/0x790 [mhi] +[ 1968.512959] ? mhi_pm_mission_mode_transition.isra.0+0x7d0/0x7d0 [mhi] +[ 1968.513063] process_one_work+0x86a/0x1400 +[ 1968.513184] ? pwq_dec_nr_in_flight+0x230/0x230 +[ 1968.513312] ? move_linked_works+0x125/0x290 +[ 1968.513416] worker_thread+0x6db/0xf60 +[ 1968.513536] ? process_one_work+0x1400/0x1400 +[ 1968.513627] kthread+0x241/0x2d0 +[ 1968.513733] ? kthread_complete_and_exit+0x20/0x20 +[ 1968.513821] ret_from_fork+0x22/0x30 +[ 1968.513924] + +Reason is mhi_deassert_dev_wake() from mhi_device_put() is called +but mhi_assert_dev_wake() from __mhi_device_get_sync() is not called +in progress of recovery. Commit 8e0559921f9a ("bus: mhi: core: +Skip device wake in error or shutdown state") add check for the +pm_state of mhi in __mhi_device_get_sync(), and the pm_state is not +the normal state untill recovery is completed, so it leads the +dev_wake is not 0 and above warning print in mhi_pm_disable_transition() +while checking mhi_cntrl->dev_wake. + +Add check in ath11k_pci_write32()/ath11k_pci_read32() to skip call +mhi_device_put() if mhi_device_get_sync() does not really do wake, +then the warning gone. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220228064606.8981-5-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/pci.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -191,6 +191,7 @@ void ath11k_pci_write32(struct ath11k_ba + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 window_start; ++ int ret = 0; + + /* for offset beyond BAR + 4K - 32, may + * need to wakeup MHI to access. +@@ -198,7 +199,7 @@ void ath11k_pci_write32(struct ath11k_ba + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && + offset >= ACCESS_ALWAYS_OFF) +- mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); ++ ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + + if (offset < WINDOW_START) { + iowrite32(value, ab->mem + offset); +@@ -222,7 +223,8 @@ void ath11k_pci_write32(struct ath11k_ba + + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ACCESS_ALWAYS_OFF) ++ offset >= ACCESS_ALWAYS_OFF && ++ !ret) + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); + } + +@@ -230,6 +232,7 @@ u32 ath11k_pci_read32(struct ath11k_base + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 val, window_start; ++ int ret = 0; + + /* for offset beyond BAR + 4K - 32, may + * need to wakeup MHI to access. +@@ -237,7 +240,7 @@ u32 ath11k_pci_read32(struct ath11k_base + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && + offset >= ACCESS_ALWAYS_OFF) +- mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); ++ ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + + if (offset < WINDOW_START) { + val = ioread32(ab->mem + offset); +@@ -261,7 +264,8 @@ u32 ath11k_pci_read32(struct ath11k_base + + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ACCESS_ALWAYS_OFF) ++ offset >= ACCESS_ALWAYS_OFF && ++ !ret) + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); + + return val; diff --git a/package/kernel/mac80211/patches/ath11k/0208-ath11k-enable-PLATFORM_CAP_PCIE_GLOBAL_RESET-QMI-hos.patch b/package/kernel/mac80211/patches/ath11k/0208-ath11k-enable-PLATFORM_CAP_PCIE_GLOBAL_RESET-QMI-hos.patch new file mode 100644 index 00000000000000..a481417229c043 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0208-ath11k-enable-PLATFORM_CAP_PCIE_GLOBAL_RESET-QMI-hos.patch @@ -0,0 +1,115 @@ +From 1e4ac7173c9394de7f54a4a861377ac3f030c614 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 11 Oct 2021 13:56:02 +0800 +Subject: [PATCH] ath11k: enable PLATFORM_CAP_PCIE_GLOBAL_RESET QMI host + capability + +In Qualcomm ARM platforms there is WL_EN pin and other power regulators +which can be controlled at platform side to completely reset the chip. +For most of x86 and other platforms, the chip is connected via PCIe M.2 +interface, and there is no way to control WL_EN pin. Instead the host +driver needs to reset the chip via PCIE_SOC_GLOBAL_RESET hardware +register, just like ath11k does currently. + +But when using PCIE_SOC_GLOBAL_RESET there are some hardware registers +which are not cleared/restored. To handle those cases we can enable +PLATFORM_CAP_PCIE_GLOBAL_RESET QMI host capability to tell the firmware +to do some platform specific operations after firmware download. + +This does not fix any known issues, but is recommended by the firmware +team, so enable the capability on QCA6390 and WCN6855 PCI devices. It is +currently unclear if this should be enabled also on QCN9074, so leave it +disabled for now. On AHB devices this is not needed as they don't use +PCIE_SOC_GLOBAL_RESET. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20211011055602.77342-1-bqiang@codeaurora.org +--- + drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/qmi.c | 4 ++++ + 3 files changed, 11 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -100,6 +100,7 @@ static const struct ath11k_hw_params ath + .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, ++ .global_reset = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -166,6 +167,7 @@ static const struct ath11k_hw_params ath + .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, ++ .global_reset = false, + }, + { + .name = "qca6390 hw2.0", +@@ -231,6 +233,7 @@ static const struct ath11k_hw_params ath + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, ++ .global_reset = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -296,6 +299,7 @@ static const struct ath11k_hw_params ath + .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, ++ .global_reset = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -361,6 +365,7 @@ static const struct ath11k_hw_params ath + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, ++ .global_reset = true, + }, + { + .name = "wcn6855 hw2.1", +@@ -425,6 +430,7 @@ static const struct ath11k_hw_params ath + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, ++ .global_reset = true, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -194,6 +194,7 @@ struct ath11k_hw_params { + bool fw_wmi_diag_event; + bool current_cc_support; + bool dbr_debug_support; ++ bool global_reset; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -15,6 +15,7 @@ + + #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 + #define HOST_CSTATE_BIT 0x04 ++#define PLATFORM_CAP_PCIE_GLOBAL_RESET 0x08 + + #define FW_BUILD_ID_MASK "QC_IMAGE_VERSION_STRING=" + +@@ -1676,6 +1677,9 @@ static int ath11k_qmi_host_cap_send(stru + req.nm_modem |= SLEEP_CLOCK_SELECT_INTERNAL_BIT; + } + ++ if (ab->hw_params.global_reset) ++ req.nm_modem |= PLATFORM_CAP_PCIE_GLOBAL_RESET; ++ + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi host cap request\n"); + + ret = qmi_txn_init(&ab->qmi.handle, &txn, diff --git a/package/kernel/mac80211/patches/ath11k/0209-ath11k-add-fallback-board-name-without-variant-while.patch b/package/kernel/mac80211/patches/ath11k/0209-ath11k-add-fallback-board-name-without-variant-while.patch new file mode 100644 index 00000000000000..67377e7939aa93 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0209-ath11k-add-fallback-board-name-without-variant-while.patch @@ -0,0 +1,159 @@ +From 62abdc06c50eb18e3fa62f7136e66842a96f6b58 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 23 Mar 2022 11:14:16 +0200 +Subject: [PATCH] ath11k: add fallback board name without variant while + searching board-2.bin + +Sometimes it has a variant value which read from DT or SMBIOS by +ath11k, and meanwhile it does not have the matched board name in +board-2.bin, then it will failed at boot up phase. + +Add fallback board name which removed variant value and search again +in board-2.bin when failed with variant and try to load the board +data again to increase boot up success rate. + +dmesg log after this patch: +[169547.248472] ath11k_pci 0000:05:00.0: boot using board name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262,variant=test' +[169547.248565] ath11k_pci 0000:05:00.0: boot firmware request ath11k/WCN6855/hw2.0/board-2.bin size 180324 +[169547.248568] ath11k_pci 0000:05:00.0: board name +[169547.248570] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 +[169547.248571] ath11k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, +[169547.248572] ath11k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor +[169547.248574] ath11k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- +[169547.248575] ath11k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- +[169547.248576] ath11k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo +[169547.248577] ath11k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 32 ard-id=262 +[169547.248578] ath11k_pci 0000:05:00.0: board name +[169547.248579] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 +[169547.248581] ath11k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, +[169547.248582] ath11k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor +[169547.248583] ath11k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- +[169547.248584] ath11k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- +[169547.248585] ath11k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo +[169547.248587] ath11k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 36 ard-id=266 +[169547.248588] ath11k_pci 0000:05:00.0: board name +[169547.248589] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 +[169547.248590] ath11k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, +[169547.248591] ath11k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor +[169547.248592] ath11k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- +[169547.248594] ath11k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- +[169547.248595] ath11k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 31 38 2c 71 6d 69 2d 62 chip-id=18,qmi-b +[169547.248596] ath11k_pci 0000:05:00.0: 00000060: 6f 61 72 64 2d 69 64 3d 32 36 36 oard-id=266 +[169547.248597] ath11k_pci 0000:05:00.0: failed to fetch board data for bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262,variant=test from ath11k/WCN6855/hw2.0/board-2.bin +[169547.248476] ath11k_pci 0000:05:00.0: boot using board name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' +[169547.248634] ath11k_pci 0000:05:00.0: boot firmware request ath11k/WCN6855/hw2.0/board-2.bin size 180324 +[169547.248636] ath11k_pci 0000:05:00.0: board name +[169547.248637] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 +[169547.248638] ath11k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, +[169547.248639] ath11k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor +[169547.248641] ath11k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- +[169547.248642] ath11k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- +[169547.248643] ath11k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo +[169547.248645] ath11k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 32 ard-id=262 +[169547.248646] ath11k_pci 0000:05:00.0: boot found match for name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' +[169547.248647] ath11k_pci 0000:05:00.0: boot found board data for 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' +[169547.248649] ath11k_pci 0000:05:00.0: using board api 2 + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220315104721.26649-2-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 48 ++++++++++++++++++++++---- + 1 file changed, 41 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -571,13 +571,13 @@ int ath11k_core_check_dt(struct ath11k_b + return 0; + } + +-static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, +- size_t name_len) ++static int __ath11k_core_create_board_name(struct ath11k_base *ab, char *name, ++ size_t name_len, bool with_variant) + { + /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ + char variant[9 + ATH11K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; + +- if (ab->qmi.target.bdf_ext[0] != '\0') ++ if (with_variant && ab->qmi.target.bdf_ext[0] != '\0') + scnprintf(variant, sizeof(variant), ",variant=%s", + ab->qmi.target.bdf_ext); + +@@ -607,6 +607,18 @@ static int ath11k_core_create_board_name + return 0; + } + ++static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, ++ size_t name_len) ++{ ++ return __ath11k_core_create_board_name(ab, name, name_len, true); ++} ++ ++static int ath11k_core_create_fallback_board_name(struct ath11k_base *ab, char *name, ++ size_t name_len) ++{ ++ return __ath11k_core_create_board_name(ab, name, name_len, false); ++} ++ + const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, + const char *file) + { +@@ -810,7 +822,7 @@ static int ath11k_core_fetch_board_data_ + + out: + if (!bd->data || !bd->len) { +- ath11k_err(ab, ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, + "failed to fetch board data for %s from %s\n", + boardname, filepath); + ret = -ENODATA; +@@ -842,10 +854,13 @@ int ath11k_core_fetch_board_data_api_1(s + #define BOARD_NAME_SIZE 200 + int ath11k_core_fetch_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) + { +- char boardname[BOARD_NAME_SIZE]; ++ char boardname[BOARD_NAME_SIZE], fallback_boardname[BOARD_NAME_SIZE]; ++ char *filename, filepath[100]; + int ret; + +- ret = ath11k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); ++ filename = ATH11K_BOARD_API2_FILE; ++ ++ ret = ath11k_core_create_board_name(ab, boardname, sizeof(boardname)); + if (ret) { + ath11k_err(ab, "failed to create board name: %d", ret); + return ret; +@@ -856,10 +871,29 @@ int ath11k_core_fetch_bdf(struct ath11k_ + if (!ret) + goto success; + ++ ret = ath11k_core_create_fallback_board_name(ab, fallback_boardname, ++ sizeof(fallback_boardname)); ++ if (ret) { ++ ath11k_err(ab, "failed to create fallback board name: %d", ret); ++ return ret; ++ } ++ ++ ret = ath11k_core_fetch_board_data_api_n(ab, bd, fallback_boardname); ++ if (!ret) ++ goto success; ++ + ab->bd_api = 1; + ret = ath11k_core_fetch_board_data_api_1(ab, bd, ATH11K_DEFAULT_BOARD_FILE); + if (ret) { +- ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", ++ ath11k_core_create_firmware_path(ab, filename, ++ filepath, sizeof(filepath)); ++ ath11k_err(ab, "failed to fetch board data for %s from %s\n", ++ boardname, filepath); ++ if (memcmp(boardname, fallback_boardname, strlen(boardname))) ++ ath11k_err(ab, "failed to fetch board data for %s from %s\n", ++ fallback_boardname, filepath); ++ ++ ath11k_err(ab, "failed to fetch board.bin from %s\n", + ab->hw_params.fw.dir); + return ret; + } diff --git a/package/kernel/mac80211/patches/ath11k/0210-ath11k-add-read-variant-from-SMBIOS-for-download-boa.patch b/package/kernel/mac80211/patches/ath11k/0210-ath11k-add-read-variant-from-SMBIOS-for-download-boa.patch new file mode 100644 index 00000000000000..86e52113ea5709 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0210-ath11k-add-read-variant-from-SMBIOS-for-download-boa.patch @@ -0,0 +1,169 @@ +From 9d97114d222047c0699bbdbf8f128907f423bbe6 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 23 Mar 2022 11:14:17 +0200 +Subject: [PATCH] ath11k: add read variant from SMBIOS for download board data + +This is to read variant from SMBIOS such as read from DT, the variant +string will be used to one part of string which used to search board +data from board-2.bin. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220315104721.26649-3-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 70 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 20 +++++++- + drivers/net/wireless/ath/ath11k/qmi.c | 4 ++ + 3 files changed, 93 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++ + #include "core.h" + #include "dp_tx.h" + #include "dp_rx.h" +@@ -548,6 +549,75 @@ int ath11k_core_resume(struct ath11k_bas + } + EXPORT_SYMBOL(ath11k_core_resume); + ++static void ath11k_core_check_bdfext(const struct dmi_header *hdr, void *data) ++{ ++ struct ath11k_base *ab = data; ++ const char *magic = ATH11K_SMBIOS_BDF_EXT_MAGIC; ++ struct ath11k_smbios_bdf *smbios = (struct ath11k_smbios_bdf *)hdr; ++ ssize_t copied; ++ size_t len; ++ int i; ++ ++ if (ab->qmi.target.bdf_ext[0] != '\0') ++ return; ++ ++ if (hdr->type != ATH11K_SMBIOS_BDF_EXT_TYPE) ++ return; ++ ++ if (hdr->length != ATH11K_SMBIOS_BDF_EXT_LENGTH) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "wrong smbios bdf ext type length (%d).\n", ++ hdr->length); ++ return; ++ } ++ ++ if (!smbios->bdf_enabled) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "bdf variant name not found.\n"); ++ return; ++ } ++ ++ /* Only one string exists (per spec) */ ++ if (memcmp(smbios->bdf_ext, magic, strlen(magic)) != 0) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "bdf variant magic does not match.\n"); ++ return; ++ } ++ ++ len = min_t(size_t, ++ strlen(smbios->bdf_ext), sizeof(ab->qmi.target.bdf_ext)); ++ for (i = 0; i < len; i++) { ++ if (!isascii(smbios->bdf_ext[i]) || !isprint(smbios->bdf_ext[i])) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "bdf variant name contains non ascii chars.\n"); ++ return; ++ } ++ } ++ ++ /* Copy extension name without magic prefix */ ++ copied = strscpy(ab->qmi.target.bdf_ext, smbios->bdf_ext + strlen(magic), ++ sizeof(ab->qmi.target.bdf_ext)); ++ if (copied < 0) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "bdf variant string is longer than the buffer can accommodate\n"); ++ return; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "found and validated bdf variant smbios_type 0x%x bdf %s\n", ++ ATH11K_SMBIOS_BDF_EXT_TYPE, ab->qmi.target.bdf_ext); ++} ++ ++int ath11k_core_check_smbios(struct ath11k_base *ab) ++{ ++ ab->qmi.target.bdf_ext[0] = '\0'; ++ dmi_walk(ath11k_core_check_bdfext, ab); ++ ++ if (ab->qmi.target.bdf_ext[0] == '\0') ++ return -ENODATA; ++ ++ return 0; ++} ++ + int ath11k_core_check_dt(struct ath11k_base *ab) + { + size_t max_len = sizeof(ab->qmi.target.bdf_ext); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -10,6 +10,8 @@ + #include + #include + #include ++#include ++#include + #include "qmi.h" + #include "htc.h" + #include "wmi.h" +@@ -37,6 +39,15 @@ + #define ATH11K_INVALID_HW_MAC_ID 0xFF + #define ATH11K_CONNECTION_LOSS_HZ (3 * HZ) + ++/* SMBIOS type containing Board Data File Name Extension */ ++#define ATH11K_SMBIOS_BDF_EXT_TYPE 0xF8 ++ ++/* SMBIOS type structure length (excluding strings-set) */ ++#define ATH11K_SMBIOS_BDF_EXT_LENGTH 0x9 ++ ++/* The magic used by QCA spec */ ++#define ATH11K_SMBIOS_BDF_EXT_MAGIC "BDF_" ++ + extern unsigned int ath11k_frame_mode; + + #define ATH11K_MON_TIMER_INTERVAL 10 +@@ -154,6 +165,13 @@ struct ath11k_ext_irq_grp { + struct net_device napi_ndev; + }; + ++struct ath11k_smbios_bdf { ++ struct dmi_header hdr; ++ u32 padding; ++ u8 bdf_enabled; ++ u8 bdf_ext[]; ++}; ++ + #define HEHANDLE_CAP_PHYINFO_SIZE 3 + #define HECAP_PHYINFO_SIZE 9 + #define HECAP_MACINFO_SIZE 5 +@@ -1046,7 +1064,7 @@ int ath11k_core_fetch_board_data_api_1(s + const char *name); + void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); + int ath11k_core_check_dt(struct ath11k_base *ath11k); +- ++int ath11k_core_check_smbios(struct ath11k_base *ab); + void ath11k_core_halt(struct ath11k *ar); + int ath11k_core_resume(struct ath11k_base *ab); + int ath11k_core_suspend(struct ath11k_base *ab); +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2095,6 +2095,10 @@ static int ath11k_qmi_request_target_cap + ab->qmi.target.fw_build_timestamp, + fw_build_id); + ++ r = ath11k_core_check_smbios(ab); ++ if (r) ++ ath11k_dbg(ab, ATH11K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); ++ + r = ath11k_core_check_dt(ab); + if (r) + ath11k_dbg(ab, ATH11K_DBG_QMI, "DT bdf variant name not set.\n"); diff --git a/package/kernel/mac80211/patches/ath11k/0211-ath11k-Add-peer-rhash-table-support.patch b/package/kernel/mac80211/patches/ath11k/0211-ath11k-Add-peer-rhash-table-support.patch new file mode 100644 index 00000000000000..ffaecc1aea24d2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0211-ath11k-Add-peer-rhash-table-support.patch @@ -0,0 +1,678 @@ +From 7b0c70d92a435913f6e11d6a248b935697e8a3eb Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Wed, 23 Mar 2022 11:14:17 +0200 +Subject: [PATCH] ath11k: Add peer rhash table support + +When more clients (128) are connected, the UL data traffic +KPI measurement is low compared to single client. This issue +is due to more CPU cycles spent on the peer lookup operation +with more clients. So reduce the peer lookup operation by +modifying the linear based lookup operation into the rhash +based lookup operation. This improve the peak throughput +measurement. Since this is a software algorithm change, it is +applicable for all the platforms. + +TCP UL 128 Clients test case Observation (64bit system): +Previous: ~550 Mbps +Now : ~860 Mbps + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01067-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1644036628-5334-1-git-send-email-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 3 +- + drivers/net/wireless/ath/ath11k/core.h | 14 + + drivers/net/wireless/ath/ath11k/mac.c | 16 +- + drivers/net/wireless/ath/ath11k/peer.c | 344 +++++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/peer.h | 10 +- + 5 files changed, 363 insertions(+), 24 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. +- * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. ++ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -1680,6 +1680,7 @@ struct ath11k_base *ath11k_core_alloc(st + goto err_free_wq; + + mutex_init(&ab->core_lock); ++ mutex_init(&ab->tbl_mtx_lock); + spin_lock_init(&ab->base_lock); + mutex_init(&ab->vdev_id_11d_lock); + init_completion(&ab->reset_complete); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #ifndef ATH11K_CORE_H +@@ -12,6 +13,7 @@ + #include + #include + #include ++#include + #include "qmi.h" + #include "htc.h" + #include "wmi.h" +@@ -803,6 +805,18 @@ struct ath11k_base { + struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS]; + struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; + unsigned long long free_vdev_map; ++ ++ /* To synchronize rhash tbl write operation */ ++ struct mutex tbl_mtx_lock; ++ ++ /* The rhashtable containing struct ath11k_peer keyed by mac addr */ ++ struct rhashtable *rhead_peer_addr; ++ struct rhashtable_params rhash_peer_addr_param; ++ ++ /* The rhashtable containing struct ath11k_peer keyed by id */ ++ struct rhashtable *rhead_peer_id; ++ struct rhashtable_params rhash_peer_id_param; ++ + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + u8 mac_addr[ETH_ALEN]; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. +- * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. ++ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -875,13 +875,16 @@ void ath11k_mac_peer_cleanup_all(struct + + lockdep_assert_held(&ar->conf_mutex); + ++ mutex_lock(&ab->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); + list_for_each_entry_safe(peer, tmp, &ab->peers, list) { + ath11k_peer_rx_tid_cleanup(ar, peer); ++ ath11k_peer_rhash_delete(ab, peer); + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ab->tbl_mtx_lock); + + ar->num_peers = 0; + ar->num_stations = 0; +@@ -4554,6 +4557,7 @@ static int ath11k_mac_op_sta_state(struc + } + + ath11k_mac_dec_num_stations(arvif, sta); ++ mutex_lock(&ar->ab->tbl_mtx_lock); + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + if (skip_peer_delete && peer) { +@@ -4561,12 +4565,14 @@ static int ath11k_mac_op_sta_state(struc + } else if (peer && peer->sta == sta) { + ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", + vif->addr, arvif->vdev_id); ++ ath11k_peer_rhash_delete(ar->ab, peer); + peer->sta = NULL; + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + spin_unlock_bh(&ar->ab->base_lock); ++ mutex_unlock(&ar->ab->tbl_mtx_lock); + + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; +@@ -8574,6 +8580,8 @@ void ath11k_mac_unregister(struct ath11k + + __ath11k_mac_unregister(ar); + } ++ ++ ath11k_peer_rhash_tbl_destroy(ab); + } + + static int __ath11k_mac_register(struct ath11k *ar) +@@ -8802,6 +8810,10 @@ int ath11k_mac_register(struct ath11k_ba + ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS(ab))) - 1; + ++ ret = ath11k_peer_rhash_tbl_init(ab); ++ if (ret) ++ return ret; ++ + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; +@@ -8831,6 +8843,8 @@ err_cleanup: + __ath11k_mac_unregister(ar); + } + ++ ath11k_peer_rhash_tbl_destroy(ab); ++ + return ret; + } + +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -1,23 +1,22 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include "core.h" + #include "peer.h" + #include "debug.h" + +-struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, +- const u8 *addr) ++static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k_base *ab, ++ int peer_id) + { + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) { +- if (peer->vdev_id != vdev_id) +- continue; +- if (!ether_addr_equal(peer->addr, addr)) ++ if (peer->peer_id != peer_id) + continue; + + return peer; +@@ -26,15 +25,15 @@ struct ath11k_peer *ath11k_peer_find(str + return NULL; + } + +-static struct ath11k_peer *ath11k_peer_find_by_pdev_idx(struct ath11k_base *ab, +- u8 pdev_idx, const u8 *addr) ++struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, ++ const u8 *addr) + { + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) { +- if (peer->pdev_idx != pdev_idx) ++ if (peer->vdev_id != vdev_id) + continue; + if (!ether_addr_equal(peer->addr, addr)) + continue; +@@ -52,14 +51,13 @@ struct ath11k_peer *ath11k_peer_find_by_ + + lockdep_assert_held(&ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) { +- if (!ether_addr_equal(peer->addr, addr)) +- continue; ++ if (!ab->rhead_peer_addr) ++ return NULL; + +- return peer; +- } ++ peer = rhashtable_lookup_fast(ab->rhead_peer_addr, addr, ++ ab->rhash_peer_addr_param); + +- return NULL; ++ return peer; + } + + struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, +@@ -69,11 +67,13 @@ struct ath11k_peer *ath11k_peer_find_by_ + + lockdep_assert_held(&ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) +- if (peer_id == peer->peer_id) +- return peer; ++ if (!ab->rhead_peer_id) ++ return NULL; + +- return NULL; ++ peer = rhashtable_lookup_fast(ab->rhead_peer_id, &peer_id, ++ ab->rhash_peer_id_param); ++ ++ return peer; + } + + struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab, +@@ -99,7 +99,7 @@ void ath11k_peer_unmap_event(struct ath1 + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_id(ab, peer_id); ++ peer = ath11k_peer_find_list_by_id(ab, peer_id); + if (!peer) { + ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n", + peer_id); +@@ -167,6 +167,76 @@ static int ath11k_wait_for_peer_common(s + return 0; + } + ++static inline int ath11k_peer_rhash_insert(struct ath11k_base *ab, ++ struct rhashtable *rtbl, ++ struct rhash_head *rhead, ++ struct rhashtable_params *params, ++ void *key) ++{ ++ struct ath11k_peer *tmp; ++ ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ tmp = rhashtable_lookup_get_insert_fast(rtbl, rhead, *params); ++ ++ if (!tmp) ++ return 0; ++ else if (IS_ERR(tmp)) ++ return PTR_ERR(tmp); ++ else ++ return -EEXIST; ++} ++ ++static inline int ath11k_peer_rhash_remove(struct ath11k_base *ab, ++ struct rhashtable *rtbl, ++ struct rhash_head *rhead, ++ struct rhashtable_params *params) ++{ ++ int ret; ++ ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ ret = rhashtable_remove_fast(rtbl, rhead, *params); ++ if (ret && ret != -ENOENT) ++ return ret; ++ ++ return 0; ++} ++ ++static int ath11k_peer_rhash_add(struct ath11k_base *ab, struct ath11k_peer *peer) ++{ ++ int ret; ++ ++ lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ if (!ab->rhead_peer_id || !ab->rhead_peer_addr) ++ return -EPERM; ++ ++ ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_id, &peer->rhash_id, ++ &ab->rhash_peer_id_param, &peer->peer_id); ++ if (ret) { ++ ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_id ret %d\n", ++ peer->addr, peer->peer_id, ret); ++ return ret; ++ } ++ ++ ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_addr, &peer->rhash_addr, ++ &ab->rhash_peer_addr_param, &peer->addr); ++ if (ret) { ++ ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_addr ret %d\n", ++ peer->addr, peer->peer_id, ret); ++ goto err_clean; ++ } ++ ++ return 0; ++ ++err_clean: ++ ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id, ++ &ab->rhash_peer_id_param); ++ return ret; ++} ++ + void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) + { + struct ath11k_peer *peer, *tmp; +@@ -174,6 +244,7 @@ void ath11k_peer_cleanup(struct ath11k * + + lockdep_assert_held(&ar->conf_mutex); + ++ mutex_lock(&ab->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); + list_for_each_entry_safe(peer, tmp, &ab->peers, list) { + if (peer->vdev_id != vdev_id) +@@ -182,12 +253,14 @@ void ath11k_peer_cleanup(struct ath11k * + ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n", + peer->addr, vdev_id); + ++ ath11k_peer_rhash_delete(ab, peer); + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + + spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ab->tbl_mtx_lock); + } + + static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) +@@ -220,14 +293,35 @@ int ath11k_wait_for_peer_delete_done(str + static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr) + { + int ret; ++ struct ath11k_peer *peer; ++ struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + ++ mutex_lock(&ab->tbl_mtx_lock); ++ spin_lock_bh(&ab->base_lock); ++ ++ peer = ath11k_peer_find_by_addr(ab, addr); ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ab->tbl_mtx_lock); ++ ++ ath11k_warn(ab, ++ "failed to find peer vdev_id %d addr %pM in delete\n", ++ vdev_id, addr); ++ return -EINVAL; ++ } ++ ++ ath11k_peer_rhash_delete(ab, peer); ++ ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ab->tbl_mtx_lock); ++ + reinit_completion(&ar->peer_delete_done); + + ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); + if (ret) { +- ath11k_warn(ar->ab, ++ ath11k_warn(ab, + "failed to delete peer vdev_id %d addr %pM ret %d\n", + vdev_id, addr, ret); + return ret; +@@ -276,7 +370,7 @@ int ath11k_peer_create(struct ath11k *ar + } + + spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find_by_pdev_idx(ar->ab, ar->pdev_idx, param->peer_addr); ++ peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr); + if (peer) { + spin_unlock_bh(&ar->ab->base_lock); + return -EINVAL; +@@ -296,11 +390,13 @@ int ath11k_peer_create(struct ath11k *ar + if (ret) + return ret; + ++ mutex_lock(&ar->ab->tbl_mtx_lock); + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); ++ mutex_unlock(&ar->ab->tbl_mtx_lock); + ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", + param->peer_addr, param->vdev_id); + +@@ -308,6 +404,13 @@ int ath11k_peer_create(struct ath11k *ar + goto cleanup; + } + ++ ret = ath11k_peer_rhash_add(ar->ab, peer); ++ if (ret) { ++ spin_unlock_bh(&ar->ab->base_lock); ++ mutex_unlock(&ar->ab->tbl_mtx_lock); ++ goto cleanup; ++ } ++ + peer->pdev_idx = ar->pdev_idx; + peer->sta = sta; + +@@ -332,6 +435,7 @@ int ath11k_peer_create(struct ath11k *ar + ar->num_peers++; + + spin_unlock_bh(&ar->ab->base_lock); ++ mutex_unlock(&ar->ab->tbl_mtx_lock); + + return 0; + +@@ -343,3 +447,201 @@ cleanup: + + return ret; + } ++ ++int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer) ++{ ++ int ret; ++ ++ lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ if (!ab->rhead_peer_id || !ab->rhead_peer_addr) ++ return -EPERM; ++ ++ ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_addr, &peer->rhash_addr, ++ &ab->rhash_peer_addr_param); ++ if (ret) { ++ ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_addr ret %d\n", ++ peer->addr, peer->peer_id, ret); ++ return ret; ++ } ++ ++ ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id, ++ &ab->rhash_peer_id_param); ++ if (ret) { ++ ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_id ret %d\n", ++ peer->addr, peer->peer_id, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ath11k_peer_rhash_id_tbl_init(struct ath11k_base *ab) ++{ ++ struct rhashtable_params *param; ++ struct rhashtable *rhash_id_tbl; ++ int ret; ++ size_t size; ++ ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ if (ab->rhead_peer_id) ++ return 0; ++ ++ size = sizeof(*ab->rhead_peer_id); ++ rhash_id_tbl = kzalloc(size, GFP_KERNEL); ++ if (!rhash_id_tbl) { ++ ath11k_warn(ab, "failed to init rhash id table due to no mem (size %zu)\n", ++ size); ++ return -ENOMEM; ++ } ++ ++ param = &ab->rhash_peer_id_param; ++ ++ param->key_offset = offsetof(struct ath11k_peer, peer_id); ++ param->head_offset = offsetof(struct ath11k_peer, rhash_id); ++ param->key_len = sizeof_field(struct ath11k_peer, peer_id); ++ param->automatic_shrinking = true; ++ param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab); ++ ++ ret = rhashtable_init(rhash_id_tbl, param); ++ if (ret) { ++ ath11k_warn(ab, "failed to init peer id rhash table %d\n", ret); ++ goto err_free; ++ } ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ if (!ab->rhead_peer_id) { ++ ab->rhead_peer_id = rhash_id_tbl; ++ } else { ++ spin_unlock_bh(&ab->base_lock); ++ goto cleanup_tbl; ++ } ++ ++ spin_unlock_bh(&ab->base_lock); ++ ++ return 0; ++ ++cleanup_tbl: ++ rhashtable_destroy(rhash_id_tbl); ++err_free: ++ kfree(rhash_id_tbl); ++ ++ return ret; ++} ++ ++static int ath11k_peer_rhash_addr_tbl_init(struct ath11k_base *ab) ++{ ++ struct rhashtable_params *param; ++ struct rhashtable *rhash_addr_tbl; ++ int ret; ++ size_t size; ++ ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ if (ab->rhead_peer_addr) ++ return 0; ++ ++ size = sizeof(*ab->rhead_peer_addr); ++ rhash_addr_tbl = kzalloc(size, GFP_KERNEL); ++ if (!rhash_addr_tbl) { ++ ath11k_warn(ab, "failed to init rhash addr table due to no mem (size %zu)\n", ++ size); ++ return -ENOMEM; ++ } ++ ++ param = &ab->rhash_peer_addr_param; ++ ++ param->key_offset = offsetof(struct ath11k_peer, addr); ++ param->head_offset = offsetof(struct ath11k_peer, rhash_addr); ++ param->key_len = sizeof_field(struct ath11k_peer, addr); ++ param->automatic_shrinking = true; ++ param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab); ++ ++ ret = rhashtable_init(rhash_addr_tbl, param); ++ if (ret) { ++ ath11k_warn(ab, "failed to init peer addr rhash table %d\n", ret); ++ goto err_free; ++ } ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ if (!ab->rhead_peer_addr) { ++ ab->rhead_peer_addr = rhash_addr_tbl; ++ } else { ++ spin_unlock_bh(&ab->base_lock); ++ goto cleanup_tbl; ++ } ++ ++ spin_unlock_bh(&ab->base_lock); ++ ++ return 0; ++ ++cleanup_tbl: ++ rhashtable_destroy(rhash_addr_tbl); ++err_free: ++ kfree(rhash_addr_tbl); ++ ++ return ret; ++} ++ ++static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k_base *ab) ++{ ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ if (!ab->rhead_peer_id) ++ return; ++ ++ rhashtable_destroy(ab->rhead_peer_id); ++ kfree(ab->rhead_peer_id); ++ ab->rhead_peer_id = NULL; ++} ++ ++static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k_base *ab) ++{ ++ lockdep_assert_held(&ab->tbl_mtx_lock); ++ ++ if (!ab->rhead_peer_addr) ++ return; ++ ++ rhashtable_destroy(ab->rhead_peer_addr); ++ kfree(ab->rhead_peer_addr); ++ ab->rhead_peer_addr = NULL; ++} ++ ++int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab) ++{ ++ int ret; ++ ++ mutex_lock(&ab->tbl_mtx_lock); ++ ++ ret = ath11k_peer_rhash_id_tbl_init(ab); ++ if (ret) ++ goto out; ++ ++ ret = ath11k_peer_rhash_addr_tbl_init(ab); ++ if (ret) ++ goto cleanup_tbl; ++ ++ mutex_unlock(&ab->tbl_mtx_lock); ++ ++ return 0; ++ ++cleanup_tbl: ++ ath11k_peer_rhash_id_tbl_destroy(ab); ++out: ++ mutex_unlock(&ab->tbl_mtx_lock); ++ return ret; ++} ++ ++void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab) ++{ ++ mutex_lock(&ab->tbl_mtx_lock); ++ ++ ath11k_peer_rhash_addr_tbl_destroy(ab); ++ ath11k_peer_rhash_id_tbl_destroy(ab); ++ ++ mutex_unlock(&ab->tbl_mtx_lock); ++} +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #ifndef ATH11K_PEER_H +@@ -20,6 +21,11 @@ struct ath11k_peer { + struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; + struct dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; + ++ /* peer id based rhashtable list pointer */ ++ struct rhash_head rhash_id; ++ /* peer addr based rhashtable list pointer */ ++ struct rhash_head rhash_addr; ++ + /* Info used in MMIC verification of + * RX fragments + */ +@@ -47,5 +53,7 @@ int ath11k_wait_for_peer_delete_done(str + const u8 *addr); + struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab, + int vdev_id); +- ++int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab); ++void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab); ++int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer); + #endif /* _PEER_H_ */ diff --git a/package/kernel/mac80211/patches/ath11k/0212-ath11k-store-and-send-country-code-to-firmware-after.patch b/package/kernel/mac80211/patches/ath11k/0212-ath11k-store-and-send-country-code-to-firmware-after.patch new file mode 100644 index 00000000000000..3d7f8909be9c98 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0212-ath11k-store-and-send-country-code-to-firmware-after.patch @@ -0,0 +1,70 @@ +From b2beae327e039736299f3c6559f3467323fe68c5 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 23 Mar 2022 21:18:56 -0400 +Subject: [PATCH] ath11k: store and send country code to firmware after + recovery + +Currently ath11k does not send the country code to firmware after device +recovery, as a result the regdomain info is reported from firmware by +default. Regdomain info is important, so ath11k also need to restore +it to the value which was used before recovery. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220324011856.11014-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 8 ++++++++ + drivers/net/wireless/ath/ath11k/reg.c | 1 + + 4 files changed, 11 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1404,6 +1404,7 @@ static void ath11k_update_11d(struct wor + pdev = &ab->pdevs[i]; + ar = pdev->ar; + ++ memcpy(&ar->alpha2, &set_current_param.alpha2, 2); + ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); + if (ret) + ath11k_warn(ar->ab, +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -671,6 +671,7 @@ struct ath11k { + int hw_rate_code; + u8 twt_enabled; + bool nlo_enabled; ++ u8 alpha2[REG_ALPHA2_LEN + 1]; + }; + + struct ath11k_band_cap { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7957,6 +7957,14 @@ ath11k_mac_op_reconfig_complete(struct i + ar->state = ATH11K_STATE_ON; + ieee80211_wake_queues(ar->hw); + ++ if (ar->ab->hw_params.current_cc_support && ++ ar->alpha2[0] != 0 && ar->alpha2[1] != 0) { ++ struct wmi_set_current_country_params set_current_param = {}; ++ ++ memcpy(&set_current_param.alpha2, ar->alpha2, 2); ++ ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); ++ } ++ + if (ab->is_reset) { + recovery_count = atomic_inc_return(&ab->recovery_count); + ath11k_dbg(ab, ATH11K_DBG_BOOT, +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -83,6 +83,7 @@ ath11k_reg_notifier(struct wiphy *wiphy, + */ + if (ar->ab->hw_params.current_cc_support) { + memcpy(&set_current_param.alpha2, request->alpha2, 2); ++ memcpy(&ar->alpha2, &set_current_param.alpha2, 2); + ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); + if (ret) + ath11k_warn(ar->ab, diff --git a/package/kernel/mac80211/patches/ath11k/0213-ath11k-add-support-to-search-regdb-data-in-board-2.b.patch b/package/kernel/mac80211/patches/ath11k/0213-ath11k-add-support-to-search-regdb-data-in-board-2.b.patch new file mode 100644 index 00000000000000..720a6f2b79b705 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0213-ath11k-add-support-to-search-regdb-data-in-board-2.b.patch @@ -0,0 +1,316 @@ +From 801cb1d234288d9bc2afeaa98c7e98f8038b7a6c Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Mon, 28 Mar 2022 14:57:19 +0300 +Subject: [PATCH] ath11k: add support to search regdb data in board-2.bin for + WCN6855 + +Currently ath11k only download the same regdb.bin file for all WCN6855 +chips, actually ath11k needs to distinguish all different WCN6855 chips. + +This is to re-use the string type which include bus, chip id, board id, +vendor, device, subsystem-vendor, subsystem-device and variant for +WCN6855 to distinguish different regdb in board-2.bin. + +ath11k will first load board-2.bin and search in it for the regdb data +with the above parameters, if matched one regdb data, then download it +to firmware, if not matched any one, then ath11k will download the file +regdb.bin to firmware. + +Add enum value ATH11K_BD_IE_REGDB and enum type ath11k_bd_ie_regdb_type +to distinguish regdb data and board data since they are in the same file +board-2.bin. + +This only take effect for WCN6855 which supports regdb in hardware parameters. + +Test log: +[ 3833.091948] ath11k_pci 0000:05:00.0: boot using board name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' +[ 3833.092072] ath11k_pci 0000:05:00.0: boot firmware request ath11k/WCN6855/hw2.0/board-2.bin size 205316 +[ 3833.092079] ath11k_pci 0000:05:00.0: board name +[ 3833.092083] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip +[ 3833.092088] ath11k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 31 -id=1 +[ 3833.092091] ath11k_pci 0000:05:00.0: board name +[ 3833.092095] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip +[ 3833.092099] ath11k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 32 -id=2 +[ 3833.092102] ath11k_pci 0000:05:00.0: board name +[ 3833.092105] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip +[ 3833.092109] ath11k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 33 -id=3 +[ 3833.092112] ath11k_pci 0000:05:00.0: board name +[ 3833.092116] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 +[ 3833.092119] ath11k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, +[ 3833.092123] ath11k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor +[ 3833.092126] ath11k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- +[ 3833.092130] ath11k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- +[ 3833.092133] ath11k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo +[ 3833.092137] ath11k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 36 2c 76 61 72 69 61 ard-id=266,varia +[ 3833.092140] ath11k_pci 0000:05:00.0: 00000070: 6e 74 3d 48 50 5f 47 38 5f 4c 61 6e 63 69 61 31 nt=HP_G8_Lancia1 +[ 3833.092144] ath11k_pci 0000:05:00.0: 00000080: 35 5 +[ 3833.092147] ath11k_pci 0000:05:00.0: board name +[ 3833.092150] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 +[ 3833.092154] ath11k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, +[ 3833.092157] ath11k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor +[ 3833.092161] ath11k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- +[ 3833.092165] ath11k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- +[ 3833.092168] ath11k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo +[ 3833.092172] ath11k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 36 ard-id=266 +[ 3833.092206] ath11k_pci 0000:05:00.0: board name +[ 3833.092209] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 76 65 6e 64 6f 72 3d 31 bus=pci,vendor=1 +[ 3833.092213] ath11k_pci 0000:05:00.0: 00000010: 37 63 62 2c 64 65 76 69 63 65 3d 31 31 30 33 2c 7cb,device=1103, +[ 3833.092216] ath11k_pci 0000:05:00.0: 00000020: 73 75 62 73 79 73 74 65 6d 2d 76 65 6e 64 6f 72 subsystem-vendor +[ 3833.092220] ath11k_pci 0000:05:00.0: 00000030: 3d 31 37 63 62 2c 73 75 62 73 79 73 74 65 6d 2d =17cb,subsystem- +[ 3833.092223] ath11k_pci 0000:05:00.0: 00000040: 64 65 76 69 63 65 3d 33 33 37 34 2c 71 6d 69 2d device=3374,qmi- +[ 3833.092227] ath11k_pci 0000:05:00.0: 00000050: 63 68 69 70 2d 69 64 3d 32 2c 71 6d 69 2d 62 6f chip-id=2,qmi-bo +[ 3833.092230] ath11k_pci 0000:05:00.0: 00000060: 61 72 64 2d 69 64 3d 32 36 32 ard-id=262 +[ 3833.092234] ath11k_pci 0000:05:00.0: boot found match regdb data for name 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' +[ 3833.092238] ath11k_pci 0000:05:00.0: board name +[ 3833.092241] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip +[ 3833.092245] ath11k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 31 31 -id=11 +[ 3833.092248] ath11k_pci 0000:05:00.0: board name +[ 3833.092251] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip +[ 3833.092255] ath11k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 32 32 -id=22 +[ 3833.092258] ath11k_pci 0000:05:00.0: board name +[ 3833.092261] ath11k_pci 0000:05:00.0: 00000000: 62 75 73 3d 70 63 69 2c 71 6d 69 2d 63 68 69 70 bus=pci,qmi-chip +[ 3833.092265] ath11k_pci 0000:05:00.0: 00000010: 2d 69 64 3d 33 33 -id=33 +[ 3833.092268] ath11k_pci 0000:05:00.0: boot found regdb data for 'bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=3374,qmi-chip-id=2,qmi-board-id=262' +[ 3833.092272] ath11k_pci 0000:05:00.0: fetched regdb + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220319023543.14288-4-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 84 ++++++++++++++++++-------- + drivers/net/wireless/ath/ath11k/hw.h | 19 ++++++ + 2 files changed, 78 insertions(+), 25 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -723,7 +723,9 @@ static int ath11k_core_parse_bd_ie_board + struct ath11k_board_data *bd, + const void *buf, size_t buf_len, + const char *boardname, +- int bd_ie_type) ++ int ie_id, ++ int name_id, ++ int data_id) + { + const struct ath11k_fw_ie *hdr; + bool name_match_found; +@@ -733,7 +735,7 @@ static int ath11k_core_parse_bd_ie_board + + name_match_found = false; + +- /* go through ATH11K_BD_IE_BOARD_ elements */ ++ /* go through ATH11K_BD_IE_BOARD_/ATH11K_BD_IE_REGDB_ elements */ + while (buf_len > sizeof(struct ath11k_fw_ie)) { + hdr = buf; + board_ie_id = le32_to_cpu(hdr->id); +@@ -744,48 +746,50 @@ static int ath11k_core_parse_bd_ie_board + buf += sizeof(*hdr); + + if (buf_len < ALIGN(board_ie_len, 4)) { +- ath11k_err(ab, "invalid ATH11K_BD_IE_BOARD length: %zu < %zu\n", ++ ath11k_err(ab, "invalid %s length: %zu < %zu\n", ++ ath11k_bd_ie_type_str(ie_id), + buf_len, ALIGN(board_ie_len, 4)); + ret = -EINVAL; + goto out; + } + +- switch (board_ie_id) { +- case ATH11K_BD_IE_BOARD_NAME: ++ if (board_ie_id == name_id) { + ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "board name", "", + board_ie_data, board_ie_len); + + if (board_ie_len != strlen(boardname)) +- break; ++ goto next; + + ret = memcmp(board_ie_data, boardname, strlen(boardname)); + if (ret) +- break; ++ goto next; + + name_match_found = true; + ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "boot found match for name '%s'", ++ "boot found match %s for name '%s'", ++ ath11k_bd_ie_type_str(ie_id), + boardname); +- break; +- case ATH11K_BD_IE_BOARD_DATA: ++ } else if (board_ie_id == data_id) { + if (!name_match_found) + /* no match found */ +- break; ++ goto next; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "boot found board data for '%s'", boardname); ++ "boot found %s for '%s'", ++ ath11k_bd_ie_type_str(ie_id), ++ boardname); + + bd->data = board_ie_data; + bd->len = board_ie_len; + + ret = 0; + goto out; +- default: +- ath11k_warn(ab, "unknown ATH11K_BD_IE_BOARD found: %d\n", ++ } else { ++ ath11k_warn(ab, "unknown %s id found: %d\n", ++ ath11k_bd_ie_type_str(ie_id), + board_ie_id); +- break; + } +- ++next: + /* jump over the padding */ + board_ie_len = ALIGN(board_ie_len, 4); + +@@ -802,7 +806,10 @@ out: + + static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, + struct ath11k_board_data *bd, +- const char *boardname) ++ const char *boardname, ++ int ie_id_match, ++ int name_id, ++ int data_id) + { + size_t len, magic_len; + const u8 *data; +@@ -867,22 +874,23 @@ static int ath11k_core_fetch_board_data_ + goto err; + } + +- switch (ie_id) { +- case ATH11K_BD_IE_BOARD: ++ if (ie_id == ie_id_match) { + ret = ath11k_core_parse_bd_ie_board(ab, bd, data, + ie_len, + boardname, +- ATH11K_BD_IE_BOARD); ++ ie_id_match, ++ name_id, ++ data_id); + if (ret == -ENOENT) + /* no match found, continue */ +- break; ++ goto next; + else if (ret) + /* there was an error, bail out */ + goto err; + /* either found or error, so stop searching */ + goto out; + } +- ++next: + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + +@@ -893,7 +901,8 @@ static int ath11k_core_fetch_board_data_ + out: + if (!bd->data || !bd->len) { + ath11k_dbg(ab, ATH11K_DBG_BOOT, +- "failed to fetch board data for %s from %s\n", ++ "failed to fetch %s for %s from %s\n", ++ ath11k_bd_ie_type_str(ie_id_match), + boardname, filepath); + ret = -ENODATA; + goto err; +@@ -937,7 +946,10 @@ int ath11k_core_fetch_bdf(struct ath11k_ + } + + ab->bd_api = 2; +- ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname); ++ ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname, ++ ATH11K_BD_IE_BOARD, ++ ATH11K_BD_IE_BOARD_NAME, ++ ATH11K_BD_IE_BOARD_DATA); + if (!ret) + goto success; + +@@ -948,7 +960,10 @@ int ath11k_core_fetch_bdf(struct ath11k_ + return ret; + } + +- ret = ath11k_core_fetch_board_data_api_n(ab, bd, fallback_boardname); ++ ret = ath11k_core_fetch_board_data_api_n(ab, bd, fallback_boardname, ++ ATH11K_BD_IE_BOARD, ++ ATH11K_BD_IE_BOARD_NAME, ++ ATH11K_BD_IE_BOARD_DATA); + if (!ret) + goto success; + +@@ -975,13 +990,32 @@ success: + + int ath11k_core_fetch_regdb(struct ath11k_base *ab, struct ath11k_board_data *bd) + { ++ char boardname[BOARD_NAME_SIZE]; + int ret; + ++ ret = ath11k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); ++ if (ret) { ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, ++ "failed to create board name for regdb: %d", ret); ++ goto exit; ++ } ++ ++ ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname, ++ ATH11K_BD_IE_REGDB, ++ ATH11K_BD_IE_REGDB_NAME, ++ ATH11K_BD_IE_REGDB_DATA); ++ if (!ret) ++ goto exit; ++ + ret = ath11k_core_fetch_board_data_api_1(ab, bd, ATH11K_REGDB_FILE_NAME); + if (ret) + ath11k_dbg(ab, ATH11K_DBG_BOOT, "failed to fetch %s from %s\n", + ATH11K_REGDB_FILE_NAME, ab->hw_params.fw.dir); + ++exit: ++ if (!ret) ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "fetched regdb\n"); ++ + return ret; + } + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -291,9 +291,16 @@ enum ath11k_bd_ie_board_type { + ATH11K_BD_IE_BOARD_DATA = 1, + }; + ++enum ath11k_bd_ie_regdb_type { ++ ATH11K_BD_IE_REGDB_NAME = 0, ++ ATH11K_BD_IE_REGDB_DATA = 1, ++}; ++ + enum ath11k_bd_ie_type { + /* contains sub IEs of enum ath11k_bd_ie_board_type */ + ATH11K_BD_IE_BOARD = 0, ++ /* contains sub IEs of enum ath11k_bd_ie_regdb_type */ ++ ATH11K_BD_IE_REGDB = 1, + }; + + struct ath11k_hw_regs { +@@ -361,4 +368,16 @@ extern const struct ath11k_hw_regs qca63 + extern const struct ath11k_hw_regs qcn9074_regs; + extern const struct ath11k_hw_regs wcn6855_regs; + ++static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type) ++{ ++ switch (type) { ++ case ATH11K_BD_IE_BOARD: ++ return "board data"; ++ case ATH11K_BD_IE_REGDB: ++ return "regdb data"; ++ } ++ ++ return "unknown"; ++} ++ + #endif diff --git a/package/kernel/mac80211/patches/ath11k/0214-ath11k-reduce-the-wait-time-of-11d-scan-and-hw-scan-.patch b/package/kernel/mac80211/patches/ath11k/0214-ath11k-reduce-the-wait-time-of-11d-scan-and-hw-scan-.patch new file mode 100644 index 00000000000000..5baf0c021a2bca --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0214-ath11k-reduce-the-wait-time-of-11d-scan-and-hw-scan-.patch @@ -0,0 +1,400 @@ +From 1f682dc9fb3790aa7ec27d3d122ff32b1eda1365 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Sun, 27 Mar 2022 23:58:32 -0400 +Subject: [PATCH] ath11k: reduce the wait time of 11d scan and hw scan while + add interface + +Currently ath11k will wait 11d scan complete while add interface in +ath11k_mac_op_add_interface(), when system resume without enable +wowlan, ath11k_mac_op_add_interface() is called for each resume, thus +it increase the resume time of system. And ath11k_mac_op_hw_scan() +after ath11k_mac_op_add_interface() also needs some time cost because +the previous 11d scan need more than 5 seconds when 6 GHz is enabled, +then the scan started event will indicated to ath11k after the 11d +scan completed. + +While 11d scan/hw scan is running in firmware, if ath11k update channel +list to firmware by WMI_SCAN_CHAN_LIST_CMDID, then firmware will cancel +the current scan which is running, it lead the scan failed. The patch +commit 9dcf6808b253 ("ath11k: add 11d scan offload support") used +finish_11d_scan/finish_11d_ch_list/pending_11d to synchronize the 11d +scan/hw scan/channel list between ath11k/firmware/mac80211 and to avoid +the scan fail. + +Add wait operation before ath11k update channel list, function +ath11k_reg_update_chan_list() will wait until the current 11d scan/hw +scan completed. And remove the wait operation of start 11d scan and +waiting channel list complete in hw scan. After these changes, resume +time cost reduce about 5 seconds and also hw scan time cost reduced +obviously, and scan failed not seen. + +The 11d scan is sent to firmware only one time for each interface added +in mac.c, and it is moved after the 1st hw scan because 11d scan will +cost some time and thus leads the AP scan result update to UI delay. +Currently priority of ath11k's hw scan is WMI_SCAN_PRIORITY_LOW, and +priority of 11d scan in firmware is WMI_SCAN_PRIORITY_MEDIUM, then the +11d scan which sent after hw scan will cancel the hw scan in firmware, +so change the priority to WMI_SCAN_PRIORITY_MEDIUM for the hw scan which +is in front of the 11d scan, thus it will not happen scan cancel in +firmware. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Fixes: 9dcf6808b253 ("ath11k: add 11d scan offload support") +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220328035832.14122-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/core.h | 13 +++-- + drivers/net/wireless/ath/ath11k/mac.c | 71 +++++++++++--------------- + drivers/net/wireless/ath/ath11k/mac.h | 2 +- + drivers/net/wireless/ath/ath11k/reg.c | 43 ++++++++++------ + drivers/net/wireless/ath/ath11k/reg.h | 2 +- + drivers/net/wireless/ath/ath11k/wmi.c | 16 +++++- + 7 files changed, 84 insertions(+), 64 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1465,6 +1465,7 @@ static void ath11k_core_pre_reconfigure_ + + ieee80211_stop_queues(ar->hw); + ath11k_mac_drain_tx(ar); ++ complete(&ar->completed_11d_scan); + complete(&ar->scan.started); + complete(&ar->scan.completed); + complete(&ar->peer_assoc_done); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -52,6 +52,8 @@ + + extern unsigned int ath11k_frame_mode; + ++#define ATH11K_SCAN_TIMEOUT_HZ (20 * HZ) ++ + #define ATH11K_MON_TIMER_INTERVAL 10 + #define ATH11K_RESET_TIMEOUT_HZ (20 * HZ) + #define ATH11K_RESET_MAX_FAIL_COUNT_FIRST 3 +@@ -216,6 +218,12 @@ enum ath11k_scan_state { + ATH11K_SCAN_ABORTING, + }; + ++enum ath11k_11d_state { ++ ATH11K_11D_IDLE, ++ ATH11K_11D_PREPARING, ++ ATH11K_11D_RUNNING, ++}; ++ + enum ath11k_dev_flags { + ATH11K_CAC_RUNNING, + ATH11K_FLAG_CORE_REGISTERED, +@@ -664,9 +672,8 @@ struct ath11k { + bool dfs_block_radar_events; + struct ath11k_thermal thermal; + u32 vdev_id_11d_scan; +- struct completion finish_11d_scan; +- struct completion finish_11d_ch_list; +- bool pending_11d; ++ struct completion completed_11d_scan; ++ enum ath11k_11d_state state_11d; + bool regdom_set_by_user; + int hw_rate_code; + u8 twt_enabled; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -3621,26 +3621,6 @@ static int ath11k_mac_op_hw_scan(struct + if (ret) + goto exit; + +- /* Currently the pending_11d=true only happened 1 time while +- * wlan interface up in ath11k_mac_11d_scan_start(), it is called by +- * ath11k_mac_op_add_interface(), after wlan interface up, +- * pending_11d=false always. +- * If remove below wait, it always happened scan fail and lead connect +- * fail while wlan interface up, because it has a 11d scan which is running +- * in firmware, and lead this scan failed. +- */ +- if (ar->pending_11d) { +- long time_left; +- unsigned long timeout = 5 * HZ; +- +- if (ar->supports_6ghz) +- timeout += 5 * HZ; +- +- time_left = wait_for_completion_timeout(&ar->finish_11d_ch_list, timeout); +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, +- "mac wait 11d channel list time left %ld\n", time_left); +- } +- + memset(&arg, 0, sizeof(arg)); + ath11k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; +@@ -3706,6 +3686,10 @@ exit: + kfree(arg.extraie.ptr); + + mutex_unlock(&ar->conf_mutex); ++ ++ if (ar->state_11d == ATH11K_11D_PREPARING) ++ ath11k_mac_11d_scan_start(ar, arvif->vdev_id); ++ + return ret; + } + +@@ -5859,7 +5843,7 @@ static int ath11k_mac_op_start(struct ie + + /* TODO: Do we need to enable ANI? */ + +- ath11k_reg_update_chan_list(ar); ++ ath11k_reg_update_chan_list(ar, false); + + ar->num_started_vdevs = 0; + ar->num_created_vdevs = 0; +@@ -5926,6 +5910,11 @@ static void ath11k_mac_op_stop(struct ie + cancel_work_sync(&ar->ab->update_11d_work); + cancel_work_sync(&ar->ab->rfkill_work); + ++ if (ar->state_11d == ATH11K_11D_PREPARING) { ++ ar->state_11d = ATH11K_11D_IDLE; ++ complete(&ar->completed_11d_scan); ++ } ++ + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { + list_del(&ppdu_stats->list); +@@ -6096,7 +6085,7 @@ static bool ath11k_mac_vif_ap_active_any + return false; + } + +-void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id, bool wait) ++void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id) + { + struct wmi_11d_scan_start_params param; + int ret; +@@ -6124,28 +6113,22 @@ void ath11k_mac_11d_scan_start(struct at + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac start 11d scan\n"); + +- if (wait) +- reinit_completion(&ar->finish_11d_scan); +- + ret = ath11k_wmi_send_11d_scan_start_cmd(ar, ¶m); + if (ret) { + ath11k_warn(ar->ab, "failed to start 11d scan vdev %d ret: %d\n", + vdev_id, ret); + } else { + ar->vdev_id_11d_scan = vdev_id; +- if (wait) { +- ar->pending_11d = true; +- ret = wait_for_completion_timeout(&ar->finish_11d_scan, +- 5 * HZ); +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, +- "mac 11d scan left time %d\n", ret); +- +- if (!ret) +- ar->pending_11d = false; +- } ++ if (ar->state_11d == ATH11K_11D_PREPARING) ++ ar->state_11d = ATH11K_11D_RUNNING; + } + + fin: ++ if (ar->state_11d == ATH11K_11D_PREPARING) { ++ ar->state_11d = ATH11K_11D_IDLE; ++ complete(&ar->completed_11d_scan); ++ } ++ + mutex_unlock(&ar->ab->vdev_id_11d_lock); + } + +@@ -6168,12 +6151,15 @@ void ath11k_mac_11d_scan_stop(struct ath + vdev_id = ar->vdev_id_11d_scan; + + ret = ath11k_wmi_send_11d_scan_stop_cmd(ar, vdev_id); +- if (ret) ++ if (ret) { + ath11k_warn(ar->ab, + "failed to stopt 11d scan vdev %d ret: %d\n", + vdev_id, ret); +- else ++ } else { + ar->vdev_id_11d_scan = ATH11K_11D_INVALID_VDEV_ID; ++ ar->state_11d = ATH11K_11D_IDLE; ++ complete(&ar->completed_11d_scan); ++ } + } + mutex_unlock(&ar->ab->vdev_id_11d_lock); + } +@@ -6369,8 +6355,10 @@ static int ath11k_mac_op_add_interface(s + goto err_peer_del; + } + +- ath11k_mac_11d_scan_start(ar, arvif->vdev_id, true); +- ++ if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map)) { ++ reinit_completion(&ar->completed_11d_scan); ++ ar->state_11d = ATH11K_11D_PREPARING; ++ } + break; + case WMI_VDEV_TYPE_MONITOR: + set_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); +@@ -7230,7 +7218,7 @@ ath11k_mac_op_unassign_vif_chanctx(struc + } + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA) +- ath11k_mac_11d_scan_start(ar, arvif->vdev_id, false); ++ ath11k_mac_11d_scan_start(ar, arvif->vdev_id); + + mutex_unlock(&ar->conf_mutex); + } +@@ -8920,8 +8908,7 @@ int ath11k_mac_allocate(struct ath11k_ba + ar->monitor_vdev_id = -1; + clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); + ar->vdev_id_11d_scan = ATH11K_11D_INVALID_VDEV_ID; +- init_completion(&ar->finish_11d_scan); +- init_completion(&ar->finish_11d_ch_list); ++ init_completion(&ar->completed_11d_scan); + } + + return 0; +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -130,7 +130,7 @@ extern const struct htt_rx_ring_tlv_filt + #define ATH11K_SCAN_11D_INTERVAL 600000 + #define ATH11K_11D_INVALID_VDEV_ID 0xFFFF + +-void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id, bool wait); ++void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id); + void ath11k_mac_11d_scan_stop(struct ath11k *ar); + void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab); + +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -103,7 +103,7 @@ ath11k_reg_notifier(struct wiphy *wiphy, + ar->regdom_set_by_user = true; + } + +-int ath11k_reg_update_chan_list(struct ath11k *ar) ++int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) + { + struct ieee80211_supported_band **bands; + struct scan_chan_list_params *params; +@@ -112,7 +112,32 @@ int ath11k_reg_update_chan_list(struct a + struct channel_param *ch; + enum nl80211_band band; + int num_channels = 0; +- int i, ret; ++ int i, ret, left; ++ ++ if (wait && ar->state_11d != ATH11K_11D_IDLE) { ++ left = wait_for_completion_timeout(&ar->completed_11d_scan, ++ ATH11K_SCAN_TIMEOUT_HZ); ++ if (!left) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "failed to receive 11d scan complete: timed out\n"); ++ ar->state_11d = ATH11K_11D_IDLE; ++ } ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "reg 11d scan wait left time %d\n", left); ++ } ++ ++ if (wait && ++ (ar->scan.state == ATH11K_SCAN_STARTING || ++ ar->scan.state == ATH11K_SCAN_RUNNING)) { ++ left = wait_for_completion_timeout(&ar->scan.completed, ++ ATH11K_SCAN_TIMEOUT_HZ); ++ if (!left) ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "failed to receive hw scan complete: timed out\n"); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "reg hw scan wait left time %d\n", left); ++ } + + bands = hw->wiphy->bands; + for (band = 0; band < NUM_NL80211_BANDS; band++) { +@@ -194,11 +219,6 @@ int ath11k_reg_update_chan_list(struct a + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); + kfree(params); + +- if (ar->pending_11d) { +- complete(&ar->finish_11d_ch_list); +- ar->pending_11d = false; +- } +- + return ret; + } + +@@ -264,15 +284,8 @@ int ath11k_regd_update(struct ath11k *ar + goto err; + } + +- if (ar->pending_11d) +- complete(&ar->finish_11d_scan); +- + rtnl_lock(); + wiphy_lock(ar->hw->wiphy); +- +- if (ar->pending_11d) +- reinit_completion(&ar->finish_11d_ch_list); +- + ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy); + wiphy_unlock(ar->hw->wiphy); + rtnl_unlock(); +@@ -283,7 +296,7 @@ int ath11k_regd_update(struct ath11k *ar + goto err; + + if (ar->state == ATH11K_STATE_ON) { +- ret = ath11k_reg_update_chan_list(ar); ++ ret = ath11k_reg_update_chan_list(ar, true); + if (ret) + goto err; + } +--- a/drivers/net/wireless/ath/ath11k/reg.h ++++ b/drivers/net/wireless/ath/ath11k/reg.h +@@ -32,5 +32,5 @@ struct ieee80211_regdomain * + ath11k_reg_build_regd(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, bool intersect); + int ath11k_regd_update(struct ath11k *ar); +-int ath11k_reg_update_chan_list(struct ath11k *ar); ++int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait); + #endif +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -2015,7 +2015,10 @@ void ath11k_wmi_start_scan_init(struct a + { + /* setup commonly used values */ + arg->scan_req_id = 1; +- arg->scan_priority = WMI_SCAN_PRIORITY_LOW; ++ if (ar->state_11d == ATH11K_11D_PREPARING) ++ arg->scan_priority = WMI_SCAN_PRIORITY_MEDIUM; ++ else ++ arg->scan_priority = WMI_SCAN_PRIORITY_LOW; + arg->dwell_time_active = 50; + arg->dwell_time_active_2g = 0; + arg->dwell_time_passive = 150; +@@ -6350,8 +6353,10 @@ static void ath11k_wmi_op_ep_tx_credits( + static int ath11k_reg_11d_new_cc_event(struct ath11k_base *ab, struct sk_buff *skb) + { + const struct wmi_11d_new_cc_ev *ev; ++ struct ath11k *ar; ++ struct ath11k_pdev *pdev; + const void **tb; +- int ret; ++ int ret, i; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { +@@ -6377,6 +6382,13 @@ static int ath11k_reg_11d_new_cc_event(s + + kfree(tb); + ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ ar->state_11d = ATH11K_11D_IDLE; ++ complete(&ar->completed_11d_scan); ++ } ++ + queue_work(ab->workqueue, &ab->update_11d_work); + + return 0; diff --git a/package/kernel/mac80211/patches/ath11k/0215-ath11k-PCI-changes-to-support-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0215-ath11k-PCI-changes-to-support-WCN6750.patch new file mode 100644 index 00000000000000..3792748ab6e484 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0215-ath11k-PCI-changes-to-support-WCN6750.patch @@ -0,0 +1,388 @@ +From 948171b5f6fcf11253355bd836e6e8b613bea12f Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 1 Apr 2022 14:53:08 +0300 +Subject: [PATCH] ath11k: PCI changes to support WCN6750 + +In order to add the support for WCN6750 in ATH11K , it is +required to move certain PCI definitions to the header file. +As a result, add ATH11K_PCI_* prefix to these definitions. + +Also, change the scope of certain PCI APIs that are required +to enable WCN6750 from static to global. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220328055714.6449-2-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/pci.c | 110 ++++++++++++-------------- + drivers/net/wireless/ath/ath11k/pci.h | 35 ++++++++ + 2 files changed, 84 insertions(+), 61 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -17,25 +18,10 @@ + #define ATH11K_PCI_BAR_NUM 0 + #define ATH11K_PCI_DMA_MASK 32 + +-#define ATH11K_PCI_IRQ_CE0_OFFSET 3 +-#define ATH11K_PCI_IRQ_DP_OFFSET 14 +- +-#define WINDOW_ENABLE_BIT 0x40000000 +-#define WINDOW_REG_ADDRESS 0x310c +-#define WINDOW_VALUE_MASK GENMASK(24, 19) +-#define WINDOW_START 0x80000 +-#define WINDOW_RANGE_MASK GENMASK(18, 0) +- + #define TCSR_SOC_HW_VERSION 0x0224 + #define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8) + #define TCSR_SOC_HW_VERSION_MINOR_MASK GENMASK(7, 0) + +-/* BAR0 + 4k is always accessible, and no +- * need to force wakeup. +- * 4K - 32 = 0xFE0 +- */ +-#define ACCESS_ALWAYS_OFF 0xFE0 +- + #define QCA6390_DEVICE_ID 0x1101 + #define QCN9074_DEVICE_ID 0x1104 + #define WCN6855_DEVICE_ID 0x1103 +@@ -147,27 +133,30 @@ static inline void ath11k_pci_select_win + { + struct ath11k_base *ab = ab_pci->ab; + +- u32 window = FIELD_GET(WINDOW_VALUE_MASK, offset); ++ u32 window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, offset); + + lockdep_assert_held(&ab_pci->window_lock); + + if (window != ab_pci->register_window) { +- iowrite32(WINDOW_ENABLE_BIT | window, +- ab->mem + WINDOW_REG_ADDRESS); +- ioread32(ab->mem + WINDOW_REG_ADDRESS); ++ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, ++ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++ ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); + ab_pci->register_window = window; + } + } + + static inline void ath11k_pci_select_static_window(struct ath11k_pci *ab_pci) + { +- u32 umac_window = FIELD_GET(WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET); +- u32 ce_window = FIELD_GET(WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE); ++ u32 umac_window; ++ u32 ce_window; + u32 window; + ++ umac_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET); ++ ce_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE); + window = (umac_window << 12) | (ce_window << 6); + +- iowrite32(WINDOW_ENABLE_BIT | window, ab_pci->ab->mem + WINDOW_REG_ADDRESS); ++ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, ++ ab_pci->ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); + } + + static inline u32 ath11k_pci_get_window_start(struct ath11k_base *ab, +@@ -176,13 +165,13 @@ static inline u32 ath11k_pci_get_window_ + u32 window_start; + + /* If offset lies within DP register range, use 3rd window */ +- if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < WINDOW_RANGE_MASK) +- window_start = 3 * WINDOW_START; ++ if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK) ++ window_start = 3 * ATH11K_PCI_WINDOW_START; + /* If offset lies within CE register range, use 2nd window */ +- else if ((offset ^ HAL_CE_WFSS_CE_REG_BASE) < WINDOW_RANGE_MASK) +- window_start = 2 * WINDOW_START; ++ else if ((offset ^ HAL_CE_WFSS_CE_REG_BASE) < ATH11K_PCI_WINDOW_RANGE_MASK) ++ window_start = 2 * ATH11K_PCI_WINDOW_START; + else +- window_start = WINDOW_START; ++ window_start = ATH11K_PCI_WINDOW_START; + + return window_start; + } +@@ -198,32 +187,32 @@ void ath11k_pci_write32(struct ath11k_ba + */ + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ACCESS_ALWAYS_OFF) ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) + ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + +- if (offset < WINDOW_START) { ++ if (offset < ATH11K_PCI_WINDOW_START) { + iowrite32(value, ab->mem + offset); + } else { + if (ab->bus_params.static_window_map) + window_start = ath11k_pci_get_window_start(ab, offset); + else +- window_start = WINDOW_START; ++ window_start = ATH11K_PCI_WINDOW_START; + +- if (window_start == WINDOW_START) { ++ if (window_start == ATH11K_PCI_WINDOW_START) { + spin_lock_bh(&ab_pci->window_lock); + ath11k_pci_select_window(ab_pci, offset); + iowrite32(value, ab->mem + window_start + +- (offset & WINDOW_RANGE_MASK)); ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + spin_unlock_bh(&ab_pci->window_lock); + } else { + iowrite32(value, ab->mem + window_start + +- (offset & WINDOW_RANGE_MASK)); ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + } + } + + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ACCESS_ALWAYS_OFF && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && + !ret) + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); + } +@@ -239,32 +228,32 @@ u32 ath11k_pci_read32(struct ath11k_base + */ + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ACCESS_ALWAYS_OFF) ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) + ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + +- if (offset < WINDOW_START) { ++ if (offset < ATH11K_PCI_WINDOW_START) { + val = ioread32(ab->mem + offset); + } else { + if (ab->bus_params.static_window_map) + window_start = ath11k_pci_get_window_start(ab, offset); + else +- window_start = WINDOW_START; ++ window_start = ATH11K_PCI_WINDOW_START; + +- if (window_start == WINDOW_START) { ++ if (window_start == ATH11K_PCI_WINDOW_START) { + spin_lock_bh(&ab_pci->window_lock); + ath11k_pci_select_window(ab_pci, offset); + val = ioread32(ab->mem + window_start + +- (offset & WINDOW_RANGE_MASK)); ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + spin_unlock_bh(&ab_pci->window_lock); + } else { + val = ioread32(ab->mem + window_start + +- (offset & WINDOW_RANGE_MASK)); ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + } + } + + if (ab->hw_params.wakeup_mhi && + test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ACCESS_ALWAYS_OFF && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && + !ret) + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); + +@@ -474,8 +463,8 @@ int ath11k_pci_get_msi_irq(struct device + return pci_irq_vector(pci_dev, vector); + } + +-static void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, +- u32 *msi_addr_hi) ++void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, ++ u32 *msi_addr_hi) + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct pci_dev *pci_dev = to_pci_dev(ab->dev); +@@ -519,8 +508,7 @@ int ath11k_pci_get_user_msi_assignment(s + return -EINVAL; + } + +-static void ath11k_pci_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, +- u32 *msi_idx) ++void ath11k_pci_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx) + { + u32 i, msi_data_idx; + +@@ -536,9 +524,9 @@ static void ath11k_pci_get_ce_msi_idx(st + *msi_idx = msi_data_idx; + } + +-static int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, +- int *num_vectors, u32 *user_base_data, +- u32 *base_vector) ++int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, ++ int *num_vectors, u32 *user_base_data, ++ u32 *base_vector) + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + +@@ -561,7 +549,7 @@ static void ath11k_pci_free_ext_irq(stru + } + } + +-static void ath11k_pci_free_irq(struct ath11k_base *ab) ++void ath11k_pci_free_irq(struct ath11k_base *ab) + { + int i, irq_idx; + +@@ -710,7 +698,7 @@ static void ath11k_pci_ext_grp_enable(st + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); + } + +-static void ath11k_pci_ext_irq_enable(struct ath11k_base *ab) ++void ath11k_pci_ext_irq_enable(struct ath11k_base *ab) + { + int i; + +@@ -741,7 +729,7 @@ static void ath11k_pci_sync_ext_irqs(str + } + } + +-static void ath11k_pci_ext_irq_disable(struct ath11k_base *ab) ++void ath11k_pci_ext_irq_disable(struct ath11k_base *ab) + { + __ath11k_pci_ext_irq_disable(ab); + ath11k_pci_sync_ext_irqs(ab); +@@ -854,8 +842,8 @@ static int ath11k_pci_ext_irq_config(str + return 0; + } + +-static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, +- const struct cpumask *m) ++int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, ++ const struct cpumask *m) + { + if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) + return 0; +@@ -863,7 +851,7 @@ static int ath11k_pci_set_irq_affinity_h + return irq_set_affinity_hint(ab_pci->pdev->irq, m); + } + +-static int ath11k_pci_config_irq(struct ath11k_base *ab) ++int ath11k_pci_config_irq(struct ath11k_base *ab) + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct ath11k_ce_pipe *ce_pipe; +@@ -939,7 +927,7 @@ static void ath11k_pci_init_qmi_ce_confi + &cfg->shadow_reg_v2_len); + } + +-static void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab) ++void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab) + { + int i; + +@@ -1151,7 +1139,7 @@ static void ath11k_pci_aspm_disable(stru + set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags); + } + +-static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) ++void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) + { + if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) + pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL, +@@ -1234,20 +1222,20 @@ static void ath11k_pci_kill_tasklets(str + } + } + +-static void ath11k_pci_ce_irq_disable_sync(struct ath11k_base *ab) ++void ath11k_pci_ce_irq_disable_sync(struct ath11k_base *ab) + { + ath11k_pci_ce_irqs_disable(ab); + ath11k_pci_sync_ce_irqs(ab); + ath11k_pci_kill_tasklets(ab); + } + +-static void ath11k_pci_stop(struct ath11k_base *ab) ++void ath11k_pci_stop(struct ath11k_base *ab) + { + ath11k_pci_ce_irq_disable_sync(ab); + ath11k_ce_cleanup_pipes(ab); + } + +-static int ath11k_pci_start(struct ath11k_base *ab) ++int ath11k_pci_start(struct ath11k_base *ab) + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + +@@ -1277,8 +1265,8 @@ static void ath11k_pci_hif_ce_irq_disabl + ath11k_pci_ce_irq_disable_sync(ab); + } + +-static int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, +- u8 *ul_pipe, u8 *dl_pipe) ++int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, ++ u8 *ul_pipe, u8 *dl_pipe) + { + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; +--- a/drivers/net/wireless/ath/ath11k/pci.h ++++ b/drivers/net/wireless/ath/ath11k/pci.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + #ifndef _ATH11K_PCI_H + #define _ATH11K_PCI_H +@@ -52,6 +53,21 @@ + #define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c + #define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4 + ++#define ATH11K_PCI_IRQ_CE0_OFFSET 3 ++#define ATH11K_PCI_IRQ_DP_OFFSET 14 ++ ++#define ATH11K_PCI_WINDOW_ENABLE_BIT 0x40000000 ++#define ATH11K_PCI_WINDOW_REG_ADDRESS 0x310c ++#define ATH11K_PCI_WINDOW_VALUE_MASK GENMASK(24, 19) ++#define ATH11K_PCI_WINDOW_START 0x80000 ++#define ATH11K_PCI_WINDOW_RANGE_MASK GENMASK(18, 0) ++ ++/* BAR0 + 4k is always accessible, and no ++ * need to force wakeup. ++ * 4K - 32 = 0xFE0 ++ */ ++#define ATH11K_PCI_ACCESS_ALWAYS_OFF 0xFE0 ++ + struct ath11k_msi_user { + char *name; + int num_vectors; +@@ -103,5 +119,24 @@ int ath11k_pci_get_user_msi_assignment(s + int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector); + void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value); + u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset); ++void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, ++ u32 *msi_addr_hi); ++void ath11k_pci_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); ++void ath11k_pci_free_irq(struct ath11k_base *ab); ++int ath11k_pci_config_irq(struct ath11k_base *ab); ++void ath11k_pci_ext_irq_enable(struct ath11k_base *ab); ++void ath11k_pci_ext_irq_disable(struct ath11k_base *ab); ++void ath11k_pci_stop(struct ath11k_base *ab); ++int ath11k_pci_start(struct ath11k_base *ab); ++int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, ++ u8 *ul_pipe, u8 *dl_pipe); ++void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab); ++void ath11k_pci_ce_irq_disable_sync(struct ath11k_base *ab); ++int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, ++ int *num_vectors, u32 *user_base_data, ++ u32 *base_vector); ++void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci); ++int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, ++ const struct cpumask *m); + + #endif diff --git a/package/kernel/mac80211/patches/ath11k/0216-ath11k-Refactor-PCI-code-to-support-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0216-ath11k-Refactor-PCI-code-to-support-WCN6750.patch new file mode 100644 index 00000000000000..bc3b1673117044 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0216-ath11k-Refactor-PCI-code-to-support-WCN6750.patch @@ -0,0 +1,2010 @@ +From bbfdc5a751a634fcdaae669cc98b3d0e1dc0eedf Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 1 Apr 2022 14:53:08 +0300 +Subject: [PATCH] ath11k: Refactor PCI code to support WCN6750 + +Unlike other ATH11K PCIe devices which are enumerated by APSS +processor (Application Processor SubSystem), WCN6750 gets +enumerated by the WPSS Q6 processor (Wireless Processor SubSystem); +In simple terms, though WCN6750 is PCIe device, it is not attached +to the APSS processor, APSS will not know of such a device being +present in the system and therefore WCN6750 will be registered as +a platform device to the kernel core like other supported AHB +devices. + +WCN6750 needs both AHB and PCI APIs for it's operation, it uses +AHB APIs for device probe/boot and PCI APIs for device setup and +register accesses. Because of this nature, it is referred as a +hybrid bus device. + +Refactor PCI code to support hybrid bus devices like WCN6750. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220328055714.6449-3-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/Makefile | 2 +- + drivers/net/wireless/ath/ath11k/mhi.c | 27 +- + drivers/net/wireless/ath/ath11k/pci.c | 816 ++--------------------- + drivers/net/wireless/ath/ath11k/pci.h | 41 -- + drivers/net/wireless/ath/ath11k/pcic.c | 747 +++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/pcic.h | 53 ++ + 6 files changed, 856 insertions(+), 830 deletions(-) + create mode 100644 drivers/net/wireless/ath/ath11k/pcic.c + create mode 100644 drivers/net/wireless/ath/ath11k/pcic.h + +--- a/drivers/net/wireless/ath/ath11k/Makefile ++++ b/drivers/net/wireless/ath/ath11k/Makefile +@@ -29,7 +29,7 @@ obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o + ath11k_ahb-y += ahb.o + + obj-$(CPTCFG_ATH11K_PCI) += ath11k_pci.o +-ath11k_pci-y += mhi.o pci.o ++ath11k_pci-y += mhi.o pci.o pcic.o + + # for tracing framework to find trace.h + CFLAGS_trace.o := -I$(src) +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -1,5 +1,8 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear +-/* Copyright (c) 2020 The Linux Foundation. All rights reserved. */ ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. ++ */ + + #include + #include +@@ -11,6 +14,7 @@ + #include "debug.h" + #include "mhi.h" + #include "pci.h" ++#include "pcic.h" + + #define MHI_TIMEOUT_DEFAULT_MS 90000 + #define RDDM_DUMP_SIZE 0x420000 +@@ -205,7 +209,7 @@ void ath11k_mhi_set_mhictrl_reset(struct + { + u32 val; + +- val = ath11k_pci_read32(ab, MHISTATUS); ++ val = ath11k_pcic_read32(ab, MHISTATUS); + + ath11k_dbg(ab, ATH11K_DBG_PCI, "MHISTATUS 0x%x\n", val); + +@@ -213,29 +217,29 @@ void ath11k_mhi_set_mhictrl_reset(struct + * has SYSERR bit set and thus need to set MHICTRL_RESET + * to clear SYSERR. + */ +- ath11k_pci_write32(ab, MHICTRL, MHICTRL_RESET_MASK); ++ ath11k_pcic_write32(ab, MHICTRL, MHICTRL_RESET_MASK); + + mdelay(10); + } + + static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab) + { +- ath11k_pci_write32(ab, PCIE_TXVECDB, 0); ++ ath11k_pcic_write32(ab, PCIE_TXVECDB, 0); + } + + static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab) + { +- ath11k_pci_write32(ab, PCIE_TXVECSTATUS, 0); ++ ath11k_pcic_write32(ab, PCIE_TXVECSTATUS, 0); + } + + static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab) + { +- ath11k_pci_write32(ab, PCIE_RXVECDB, 0); ++ ath11k_pcic_write32(ab, PCIE_RXVECDB, 0); + } + + static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab) + { +- ath11k_pci_write32(ab, PCIE_RXVECSTATUS, 0); ++ ath11k_pcic_write32(ab, PCIE_RXVECSTATUS, 0); + } + + void ath11k_mhi_clear_vector(struct ath11k_base *ab) +@@ -254,9 +258,9 @@ static int ath11k_mhi_get_msi(struct ath + int *irq; + unsigned int msi_data; + +- ret = ath11k_pci_get_user_msi_assignment(ab_pci, +- "MHI", &num_vectors, +- &user_base_data, &base_vector); ++ ret = ath11k_pcic_get_user_msi_assignment(ab_pci, ++ "MHI", &num_vectors, ++ &user_base_data, &base_vector); + if (ret) + return ret; + +@@ -273,8 +277,7 @@ static int ath11k_mhi_get_msi(struct ath + if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) + msi_data += i; + +- irq[i] = ath11k_pci_get_msi_irq(ab->dev, +- msi_data); ++ irq[i] = ath11k_pcic_get_msi_irq(ab->dev, msi_data); + } + + ab_pci->mhi_ctrl->irq = irq; +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -14,6 +14,7 @@ + #include "hif.h" + #include "mhi.h" + #include "debug.h" ++#include "pcic.h" + + #define ATH11K_PCI_BAR_NUM 0 + #define ATH11K_PCI_DMA_MASK 32 +@@ -75,76 +76,6 @@ static const struct ath11k_msi_config ms + }, + }; + +-static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { +- "bhi", +- "mhi-er0", +- "mhi-er1", +- "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", +-}; +- +-static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset) +-{ +- struct ath11k_base *ab = ab_pci->ab; +- +- u32 window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, offset); +- +- lockdep_assert_held(&ab_pci->window_lock); +- +- if (window != ab_pci->register_window) { +- iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, +- ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +- ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +- ab_pci->register_window = window; +- } +-} +- + static inline void ath11k_pci_select_static_window(struct ath11k_pci *ab_pci) + { + u32 umac_window; +@@ -159,116 +90,15 @@ static inline void ath11k_pci_select_sta + ab_pci->ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); + } + +-static inline u32 ath11k_pci_get_window_start(struct ath11k_base *ab, +- u32 offset) +-{ +- u32 window_start; +- +- /* If offset lies within DP register range, use 3rd window */ +- if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK) +- window_start = 3 * ATH11K_PCI_WINDOW_START; +- /* If offset lies within CE register range, use 2nd window */ +- else if ((offset ^ HAL_CE_WFSS_CE_REG_BASE) < ATH11K_PCI_WINDOW_RANGE_MASK) +- window_start = 2 * ATH11K_PCI_WINDOW_START; +- else +- window_start = ATH11K_PCI_WINDOW_START; +- +- return window_start; +-} +- +-void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- u32 window_start; +- int ret = 0; +- +- /* for offset beyond BAR + 4K - 32, may +- * need to wakeup MHI to access. +- */ +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) +- ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); +- +- if (offset < ATH11K_PCI_WINDOW_START) { +- iowrite32(value, ab->mem + offset); +- } else { +- if (ab->bus_params.static_window_map) +- window_start = ath11k_pci_get_window_start(ab, offset); +- else +- window_start = ATH11K_PCI_WINDOW_START; +- +- if (window_start == ATH11K_PCI_WINDOW_START) { +- spin_lock_bh(&ab_pci->window_lock); +- ath11k_pci_select_window(ab_pci, offset); +- iowrite32(value, ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- spin_unlock_bh(&ab_pci->window_lock); +- } else { +- iowrite32(value, ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- } +- } +- +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && +- !ret) +- mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); +-} +- +-u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- u32 val, window_start; +- int ret = 0; +- +- /* for offset beyond BAR + 4K - 32, may +- * need to wakeup MHI to access. +- */ +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) +- ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); +- +- if (offset < ATH11K_PCI_WINDOW_START) { +- val = ioread32(ab->mem + offset); +- } else { +- if (ab->bus_params.static_window_map) +- window_start = ath11k_pci_get_window_start(ab, offset); +- else +- window_start = ATH11K_PCI_WINDOW_START; +- +- if (window_start == ATH11K_PCI_WINDOW_START) { +- spin_lock_bh(&ab_pci->window_lock); +- ath11k_pci_select_window(ab_pci, offset); +- val = ioread32(ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- spin_unlock_bh(&ab_pci->window_lock); +- } else { +- val = ioread32(ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- } +- } +- +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && +- !ret) +- mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); +- +- return val; +-} +- + static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) + { + u32 val, delay; + +- val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); ++ val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); + + val |= PCIE_SOC_GLOBAL_RESET_V; + +- ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); ++ ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + + /* TODO: exact time to sleep is uncertain */ + delay = 10; +@@ -277,11 +107,11 @@ static void ath11k_pci_soc_global_reset( + /* Need to toggle V bit back otherwise stuck in reset status */ + val &= ~PCIE_SOC_GLOBAL_RESET_V; + +- ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); ++ ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + + mdelay(delay); + +- val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); ++ val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); + if (val == 0xffffffff) + ath11k_warn(ab, "link down error during global reset\n"); + } +@@ -291,10 +121,10 @@ static void ath11k_pci_clear_dbg_registe + u32 val; + + /* read cookie */ +- val = ath11k_pci_read32(ab, PCIE_Q6_COOKIE_ADDR); ++ val = ath11k_pcic_read32(ab, PCIE_Q6_COOKIE_ADDR); + ath11k_dbg(ab, ATH11K_DBG_PCI, "cookie:0x%x\n", val); + +- val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY); ++ val = ath11k_pcic_read32(ab, WLAON_WARM_SW_ENTRY); + ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val); + + /* TODO: exact time to sleep is uncertain */ +@@ -303,16 +133,16 @@ static void ath11k_pci_clear_dbg_registe + /* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from + * continuing warm path and entering dead loop. + */ +- ath11k_pci_write32(ab, WLAON_WARM_SW_ENTRY, 0); ++ ath11k_pcic_write32(ab, WLAON_WARM_SW_ENTRY, 0); + mdelay(10); + +- val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY); ++ val = ath11k_pcic_read32(ab, WLAON_WARM_SW_ENTRY); + ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val); + + /* A read clear register. clear the register to prevent + * Q6 from entering wrong code path. + */ +- val = ath11k_pci_read32(ab, WLAON_SOC_RESET_CAUSE_REG); ++ val = ath11k_pcic_read32(ab, WLAON_SOC_RESET_CAUSE_REG); + ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause:%d\n", val); + } + +@@ -322,14 +152,14 @@ static int ath11k_pci_set_link_reg(struc + u32 v; + int i; + +- v = ath11k_pci_read32(ab, offset); ++ v = ath11k_pcic_read32(ab, offset); + if ((v & mask) == value) + return 0; + + for (i = 0; i < 10; i++) { +- ath11k_pci_write32(ab, offset, (v & ~mask) | value); ++ ath11k_pcic_write32(ab, offset, (v & ~mask) | value); + +- v = ath11k_pci_read32(ab, offset); ++ v = ath11k_pcic_read32(ab, offset); + if ((v & mask) == value) + return 0; + +@@ -390,23 +220,23 @@ static void ath11k_pci_enable_ltssm(stru + u32 val; + int i; + +- val = ath11k_pci_read32(ab, PCIE_PCIE_PARF_LTSSM); ++ val = ath11k_pcic_read32(ab, PCIE_PCIE_PARF_LTSSM); + + /* PCIE link seems very unstable after the Hot Reset*/ + for (i = 0; val != PARM_LTSSM_VALUE && i < 5; i++) { + if (val == 0xffffffff) + mdelay(5); + +- ath11k_pci_write32(ab, PCIE_PCIE_PARF_LTSSM, PARM_LTSSM_VALUE); +- val = ath11k_pci_read32(ab, PCIE_PCIE_PARF_LTSSM); ++ ath11k_pcic_write32(ab, PCIE_PCIE_PARF_LTSSM, PARM_LTSSM_VALUE); ++ val = ath11k_pcic_read32(ab, PCIE_PCIE_PARF_LTSSM); + } + + ath11k_dbg(ab, ATH11K_DBG_PCI, "pci ltssm 0x%x\n", val); + +- val = ath11k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST); ++ val = ath11k_pcic_read32(ab, GCC_GCC_PCIE_HOT_RST); + val |= GCC_GCC_PCIE_HOT_RST_VAL; +- ath11k_pci_write32(ab, GCC_GCC_PCIE_HOT_RST, val); +- val = ath11k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST); ++ ath11k_pcic_write32(ab, GCC_GCC_PCIE_HOT_RST, val); ++ val = ath11k_pcic_read32(ab, GCC_GCC_PCIE_HOT_RST); + + ath11k_dbg(ab, ATH11K_DBG_PCI, "pci pcie_hot_rst 0x%x\n", val); + +@@ -420,21 +250,21 @@ static void ath11k_pci_clear_all_intrs(s + * So when download SBL again, SBL will open Interrupt and + * receive it, and crash immediately. + */ +- ath11k_pci_write32(ab, PCIE_PCIE_INT_ALL_CLEAR, PCIE_INT_CLEAR_ALL); ++ ath11k_pcic_write32(ab, PCIE_PCIE_INT_ALL_CLEAR, PCIE_INT_CLEAR_ALL); + } + + static void ath11k_pci_set_wlaon_pwr_ctrl(struct ath11k_base *ab) + { + u32 val; + +- val = ath11k_pci_read32(ab, WLAON_QFPROM_PWR_CTRL_REG); ++ val = ath11k_pcic_read32(ab, WLAON_QFPROM_PWR_CTRL_REG); + val &= ~QFPROM_PWR_CTRL_VDD4BLOW_MASK; +- ath11k_pci_write32(ab, WLAON_QFPROM_PWR_CTRL_REG, val); ++ ath11k_pcic_write32(ab, WLAON_QFPROM_PWR_CTRL_REG, val); + } + + static void ath11k_pci_force_wake(struct ath11k_base *ab) + { +- ath11k_pci_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1); ++ ath11k_pcic_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1); + mdelay(5); + } + +@@ -456,462 +286,6 @@ static void ath11k_pci_sw_reset(struct a + ath11k_mhi_set_mhictrl_reset(ab); + } + +-int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector) +-{ +- struct pci_dev *pci_dev = to_pci_dev(dev); +- +- return pci_irq_vector(pci_dev, vector); +-} +- +-void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, +- u32 *msi_addr_hi) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- struct pci_dev *pci_dev = to_pci_dev(ab->dev); +- +- pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, +- msi_addr_lo); +- +- if (test_bit(ATH11K_PCI_FLAG_IS_MSI_64, &ab_pci->flags)) { +- pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, +- msi_addr_hi); +- } else { +- *msi_addr_hi = 0; +- } +-} +- +-int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_name, +- int *num_vectors, u32 *user_base_data, +- u32 *base_vector) +-{ +- struct ath11k_base *ab = ab_pci->ab; +- const struct ath11k_msi_config *msi_config = ab_pci->msi_config; +- int idx; +- +- for (idx = 0; idx < msi_config->total_users; idx++) { +- if (strcmp(user_name, msi_config->users[idx].name) == 0) { +- *num_vectors = msi_config->users[idx].num_vectors; +- *base_vector = msi_config->users[idx].base_vector; +- *user_base_data = *base_vector + ab_pci->msi_ep_base_data; +- +- ath11k_dbg(ab, ATH11K_DBG_PCI, +- "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", +- user_name, *num_vectors, *user_base_data, +- *base_vector); +- +- return 0; +- } +- } +- +- ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name); +- +- return -EINVAL; +-} +- +-void ath11k_pci_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx) +-{ +- u32 i, msi_data_idx; +- +- for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { +- if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +- continue; +- +- if (ce_id == i) +- break; +- +- msi_data_idx++; +- } +- *msi_idx = msi_data_idx; +-} +- +-int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, +- int *num_vectors, u32 *user_base_data, +- u32 *base_vector) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- +- return ath11k_pci_get_user_msi_assignment(ab_pci, user_name, +- num_vectors, user_base_data, +- base_vector); +-} +- +-static void ath11k_pci_free_ext_irq(struct ath11k_base *ab) +-{ +- int i, j; +- +- for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { +- struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; +- +- for (j = 0; j < irq_grp->num_irq; j++) +- free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); +- +- netif_napi_del(&irq_grp->napi); +- } +-} +- +-void ath11k_pci_free_irq(struct ath11k_base *ab) +-{ +- int i, irq_idx; +- +- for (i = 0; i < ab->hw_params.ce_count; i++) { +- if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +- continue; +- irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; +- free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); +- } +- +- ath11k_pci_free_ext_irq(ab); +-} +- +-static void ath11k_pci_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- u32 irq_idx; +- +- /* In case of one MSI vector, we handle irq enable/disable in a +- * uniform way since we only have one irq +- */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- return; +- +- irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; +- enable_irq(ab->irq_num[irq_idx]); +-} +- +-static void ath11k_pci_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- u32 irq_idx; +- +- /* In case of one MSI vector, we handle irq enable/disable in a +- * uniform way since we only have one irq +- */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- return; +- +- irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; +- disable_irq_nosync(ab->irq_num[irq_idx]); +-} +- +-static void ath11k_pci_ce_irqs_disable(struct ath11k_base *ab) +-{ +- int i; +- +- clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); +- +- for (i = 0; i < ab->hw_params.ce_count; i++) { +- if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +- continue; +- ath11k_pci_ce_irq_disable(ab, i); +- } +-} +- +-static void ath11k_pci_sync_ce_irqs(struct ath11k_base *ab) +-{ +- int i; +- int irq_idx; +- +- for (i = 0; i < ab->hw_params.ce_count; i++) { +- if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +- continue; +- +- irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; +- synchronize_irq(ab->irq_num[irq_idx]); +- } +-} +- +-static void ath11k_pci_ce_tasklet(struct tasklet_struct *t) +-{ +- struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq); +- int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; +- +- ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); +- +- enable_irq(ce_pipe->ab->irq_num[irq_idx]); +-} +- +-static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg) +-{ +- struct ath11k_ce_pipe *ce_pipe = arg; +- struct ath11k_base *ab = ce_pipe->ab; +- int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; +- +- if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags)) +- return IRQ_HANDLED; +- +- /* last interrupt received for this CE */ +- ce_pipe->timestamp = jiffies; +- +- disable_irq_nosync(ab->irq_num[irq_idx]); +- +- tasklet_schedule(&ce_pipe->intr_tq); +- +- return IRQ_HANDLED; +-} +- +-static void ath11k_pci_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); +- int i; +- +- /* In case of one MSI vector, we handle irq enable/disable +- * in a uniform way since we only have one irq +- */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- return; +- +- for (i = 0; i < irq_grp->num_irq; i++) +- disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +-} +- +-static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc) +-{ +- int i; +- +- clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &sc->dev_flags); +- +- for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { +- struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i]; +- +- ath11k_pci_ext_grp_disable(irq_grp); +- +- if (irq_grp->napi_enabled) { +- napi_synchronize(&irq_grp->napi); +- napi_disable(&irq_grp->napi); +- irq_grp->napi_enabled = false; +- } +- } +-} +- +-static void ath11k_pci_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); +- int i; +- +- /* In case of one MSI vector, we handle irq enable/disable in a +- * uniform way since we only have one irq +- */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- return; +- +- for (i = 0; i < irq_grp->num_irq; i++) +- enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +-} +- +-void ath11k_pci_ext_irq_enable(struct ath11k_base *ab) +-{ +- int i; +- +- set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); +- +- for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { +- struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; +- +- if (!irq_grp->napi_enabled) { +- napi_enable(&irq_grp->napi); +- irq_grp->napi_enabled = true; +- } +- ath11k_pci_ext_grp_enable(irq_grp); +- } +-} +- +-static void ath11k_pci_sync_ext_irqs(struct ath11k_base *ab) +-{ +- int i, j, irq_idx; +- +- for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { +- struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; +- +- for (j = 0; j < irq_grp->num_irq; j++) { +- irq_idx = irq_grp->irqs[j]; +- synchronize_irq(ab->irq_num[irq_idx]); +- } +- } +-} +- +-void ath11k_pci_ext_irq_disable(struct ath11k_base *ab) +-{ +- __ath11k_pci_ext_irq_disable(ab); +- ath11k_pci_sync_ext_irqs(ab); +-} +- +-static int ath11k_pci_ext_grp_napi_poll(struct napi_struct *napi, int budget) +-{ +- struct ath11k_ext_irq_grp *irq_grp = container_of(napi, +- struct ath11k_ext_irq_grp, +- napi); +- struct ath11k_base *ab = irq_grp->ab; +- int work_done; +- int i; +- +- work_done = ath11k_dp_service_srng(ab, irq_grp, budget); +- if (work_done < budget) { +- napi_complete_done(napi, work_done); +- for (i = 0; i < irq_grp->num_irq; i++) +- enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +- } +- +- if (work_done > budget) +- work_done = budget; +- +- return work_done; +-} +- +-static irqreturn_t ath11k_pci_ext_interrupt_handler(int irq, void *arg) +-{ +- struct ath11k_ext_irq_grp *irq_grp = arg; +- struct ath11k_base *ab = irq_grp->ab; +- int i; +- +- if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags)) +- return IRQ_HANDLED; +- +- ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq); +- +- /* last interrupt received for this group */ +- irq_grp->timestamp = jiffies; +- +- for (i = 0; i < irq_grp->num_irq; i++) +- disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +- +- napi_schedule(&irq_grp->napi); +- +- return IRQ_HANDLED; +-} +- +-static int ath11k_pci_ext_irq_config(struct ath11k_base *ab) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- int i, j, ret, num_vectors = 0; +- u32 user_base_data = 0, base_vector = 0; +- +- ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), "DP", +- &num_vectors, +- &user_base_data, +- &base_vector); +- if (ret < 0) +- return ret; +- +- for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { +- struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; +- u32 num_irq = 0; +- +- irq_grp->ab = ab; +- irq_grp->grp_id = i; +- init_dummy_netdev(&irq_grp->napi_ndev); +- netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, +- ath11k_pci_ext_grp_napi_poll, NAPI_POLL_WEIGHT); +- +- if (ab->hw_params.ring_mask->tx[i] || +- ab->hw_params.ring_mask->rx[i] || +- ab->hw_params.ring_mask->rx_err[i] || +- ab->hw_params.ring_mask->rx_wbm_rel[i] || +- ab->hw_params.ring_mask->reo_status[i] || +- ab->hw_params.ring_mask->rxdma2host[i] || +- ab->hw_params.ring_mask->host2rxdma[i] || +- ab->hw_params.ring_mask->rx_mon_status[i]) { +- num_irq = 1; +- } +- +- irq_grp->num_irq = num_irq; +- irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i; +- +- for (j = 0; j < irq_grp->num_irq; j++) { +- int irq_idx = irq_grp->irqs[j]; +- int vector = (i % num_vectors) + base_vector; +- int irq = ath11k_pci_get_msi_irq(ab->dev, vector); +- +- ab->irq_num[irq_idx] = irq; +- +- ath11k_dbg(ab, ATH11K_DBG_PCI, +- "irq:%d group:%d\n", irq, i); +- +- irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); +- ret = request_irq(irq, ath11k_pci_ext_interrupt_handler, +- ab_pci->irq_flags, +- "DP_EXT_IRQ", irq_grp); +- if (ret) { +- ath11k_err(ab, "failed request irq %d: %d\n", +- vector, ret); +- return ret; +- } +- } +- ath11k_pci_ext_grp_disable(irq_grp); +- } +- +- return 0; +-} +- +-int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, +- const struct cpumask *m) +-{ +- if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- return 0; +- +- return irq_set_affinity_hint(ab_pci->pdev->irq, m); +-} +- +-int ath11k_pci_config_irq(struct ath11k_base *ab) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- struct ath11k_ce_pipe *ce_pipe; +- u32 msi_data_start; +- u32 msi_data_count, msi_data_idx; +- u32 msi_irq_start; +- unsigned int msi_data; +- int irq, i, ret, irq_idx; +- +- ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), +- "CE", &msi_data_count, +- &msi_data_start, &msi_irq_start); +- if (ret) +- return ret; +- +- ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0)); +- if (ret) { +- ath11k_err(ab, "failed to set irq affinity %d\n", ret); +- return ret; +- } +- +- /* Configure CE irqs */ +- for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { +- if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +- continue; +- +- msi_data = (msi_data_idx % msi_data_count) + msi_irq_start; +- irq = ath11k_pci_get_msi_irq(ab->dev, msi_data); +- ce_pipe = &ab->ce.ce_pipe[i]; +- +- irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; +- +- tasklet_setup(&ce_pipe->intr_tq, ath11k_pci_ce_tasklet); +- +- ret = request_irq(irq, ath11k_pci_ce_interrupt_handler, +- ab_pci->irq_flags, irq_name[irq_idx], +- ce_pipe); +- if (ret) { +- ath11k_err(ab, "failed to request irq %d: %d\n", +- irq_idx, ret); +- goto err_irq_affinity_cleanup; +- } +- +- ab->irq_num[irq_idx] = irq; +- msi_data_idx++; +- +- ath11k_pci_ce_irq_disable(ab, i); +- } +- +- ret = ath11k_pci_ext_irq_config(ab); +- if (ret) +- goto err_irq_affinity_cleanup; +- +- return 0; +- +-err_irq_affinity_cleanup: +- ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); +- return ret; +-} +- + static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab) + { + struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; +@@ -927,19 +301,6 @@ static void ath11k_pci_init_qmi_ce_confi + &cfg->shadow_reg_v2_len); + } + +-void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab) +-{ +- int i; +- +- set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); +- +- for (i = 0; i < ab->hw_params.ce_count; i++) { +- if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +- continue; +- ath11k_pci_ce_irq_enable(ab, i); +- } +-} +- + static void ath11k_pci_msi_config(struct ath11k_pci *ab_pci, bool enable) + { + struct pci_dev *dev = ab_pci->pdev; +@@ -1139,13 +500,6 @@ static void ath11k_pci_aspm_disable(stru + set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags); + } + +-void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) +-{ +- if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) +- pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL, +- ab_pci->link_ctl); +-} +- + static int ath11k_pci_power_up(struct ath11k_base *ab) + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +@@ -1179,7 +533,7 @@ static void ath11k_pci_power_down(struct + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + /* restore aspm in case firmware bootup fails */ +- ath11k_pci_aspm_restore(ab_pci); ++ ath11k_pcic_aspm_restore(ab_pci); + + ath11k_pci_force_wake(ab_pci->ab); + +@@ -1208,130 +562,40 @@ static int ath11k_pci_hif_resume(struct + return 0; + } + +-static void ath11k_pci_kill_tasklets(struct ath11k_base *ab) +-{ +- int i; +- +- for (i = 0; i < ab->hw_params.ce_count; i++) { +- struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; +- +- if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) +- continue; +- +- tasklet_kill(&ce_pipe->intr_tq); +- } +-} +- +-void ath11k_pci_ce_irq_disable_sync(struct ath11k_base *ab) +-{ +- ath11k_pci_ce_irqs_disable(ab); +- ath11k_pci_sync_ce_irqs(ab); +- ath11k_pci_kill_tasklets(ab); +-} +- +-void ath11k_pci_stop(struct ath11k_base *ab) +-{ +- ath11k_pci_ce_irq_disable_sync(ab); +- ath11k_ce_cleanup_pipes(ab); +-} +- +-int ath11k_pci_start(struct ath11k_base *ab) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- +- set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); +- +- /* TODO: for now don't restore ASPM in case of single MSI +- * vector as MHI register reading in M2 causes system hang. +- */ +- if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- ath11k_pci_aspm_restore(ab_pci); +- else +- ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n"); +- +- ath11k_pci_ce_irqs_enable(ab); +- ath11k_ce_rx_post_buf(ab); +- +- return 0; +-} +- + static void ath11k_pci_hif_ce_irq_enable(struct ath11k_base *ab) + { +- ath11k_pci_ce_irqs_enable(ab); ++ ath11k_pcic_ce_irqs_enable(ab); + } + + static void ath11k_pci_hif_ce_irq_disable(struct ath11k_base *ab) + { +- ath11k_pci_ce_irq_disable_sync(ab); +-} +- +-int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, +- u8 *ul_pipe, u8 *dl_pipe) +-{ +- const struct service_to_pipe *entry; +- bool ul_set = false, dl_set = false; +- int i; +- +- for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) { +- entry = &ab->hw_params.svc_to_ce_map[i]; +- +- if (__le32_to_cpu(entry->service_id) != service_id) +- continue; +- +- switch (__le32_to_cpu(entry->pipedir)) { +- case PIPEDIR_NONE: +- break; +- case PIPEDIR_IN: +- WARN_ON(dl_set); +- *dl_pipe = __le32_to_cpu(entry->pipenum); +- dl_set = true; +- break; +- case PIPEDIR_OUT: +- WARN_ON(ul_set); +- *ul_pipe = __le32_to_cpu(entry->pipenum); +- ul_set = true; +- break; +- case PIPEDIR_INOUT: +- WARN_ON(dl_set); +- WARN_ON(ul_set); +- *dl_pipe = __le32_to_cpu(entry->pipenum); +- *ul_pipe = __le32_to_cpu(entry->pipenum); +- dl_set = true; +- ul_set = true; +- break; +- } +- } +- +- if (WARN_ON(!ul_set || !dl_set)) +- return -ENOENT; +- +- return 0; ++ ath11k_pcic_ce_irq_disable_sync(ab); + } + + static const struct ath11k_hif_ops ath11k_pci_hif_ops = { +- .start = ath11k_pci_start, +- .stop = ath11k_pci_stop, +- .read32 = ath11k_pci_read32, +- .write32 = ath11k_pci_write32, ++ .start = ath11k_pcic_start, ++ .stop = ath11k_pcic_stop, ++ .read32 = ath11k_pcic_read32, ++ .write32 = ath11k_pcic_write32, + .power_down = ath11k_pci_power_down, + .power_up = ath11k_pci_power_up, + .suspend = ath11k_pci_hif_suspend, + .resume = ath11k_pci_hif_resume, +- .irq_enable = ath11k_pci_ext_irq_enable, +- .irq_disable = ath11k_pci_ext_irq_disable, +- .get_msi_address = ath11k_pci_get_msi_address, ++ .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_get_user_msi_assignment, +- .map_service_to_pipe = ath11k_pci_map_service_to_pipe, ++ .map_service_to_pipe = ath11k_pcic_map_service_to_pipe, + .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, + .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, +- .get_ce_msi_idx = ath11k_pci_get_ce_msi_idx, ++ .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx, + }; + + static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor) + { + u32 soc_hw_version; + +- soc_hw_version = ath11k_pci_read32(ab, TCSR_SOC_HW_VERSION); ++ soc_hw_version = ath11k_pcic_read32(ab, TCSR_SOC_HW_VERSION); + *major = FIELD_GET(TCSR_SOC_HW_VERSION_MAJOR_MASK, + soc_hw_version); + *minor = FIELD_GET(TCSR_SOC_HW_VERSION_MINOR_MASK, +@@ -1473,7 +737,7 @@ unsupported_wcn6855_soc: + + ath11k_pci_init_qmi_ce_config(ab); + +- ret = ath11k_pci_config_irq(ab); ++ ret = ath11k_pcic_config_irq(ab); + if (ret) { + ath11k_err(ab, "failed to config irq: %d\n", ret); + goto err_ce_free; +@@ -1498,7 +762,7 @@ unsupported_wcn6855_soc: + return 0; + + err_free_irq: +- ath11k_pci_free_irq(ab); ++ ath11k_pcic_free_irq(ab); + + err_ce_free: + ath11k_ce_free_pipes(ab); +@@ -1526,7 +790,7 @@ static void ath11k_pci_remove(struct pci + struct ath11k_base *ab = pci_get_drvdata(pdev); + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + +- ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); ++ ath11k_pcic_set_irq_affinity_hint(ab_pci, NULL); + + if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath11k_pci_power_down(ab); +@@ -1542,7 +806,7 @@ static void ath11k_pci_remove(struct pci + qmi_fail: + ath11k_mhi_unregister(ab_pci); + +- ath11k_pci_free_irq(ab); ++ ath11k_pcic_free_irq(ab); + ath11k_pci_free_msi(ab_pci); + ath11k_pci_free_region(ab_pci); + +--- a/drivers/net/wireless/ath/ath11k/pci.h ++++ b/drivers/net/wireless/ath/ath11k/pci.h +@@ -53,21 +53,6 @@ + #define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c + #define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4 + +-#define ATH11K_PCI_IRQ_CE0_OFFSET 3 +-#define ATH11K_PCI_IRQ_DP_OFFSET 14 +- +-#define ATH11K_PCI_WINDOW_ENABLE_BIT 0x40000000 +-#define ATH11K_PCI_WINDOW_REG_ADDRESS 0x310c +-#define ATH11K_PCI_WINDOW_VALUE_MASK GENMASK(24, 19) +-#define ATH11K_PCI_WINDOW_START 0x80000 +-#define ATH11K_PCI_WINDOW_RANGE_MASK GENMASK(18, 0) +- +-/* BAR0 + 4k is always accessible, and no +- * need to force wakeup. +- * 4K - 32 = 0xFE0 +- */ +-#define ATH11K_PCI_ACCESS_ALWAYS_OFF 0xFE0 +- + struct ath11k_msi_user { + char *name; + int num_vectors; +@@ -113,30 +98,4 @@ static inline struct ath11k_pci *ath11k_ + return (struct ath11k_pci *)ab->drv_priv; + } + +-int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ar_pci, char *user_name, +- int *num_vectors, u32 *user_base_data, +- u32 *base_vector); +-int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector); +-void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value); +-u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset); +-void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, +- u32 *msi_addr_hi); +-void ath11k_pci_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); +-void ath11k_pci_free_irq(struct ath11k_base *ab); +-int ath11k_pci_config_irq(struct ath11k_base *ab); +-void ath11k_pci_ext_irq_enable(struct ath11k_base *ab); +-void ath11k_pci_ext_irq_disable(struct ath11k_base *ab); +-void ath11k_pci_stop(struct ath11k_base *ab); +-int ath11k_pci_start(struct ath11k_base *ab); +-int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, +- u8 *ul_pipe, u8 *dl_pipe); +-void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab); +-void ath11k_pci_ce_irq_disable_sync(struct ath11k_base *ab); +-int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, +- int *num_vectors, u32 *user_base_data, +- u32 *base_vector); +-void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci); +-int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, +- const struct cpumask *m); +- + #endif +--- /dev/null ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -0,0 +1,747 @@ ++// SPDX-License-Identifier: BSD-3-Clause-Clear ++/* ++ * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++#include ++#include "core.h" ++#include "pcic.h" ++#include "debug.h" ++ ++static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { ++ "bhi", ++ "mhi-er0", ++ "mhi-er1", ++ "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", ++}; ++ ++void ath11k_pcic_aspm_restore(struct ath11k_pci *ab_pci) ++{ ++ if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) ++ pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL, ++ ab_pci->link_ctl); ++} ++ ++static inline void ath11k_pcic_select_window(struct ath11k_pci *ab_pci, u32 offset) ++{ ++ struct ath11k_base *ab = ab_pci->ab; ++ ++ u32 window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, offset); ++ ++ lockdep_assert_held(&ab_pci->window_lock); ++ ++ if (window != ab_pci->register_window) { ++ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, ++ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++ ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++ ab_pci->register_window = window; ++ } ++} ++ ++static inline u32 ath11k_pcic_get_window_start(struct ath11k_base *ab, ++ u32 offset) ++{ ++ u32 window_start; ++ ++ /* If offset lies within DP register range, use 3rd window */ ++ if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK) ++ window_start = 3 * ATH11K_PCI_WINDOW_START; ++ /* If offset lies within CE register range, use 2nd window */ ++ else if ((offset ^ HAL_CE_WFSS_CE_REG_BASE) < ATH11K_PCI_WINDOW_RANGE_MASK) ++ window_start = 2 * ATH11K_PCI_WINDOW_START; ++ else ++ window_start = ATH11K_PCI_WINDOW_START; ++ ++ return window_start; ++} ++ ++void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ u32 window_start; ++ int ret = 0; ++ ++ /* for offset beyond BAR + 4K - 32, may ++ * need to wakeup MHI to access. ++ */ ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) ++ ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); ++ ++ if (offset < ATH11K_PCI_WINDOW_START) { ++ iowrite32(value, ab->mem + offset); ++ } else { ++ if (ab->bus_params.static_window_map) ++ window_start = ath11k_pcic_get_window_start(ab, offset); ++ else ++ window_start = ATH11K_PCI_WINDOW_START; ++ ++ if (window_start == ATH11K_PCI_WINDOW_START) { ++ spin_lock_bh(&ab_pci->window_lock); ++ ath11k_pcic_select_window(ab_pci, offset); ++ iowrite32(value, ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ spin_unlock_bh(&ab_pci->window_lock); ++ } else { ++ iowrite32(value, ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ } ++ } ++ ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ++ !ret) ++ mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); ++} ++ ++u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ u32 val, window_start; ++ int ret = 0; ++ ++ /* for offset beyond BAR + 4K - 32, may ++ * need to wakeup MHI to access. ++ */ ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) ++ ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); ++ ++ if (offset < ATH11K_PCI_WINDOW_START) { ++ val = ioread32(ab->mem + offset); ++ } else { ++ if (ab->bus_params.static_window_map) ++ window_start = ath11k_pcic_get_window_start(ab, offset); ++ else ++ window_start = ATH11K_PCI_WINDOW_START; ++ ++ if (window_start == ATH11K_PCI_WINDOW_START) { ++ spin_lock_bh(&ab_pci->window_lock); ++ ath11k_pcic_select_window(ab_pci, offset); ++ val = ioread32(ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ spin_unlock_bh(&ab_pci->window_lock); ++ } else { ++ val = ioread32(ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ } ++ } ++ ++ if (ab->hw_params.wakeup_mhi && ++ test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ++ !ret) ++ mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); ++ ++ return val; ++} ++ ++int ath11k_pcic_get_msi_irq(struct device *dev, unsigned int vector) ++{ ++ struct pci_dev *pci_dev = to_pci_dev(dev); ++ ++ return pci_irq_vector(pci_dev, vector); ++} ++ ++void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, ++ u32 *msi_addr_hi) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ struct pci_dev *pci_dev = to_pci_dev(ab->dev); ++ ++ pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, ++ msi_addr_lo); ++ ++ if (test_bit(ATH11K_PCI_FLAG_IS_MSI_64, &ab_pci->flags)) { ++ pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, ++ msi_addr_hi); ++ } else { ++ *msi_addr_hi = 0; ++ } ++} ++ ++int ath11k_pcic_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_name, ++ int *num_vectors, u32 *user_base_data, ++ u32 *base_vector) ++{ ++ struct ath11k_base *ab = ab_pci->ab; ++ const struct ath11k_msi_config *msi_config = ab_pci->msi_config; ++ int idx; ++ ++ for (idx = 0; idx < msi_config->total_users; idx++) { ++ if (strcmp(user_name, msi_config->users[idx].name) == 0) { ++ *num_vectors = msi_config->users[idx].num_vectors; ++ *base_vector = msi_config->users[idx].base_vector; ++ *user_base_data = *base_vector + ab_pci->msi_ep_base_data; ++ ++ ath11k_dbg(ab, ATH11K_DBG_PCI, ++ "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", ++ user_name, *num_vectors, *user_base_data, ++ *base_vector); ++ ++ return 0; ++ } ++ } ++ ++ ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name); ++ ++ return -EINVAL; ++} ++ ++void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx) ++{ ++ u32 i, msi_data_idx; ++ ++ for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { ++ if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) ++ continue; ++ ++ if (ce_id == i) ++ break; ++ ++ msi_data_idx++; ++ } ++ *msi_idx = msi_data_idx; ++} ++ ++int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, ++ int *num_vectors, u32 *user_base_data, ++ u32 *base_vector) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ ++ return ath11k_pcic_get_user_msi_assignment(ab_pci, user_name, ++ num_vectors, user_base_data, ++ base_vector); ++} ++ ++static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab) ++{ ++ int i, j; ++ ++ for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { ++ struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; ++ ++ for (j = 0; j < irq_grp->num_irq; j++) ++ free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); ++ ++ netif_napi_del(&irq_grp->napi); ++ } ++} ++ ++void ath11k_pcic_free_irq(struct ath11k_base *ab) ++{ ++ int i, irq_idx; ++ ++ for (i = 0; i < ab->hw_params.ce_count; i++) { ++ if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) ++ continue; ++ irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; ++ free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); ++ } ++ ++ ath11k_pcic_free_ext_irq(ab); ++} ++ ++static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ u32 irq_idx; ++ ++ /* In case of one MSI vector, we handle irq enable/disable in a ++ * uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ ++ irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; ++ enable_irq(ab->irq_num[irq_idx]); ++} ++ ++static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ u32 irq_idx; ++ ++ /* In case of one MSI vector, we handle irq enable/disable in a ++ * uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ ++ irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; ++ disable_irq_nosync(ab->irq_num[irq_idx]); ++} ++ ++static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab) ++{ ++ int i; ++ ++ clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); ++ ++ for (i = 0; i < ab->hw_params.ce_count; i++) { ++ if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) ++ continue; ++ ath11k_pcic_ce_irq_disable(ab, i); ++ } ++} ++ ++static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab) ++{ ++ int i; ++ int irq_idx; ++ ++ for (i = 0; i < ab->hw_params.ce_count; i++) { ++ if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) ++ continue; ++ ++ irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; ++ synchronize_irq(ab->irq_num[irq_idx]); ++ } ++} ++ ++static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t) ++{ ++ struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq); ++ int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; ++ ++ ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); ++ ++ enable_irq(ce_pipe->ab->irq_num[irq_idx]); ++} ++ ++static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg) ++{ ++ struct ath11k_ce_pipe *ce_pipe = arg; ++ struct ath11k_base *ab = ce_pipe->ab; ++ int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; ++ ++ if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags)) ++ return IRQ_HANDLED; ++ ++ /* last interrupt received for this CE */ ++ ce_pipe->timestamp = jiffies; ++ ++ disable_irq_nosync(ab->irq_num[irq_idx]); ++ ++ tasklet_schedule(&ce_pipe->intr_tq); ++ ++ return IRQ_HANDLED; ++} ++ ++static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); ++ int i; ++ ++ /* In case of one MSI vector, we handle irq enable/disable ++ * in a uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ ++ for (i = 0; i < irq_grp->num_irq; i++) ++ disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); ++} ++ ++static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *sc) ++{ ++ int i; ++ ++ clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &sc->dev_flags); ++ ++ for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { ++ struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i]; ++ ++ ath11k_pcic_ext_grp_disable(irq_grp); ++ ++ if (irq_grp->napi_enabled) { ++ napi_synchronize(&irq_grp->napi); ++ napi_disable(&irq_grp->napi); ++ irq_grp->napi_enabled = false; ++ } ++ } ++} ++ ++static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); ++ int i; ++ ++ /* In case of one MSI vector, we handle irq enable/disable in a ++ * uniform way since we only have one irq ++ */ ++ if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return; ++ ++ for (i = 0; i < irq_grp->num_irq; i++) ++ enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); ++} ++ ++void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab) ++{ ++ int i; ++ ++ set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); ++ ++ for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { ++ struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; ++ ++ if (!irq_grp->napi_enabled) { ++ napi_enable(&irq_grp->napi); ++ irq_grp->napi_enabled = true; ++ } ++ ath11k_pcic_ext_grp_enable(irq_grp); ++ } ++} ++ ++static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab) ++{ ++ int i, j, irq_idx; ++ ++ for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { ++ struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; ++ ++ for (j = 0; j < irq_grp->num_irq; j++) { ++ irq_idx = irq_grp->irqs[j]; ++ synchronize_irq(ab->irq_num[irq_idx]); ++ } ++ } ++} ++ ++void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab) ++{ ++ __ath11k_pcic_ext_irq_disable(ab); ++ ath11k_pcic_sync_ext_irqs(ab); ++} ++ ++static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget) ++{ ++ struct ath11k_ext_irq_grp *irq_grp = container_of(napi, ++ struct ath11k_ext_irq_grp, ++ napi); ++ struct ath11k_base *ab = irq_grp->ab; ++ int work_done; ++ int i; ++ ++ work_done = ath11k_dp_service_srng(ab, irq_grp, budget); ++ if (work_done < budget) { ++ napi_complete_done(napi, work_done); ++ for (i = 0; i < irq_grp->num_irq; i++) ++ enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); ++ } ++ ++ if (work_done > budget) ++ work_done = budget; ++ ++ return work_done; ++} ++ ++static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg) ++{ ++ struct ath11k_ext_irq_grp *irq_grp = arg; ++ struct ath11k_base *ab = irq_grp->ab; ++ int i; ++ ++ if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags)) ++ return IRQ_HANDLED; ++ ++ ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq); ++ ++ /* last interrupt received for this group */ ++ irq_grp->timestamp = jiffies; ++ ++ for (i = 0; i < irq_grp->num_irq; i++) ++ disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); ++ ++ napi_schedule(&irq_grp->napi); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ int i, j, ret, num_vectors = 0; ++ u32 user_base_data = 0, base_vector = 0; ++ ++ ret = ath11k_pcic_get_user_msi_assignment(ath11k_pci_priv(ab), "DP", ++ &num_vectors, ++ &user_base_data, ++ &base_vector); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { ++ struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; ++ u32 num_irq = 0; ++ ++ irq_grp->ab = ab; ++ irq_grp->grp_id = i; ++ init_dummy_netdev(&irq_grp->napi_ndev); ++ netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, ++ ath11k_pcic_ext_grp_napi_poll, NAPI_POLL_WEIGHT); ++ ++ if (ab->hw_params.ring_mask->tx[i] || ++ ab->hw_params.ring_mask->rx[i] || ++ ab->hw_params.ring_mask->rx_err[i] || ++ ab->hw_params.ring_mask->rx_wbm_rel[i] || ++ ab->hw_params.ring_mask->reo_status[i] || ++ ab->hw_params.ring_mask->rxdma2host[i] || ++ ab->hw_params.ring_mask->host2rxdma[i] || ++ ab->hw_params.ring_mask->rx_mon_status[i]) { ++ num_irq = 1; ++ } ++ ++ irq_grp->num_irq = num_irq; ++ irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i; ++ ++ for (j = 0; j < irq_grp->num_irq; j++) { ++ int irq_idx = irq_grp->irqs[j]; ++ int vector = (i % num_vectors) + base_vector; ++ int irq = ath11k_pcic_get_msi_irq(ab->dev, vector); ++ ++ ab->irq_num[irq_idx] = irq; ++ ++ ath11k_dbg(ab, ATH11K_DBG_PCI, ++ "irq:%d group:%d\n", irq, i); ++ ++ irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); ++ ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler, ++ ab_pci->irq_flags, ++ "DP_EXT_IRQ", irq_grp); ++ if (ret) { ++ ath11k_err(ab, "failed request irq %d: %d\n", ++ vector, ret); ++ return ret; ++ } ++ } ++ ath11k_pcic_ext_grp_disable(irq_grp); ++ } ++ ++ return 0; ++} ++ ++int ath11k_pcic_set_irq_affinity_hint(struct ath11k_pci *ab_pci, ++ const struct cpumask *m) ++{ ++ if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ return 0; ++ ++ return irq_set_affinity_hint(ab_pci->pdev->irq, m); ++} ++ ++int ath11k_pcic_config_irq(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ struct ath11k_ce_pipe *ce_pipe; ++ u32 msi_data_start; ++ u32 msi_data_count, msi_data_idx; ++ u32 msi_irq_start; ++ unsigned int msi_data; ++ int irq, i, ret, irq_idx; ++ ++ ret = ath11k_pcic_get_user_msi_assignment(ath11k_pci_priv(ab), ++ "CE", &msi_data_count, ++ &msi_data_start, &msi_irq_start); ++ if (ret) ++ return ret; ++ ++ ret = ath11k_pcic_set_irq_affinity_hint(ab_pci, cpumask_of(0)); ++ if (ret) { ++ ath11k_err(ab, "failed to set irq affinity %d\n", ret); ++ return ret; ++ } ++ ++ /* Configure CE irqs */ ++ for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { ++ if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) ++ continue; ++ ++ msi_data = (msi_data_idx % msi_data_count) + msi_irq_start; ++ irq = ath11k_pcic_get_msi_irq(ab->dev, msi_data); ++ ce_pipe = &ab->ce.ce_pipe[i]; ++ ++ irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; ++ ++ tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet); ++ ++ ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler, ++ ab_pci->irq_flags, irq_name[irq_idx], ++ ce_pipe); ++ if (ret) { ++ ath11k_err(ab, "failed to request irq %d: %d\n", ++ irq_idx, ret); ++ goto err_irq_affinity_cleanup; ++ } ++ ++ ab->irq_num[irq_idx] = irq; ++ msi_data_idx++; ++ ++ ath11k_pcic_ce_irq_disable(ab, i); ++ } ++ ++ ret = ath11k_pcic_ext_irq_config(ab); ++ if (ret) ++ goto err_irq_affinity_cleanup; ++ ++ return 0; ++ ++err_irq_affinity_cleanup: ++ ath11k_pcic_set_irq_affinity_hint(ab_pci, NULL); ++ return ret; ++} ++ ++void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab) ++{ ++ int i; ++ ++ set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); ++ ++ for (i = 0; i < ab->hw_params.ce_count; i++) { ++ if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) ++ continue; ++ ath11k_pcic_ce_irq_enable(ab, i); ++ } ++} ++ ++static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab) ++{ ++ int i; ++ ++ for (i = 0; i < ab->hw_params.ce_count; i++) { ++ struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; ++ ++ if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) ++ continue; ++ ++ tasklet_kill(&ce_pipe->intr_tq); ++ } ++} ++ ++void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab) ++{ ++ ath11k_pcic_ce_irqs_disable(ab); ++ ath11k_pcic_sync_ce_irqs(ab); ++ ath11k_pcic_kill_tasklets(ab); ++} ++ ++void ath11k_pcic_stop(struct ath11k_base *ab) ++{ ++ ath11k_pcic_ce_irq_disable_sync(ab); ++ ath11k_ce_cleanup_pipes(ab); ++} ++ ++int ath11k_pcic_start(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ ++ set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); ++ ++ /* TODO: for now don't restore ASPM in case of single MSI ++ * vector as MHI register reading in M2 causes system hang. ++ */ ++ if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ ath11k_pcic_aspm_restore(ab_pci); ++ else ++ ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n"); ++ ++ ath11k_pcic_ce_irqs_enable(ab); ++ ath11k_ce_rx_post_buf(ab); ++ ++ return 0; ++} ++ ++int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, ++ u8 *ul_pipe, u8 *dl_pipe) ++{ ++ const struct service_to_pipe *entry; ++ bool ul_set = false, dl_set = false; ++ int i; ++ ++ for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) { ++ entry = &ab->hw_params.svc_to_ce_map[i]; ++ ++ if (__le32_to_cpu(entry->service_id) != service_id) ++ continue; ++ ++ switch (__le32_to_cpu(entry->pipedir)) { ++ case PIPEDIR_NONE: ++ break; ++ case PIPEDIR_IN: ++ WARN_ON(dl_set); ++ *dl_pipe = __le32_to_cpu(entry->pipenum); ++ dl_set = true; ++ break; ++ case PIPEDIR_OUT: ++ WARN_ON(ul_set); ++ *ul_pipe = __le32_to_cpu(entry->pipenum); ++ ul_set = true; ++ break; ++ case PIPEDIR_INOUT: ++ WARN_ON(dl_set); ++ WARN_ON(ul_set); ++ *dl_pipe = __le32_to_cpu(entry->pipenum); ++ *ul_pipe = __le32_to_cpu(entry->pipenum); ++ dl_set = true; ++ ul_set = true; ++ break; ++ } ++ } ++ ++ if (WARN_ON(!ul_set || !dl_set)) ++ return -ENOENT; ++ ++ return 0; ++} +--- /dev/null ++++ b/drivers/net/wireless/ath/ath11k/pcic.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: BSD-3-Clause-Clear */ ++/* ++ * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++#ifndef _ATH11K_PCI_CMN_H ++#define _ATH11K_PCI_CMN_H ++ ++#include "core.h" ++#include "pci.h" ++ ++#define ATH11K_PCI_IRQ_CE0_OFFSET 3 ++#define ATH11K_PCI_IRQ_DP_OFFSET 14 ++ ++#define ATH11K_PCI_WINDOW_ENABLE_BIT 0x40000000 ++#define ATH11K_PCI_WINDOW_REG_ADDRESS 0x310c ++#define ATH11K_PCI_WINDOW_VALUE_MASK GENMASK(24, 19) ++#define ATH11K_PCI_WINDOW_START 0x80000 ++#define ATH11K_PCI_WINDOW_RANGE_MASK GENMASK(18, 0) ++ ++/* BAR0 + 4k is always accessible, and no ++ * need to force wakeup. ++ * 4K - 32 = 0xFE0 ++ */ ++#define ATH11K_PCI_ACCESS_ALWAYS_OFF 0xFE0 ++ ++int ath11k_pcic_get_user_msi_assignment(struct ath11k_pci *ar_pci, char *user_name, ++ int *num_vectors, u32 *user_base_data, ++ u32 *base_vector); ++int ath11k_pcic_get_msi_irq(struct device *dev, unsigned int vector); ++void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value); ++u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset); ++void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, ++ u32 *msi_addr_hi); ++void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); ++void ath11k_pcic_free_irq(struct ath11k_base *ab); ++int ath11k_pcic_config_irq(struct ath11k_base *ab); ++void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab); ++void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab); ++void ath11k_pcic_stop(struct ath11k_base *ab); ++int ath11k_pcic_start(struct ath11k_base *ab); ++int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, ++ u8 *ul_pipe, u8 *dl_pipe); ++void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab); ++void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab); ++int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, ++ int *num_vectors, u32 *user_base_data, ++ u32 *base_vector); ++void ath11k_pcic_aspm_restore(struct ath11k_pci *ab_pci); ++int ath11k_pcic_set_irq_affinity_hint(struct ath11k_pci *ab_pci, ++ const struct cpumask *m); ++#endif diff --git a/package/kernel/mac80211/patches/ath11k/0217-ath11k-Choose-MSI-config-based-on-HW-revision.patch b/package/kernel/mac80211/patches/ath11k/0217-ath11k-Choose-MSI-config-based-on-HW-revision.patch new file mode 100644 index 00000000000000..03fdd7eb433bf6 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0217-ath11k-Choose-MSI-config-based-on-HW-revision.patch @@ -0,0 +1,184 @@ +From 8d06b8023ace027dc31a9cb3c85c3c8fe83289c5 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 1 Apr 2022 14:53:08 +0300 +Subject: [PATCH] ath11k: Choose MSI config based on HW revision + +Instead of selecting MSI config based on magic numbers, make +the assignment based on HW revision. The logic is similar to +the selection of HW params. This improves readability of the +code and also simplifies new additions. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220328055714.6449-4-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/pci.c | 31 +++--------- + drivers/net/wireless/ath/ath11k/pci.h | 1 + + drivers/net/wireless/ath/ath11k/pcic.c | 70 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/pcic.h | 1 + + 4 files changed, 78 insertions(+), 25 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -43,28 +43,6 @@ static const struct ath11k_bus_params at + .fixed_mem_region = false, + }; + +-static const struct ath11k_msi_config ath11k_msi_config[] = { +- { +- .total_vectors = 32, +- .total_users = 4, +- .users = (struct ath11k_msi_user[]) { +- { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, +- { .name = "CE", .num_vectors = 10, .base_vector = 3 }, +- { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, +- { .name = "DP", .num_vectors = 18, .base_vector = 14 }, +- }, +- }, +- { +- .total_vectors = 16, +- .total_users = 3, +- .users = (struct ath11k_msi_user[]) { +- { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, +- { .name = "CE", .num_vectors = 5, .base_vector = 3 }, +- { .name = "DP", .num_vectors = 8, .base_vector = 8 }, +- }, +- }, +-}; +- + static const struct ath11k_msi_config msi_config_one_msi = { + .total_vectors = 1, + .total_users = 4, +@@ -667,10 +645,8 @@ static int ath11k_pci_probe(struct pci_d + ret = -EOPNOTSUPP; + goto err_pci_free_region; + } +- ab_pci->msi_config = &ath11k_msi_config[0]; + break; + case QCN9074_DEVICE_ID: +- ab_pci->msi_config = &ath11k_msi_config[1]; + ab->bus_params.static_window_map = true; + ab->hw_rev = ATH11K_HW_QCN9074_HW10; + break; +@@ -700,7 +676,6 @@ unsupported_wcn6855_soc: + ret = -EOPNOTSUPP; + goto err_pci_free_region; + } +- ab_pci->msi_config = &ath11k_msi_config[0]; + break; + default: + dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n", +@@ -709,6 +684,12 @@ unsupported_wcn6855_soc: + goto err_pci_free_region; + } + ++ ret = ath11k_pcic_init_msi_config(ab); ++ if (ret) { ++ ath11k_err(ab, "failed to init msi config: %d\n", ret); ++ goto err_pci_free_region; ++ } ++ + ret = ath11k_pci_alloc_msi(ab_pci); + if (ret) { + ath11k_err(ab, "failed to enable msi: %d\n", ret); +--- a/drivers/net/wireless/ath/ath11k/pci.h ++++ b/drivers/net/wireless/ath/ath11k/pci.h +@@ -63,6 +63,7 @@ struct ath11k_msi_config { + int total_vectors; + int total_users; + struct ath11k_msi_user *users; ++ u16 hw_rev; + }; + + enum ath11k_pci_flags { +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -63,6 +63,76 @@ static const char *irq_name[ATH11K_IRQ_N + "tcl2host-status-ring", + }; + ++static const struct ath11k_msi_config ath11k_msi_config[] = { ++ { ++ .total_vectors = 32, ++ .total_users = 4, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, ++ { .name = "CE", .num_vectors = 10, .base_vector = 3 }, ++ { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, ++ { .name = "DP", .num_vectors = 18, .base_vector = 14 }, ++ }, ++ .hw_rev = ATH11K_HW_QCA6390_HW20, ++ }, ++ { ++ .total_vectors = 16, ++ .total_users = 3, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, ++ { .name = "CE", .num_vectors = 5, .base_vector = 3 }, ++ { .name = "DP", .num_vectors = 8, .base_vector = 8 }, ++ }, ++ .hw_rev = ATH11K_HW_QCN9074_HW10, ++ }, ++ { ++ .total_vectors = 32, ++ .total_users = 4, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, ++ { .name = "CE", .num_vectors = 10, .base_vector = 3 }, ++ { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, ++ { .name = "DP", .num_vectors = 18, .base_vector = 14 }, ++ }, ++ .hw_rev = ATH11K_HW_WCN6855_HW20, ++ }, ++ { ++ .total_vectors = 32, ++ .total_users = 4, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, ++ { .name = "CE", .num_vectors = 10, .base_vector = 3 }, ++ { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, ++ { .name = "DP", .num_vectors = 18, .base_vector = 14 }, ++ }, ++ .hw_rev = ATH11K_HW_WCN6855_HW21, ++ }, ++}; ++ ++int ath11k_pcic_init_msi_config(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ const struct ath11k_msi_config *msi_config; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) { ++ msi_config = &ath11k_msi_config[i]; ++ ++ if (msi_config->hw_rev == ab->hw_rev) ++ break; ++ } ++ ++ if (i == ARRAY_SIZE(ath11k_msi_config)) { ++ ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n", ++ ab->hw_rev); ++ return -EINVAL; ++ } ++ ++ ab_pci->msi_config = msi_config; ++ return 0; ++} ++EXPORT_SYMBOL(ath11k_pcic_init_msi_config); ++ + void ath11k_pcic_aspm_restore(struct ath11k_pci *ab_pci) + { + if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) +--- a/drivers/net/wireless/ath/ath11k/pcic.h ++++ b/drivers/net/wireless/ath/ath11k/pcic.h +@@ -50,4 +50,5 @@ int ath11k_get_user_msi_assignment(struc + void ath11k_pcic_aspm_restore(struct ath11k_pci *ab_pci); + int ath11k_pcic_set_irq_affinity_hint(struct ath11k_pci *ab_pci, + const struct cpumask *m); ++int ath11k_pcic_init_msi_config(struct ath11k_base *ab); + #endif diff --git a/package/kernel/mac80211/patches/ath11k/0218-ath11k-Refactor-MSI-logic-to-support-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0218-ath11k-Refactor-MSI-logic-to-support-WCN6750.patch new file mode 100644 index 00000000000000..30e0e6099dfae2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0218-ath11k-Refactor-MSI-logic-to-support-WCN6750.patch @@ -0,0 +1,299 @@ +From 0cfaf2243e9eef8ed32cdde6467a7e123a9f915f Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 1 Apr 2022 14:53:08 +0300 +Subject: [PATCH] ath11k: Refactor MSI logic to support WCN6750 + +Refactor MSI logic in order to support hybrid bus devices +like WCN6750. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220328055714.6449-5-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 22 ++++++++++++++ + drivers/net/wireless/ath/ath11k/mhi.c | 3 +- + drivers/net/wireless/ath/ath11k/pci.c | 29 +++++++++++------- + drivers/net/wireless/ath/ath11k/pci.h | 16 ---------- + drivers/net/wireless/ath/ath11k/pcic.c | 41 +++++--------------------- + drivers/net/wireless/ath/ath11k/pcic.h | 5 +--- + 6 files changed, 51 insertions(+), 65 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -769,6 +769,19 @@ struct ath11k_soc_dp_stats { + struct ath11k_dp_ring_bp_stats bp_stats; + }; + ++struct ath11k_msi_user { ++ char *name; ++ int num_vectors; ++ u32 base_vector; ++}; ++ ++struct ath11k_msi_config { ++ int total_vectors; ++ int total_users; ++ struct ath11k_msi_user *users; ++ u16 hw_rev; ++}; ++ + /* Master structure to hold the hw data which may be used in core module */ + struct ath11k_base { + enum ath11k_hw_rev hw_rev; +@@ -905,6 +918,15 @@ struct ath11k_base { + u32 subsystem_device; + } id; + ++ struct { ++ struct { ++ const struct ath11k_msi_config *config; ++ u32 ep_base_data; ++ u32 addr_lo; ++ u32 addr_hi; ++ } msi; ++ } pci; ++ + /* must be last */ + u8 drv_priv[] __aligned(sizeof(void *)); + }; +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -258,8 +258,7 @@ static int ath11k_mhi_get_msi(struct ath + int *irq; + unsigned int msi_data; + +- ret = ath11k_pcic_get_user_msi_assignment(ab_pci, +- "MHI", &num_vectors, ++ ret = ath11k_pcic_get_user_msi_assignment(ab, "MHI", &num_vectors, + &user_base_data, &base_vector); + if (ret) + return ret; +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -307,12 +307,13 @@ static void ath11k_pci_msi_disable(struc + static int ath11k_pci_alloc_msi(struct ath11k_pci *ab_pci) + { + struct ath11k_base *ab = ab_pci->ab; +- const struct ath11k_msi_config *msi_config = ab_pci->msi_config; ++ const struct ath11k_msi_config *msi_config = ab->pci.msi.config; ++ struct pci_dev *pci_dev = ab_pci->pdev; + struct msi_desc *msi_desc; + int num_vectors; + int ret; + +- num_vectors = pci_alloc_irq_vectors(ab_pci->pdev, ++ num_vectors = pci_alloc_irq_vectors(pci_dev, + msi_config->total_vectors, + msi_config->total_vectors, + PCI_IRQ_MSI); +@@ -329,7 +330,7 @@ static int ath11k_pci_alloc_msi(struct a + goto reset_msi_config; + } + clear_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags); +- ab_pci->msi_config = &msi_config_one_msi; ++ ab->pci.msi.config = &msi_config_one_msi; + ab_pci->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; + ath11k_dbg(ab, ATH11K_DBG_PCI, "request MSI one vector\n"); + } +@@ -344,11 +345,19 @@ static int ath11k_pci_alloc_msi(struct a + goto free_msi_vector; + } + +- ab_pci->msi_ep_base_data = msi_desc->msg.data; +- if (msi_desc->msi_attrib.is_64) +- set_bit(ATH11K_PCI_FLAG_IS_MSI_64, &ab_pci->flags); ++ ab->pci.msi.ep_base_data = msi_desc->msg.data; ++ ++ pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, ++ &ab->pci.msi.addr_lo); ++ ++ if (msi_desc->msi_attrib.is_64) { ++ pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, ++ &ab->pci.msi.addr_hi); ++ } else { ++ ab->pci.msi.addr_hi = 0; ++ } + +- ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab_pci->msi_ep_base_data); ++ ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab->pci.msi.ep_base_data); + + return 0; + +@@ -375,10 +384,10 @@ static int ath11k_pci_config_msi_data(st + return -EINVAL; + } + +- ab_pci->msi_ep_base_data = msi_desc->msg.data; ++ ab_pci->ab->pci.msi.ep_base_data = msi_desc->msg.data; + + ath11k_dbg(ab_pci->ab, ATH11K_DBG_PCI, "pci after request_irq msi_ep_base_data %d\n", +- ab_pci->msi_ep_base_data); ++ ab_pci->ab->pci.msi.ep_base_data); + + return 0; + } +@@ -562,7 +571,7 @@ static const struct ath11k_hif_ops ath11 + .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_get_user_msi_assignment, ++ .get_user_msi_vector = ath11k_pcic_get_user_msi_assignment, + .map_service_to_pipe = ath11k_pcic_map_service_to_pipe, + .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, + .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, +--- a/drivers/net/wireless/ath/ath11k/pci.h ++++ b/drivers/net/wireless/ath/ath11k/pci.h +@@ -53,22 +53,8 @@ + #define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c + #define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4 + +-struct ath11k_msi_user { +- char *name; +- int num_vectors; +- u32 base_vector; +-}; +- +-struct ath11k_msi_config { +- int total_vectors; +- int total_users; +- struct ath11k_msi_user *users; +- u16 hw_rev; +-}; +- + enum ath11k_pci_flags { + ATH11K_PCI_FLAG_INIT_DONE, +- ATH11K_PCI_FLAG_IS_MSI_64, + ATH11K_PCI_ASPM_RESTORE, + ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, + }; +@@ -78,9 +64,7 @@ struct ath11k_pci { + struct ath11k_base *ab; + u16 dev_id; + char amss_path[100]; +- u32 msi_ep_base_data; + struct mhi_controller *mhi_ctrl; +- const struct ath11k_msi_config *msi_config; + unsigned long mhi_state; + u32 register_window; + +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -111,7 +111,6 @@ static const struct ath11k_msi_config at + + int ath11k_pcic_init_msi_config(struct ath11k_base *ab) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + const struct ath11k_msi_config *msi_config; + int i; + +@@ -128,7 +127,7 @@ int ath11k_pcic_init_msi_config(struct a + return -EINVAL; + } + +- ab_pci->msi_config = msi_config; ++ ab->pci.msi.config = msi_config; + return 0; + } + EXPORT_SYMBOL(ath11k_pcic_init_msi_config); +@@ -267,33 +266,22 @@ int ath11k_pcic_get_msi_irq(struct devic + void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, + u32 *msi_addr_hi) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- struct pci_dev *pci_dev = to_pci_dev(ab->dev); +- +- pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, +- msi_addr_lo); +- +- if (test_bit(ATH11K_PCI_FLAG_IS_MSI_64, &ab_pci->flags)) { +- pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, +- msi_addr_hi); +- } else { +- *msi_addr_hi = 0; +- } ++ *msi_addr_lo = ab->pci.msi.addr_lo; ++ *msi_addr_hi = ab->pci.msi.addr_hi; + } + +-int ath11k_pcic_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_name, ++int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector) + { +- struct ath11k_base *ab = ab_pci->ab; +- const struct ath11k_msi_config *msi_config = ab_pci->msi_config; ++ const struct ath11k_msi_config *msi_config = ab->pci.msi.config; + int idx; + + for (idx = 0; idx < msi_config->total_users; idx++) { + if (strcmp(user_name, msi_config->users[idx].name) == 0) { + *num_vectors = msi_config->users[idx].num_vectors; + *base_vector = msi_config->users[idx].base_vector; +- *user_base_data = *base_vector + ab_pci->msi_ep_base_data; ++ *user_base_data = *base_vector + ab->pci.msi.ep_base_data; + + ath11k_dbg(ab, ATH11K_DBG_PCI, + "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", +@@ -325,17 +313,6 @@ void ath11k_pcic_get_ce_msi_idx(struct a + *msi_idx = msi_data_idx; + } + +-int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, +- int *num_vectors, u32 *user_base_data, +- u32 *base_vector) +-{ +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- +- return ath11k_pcic_get_user_msi_assignment(ab_pci, user_name, +- num_vectors, user_base_data, +- base_vector); +-} +- + static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab) + { + int i, j; +@@ -586,8 +563,7 @@ static int ath11k_pcic_ext_irq_config(st + int i, j, ret, num_vectors = 0; + u32 user_base_data = 0, base_vector = 0; + +- ret = ath11k_pcic_get_user_msi_assignment(ath11k_pci_priv(ab), "DP", +- &num_vectors, ++ ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors, + &user_base_data, + &base_vector); + if (ret < 0) +@@ -662,8 +638,7 @@ int ath11k_pcic_config_irq(struct ath11k + unsigned int msi_data; + int irq, i, ret, irq_idx; + +- ret = ath11k_pcic_get_user_msi_assignment(ath11k_pci_priv(ab), +- "CE", &msi_data_count, ++ ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count, + &msi_data_start, &msi_irq_start); + if (ret) + return ret; +--- a/drivers/net/wireless/ath/ath11k/pcic.h ++++ b/drivers/net/wireless/ath/ath11k/pcic.h +@@ -25,7 +25,7 @@ + */ + #define ATH11K_PCI_ACCESS_ALWAYS_OFF 0xFE0 + +-int ath11k_pcic_get_user_msi_assignment(struct ath11k_pci *ar_pci, char *user_name, ++int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector); + int ath11k_pcic_get_msi_irq(struct device *dev, unsigned int vector); +@@ -44,9 +44,6 @@ int ath11k_pcic_map_service_to_pipe(stru + u8 *ul_pipe, u8 *dl_pipe); + void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab); + void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab); +-int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, +- int *num_vectors, u32 *user_base_data, +- u32 *base_vector); + void ath11k_pcic_aspm_restore(struct ath11k_pci *ab_pci); + int ath11k_pcic_set_irq_affinity_hint(struct ath11k_pci *ab_pci, + const struct cpumask *m); diff --git a/package/kernel/mac80211/patches/ath11k/0219-ath11k-Remove-core-PCI-references-from-PCI-common-co.patch b/package/kernel/mac80211/patches/ath11k/0219-ath11k-Remove-core-PCI-references-from-PCI-common-co.patch new file mode 100644 index 00000000000000..ef7cb1ec80e892 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0219-ath11k-Remove-core-PCI-references-from-PCI-common-co.patch @@ -0,0 +1,845 @@ +From 5b32b6dd966338005671780c1df02327582c4be4 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 1 Apr 2022 14:53:08 +0300 +Subject: [PATCH] ath11k: Remove core PCI references from PCI common code + +Remove core PCI and ath11k PCI references(struct ath11k_pci) +from PCI common code. Since, PCI common code will be used +by hybrid bus devices, this code should be independent +from ATH11K PCI references and Linux core PCI references +like struct pci_dev. + +Since this change introduces function callbacks for bus wakeup +and bus release operations, wakeup_mhi HW param is no longer +needed and hence it is removed completely. Alternatively, bus +wakeup/release ops for QCA9074 are initialized to NULL as +QCA9704 does not need bus wakeup/release for register accesses. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220328055714.6449-6-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 6 - + drivers/net/wireless/ath/ath11k/core.h | 12 ++ + drivers/net/wireless/ath/ath11k/hw.h | 2 +- + drivers/net/wireless/ath/ath11k/mhi.c | 6 +- + drivers/net/wireless/ath/ath11k/pci.c | 146 +++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/pci.h | 5 +- + drivers/net/wireless/ath/ath11k/pcic.c | 169 +++++++++---------------- + drivers/net/wireless/ath/ath11k/pcic.h | 5 - + 8 files changed, 211 insertions(+), 140 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -96,7 +96,6 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, +- .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, + .current_cc_support = false, +@@ -163,7 +162,6 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, +- .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, + .current_cc_support = false, +@@ -229,7 +227,6 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, +- .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, +@@ -295,7 +292,6 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = true, + .alloc_cacheable_memory = true, +- .wakeup_mhi = false, + .supports_rssi_stats = false, + .fw_wmi_diag_event = false, + .current_cc_support = false, +@@ -361,7 +357,6 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, +- .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, +@@ -426,7 +421,6 @@ static const struct ath11k_hw_params ath + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, +- .wakeup_mhi = true, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -239,6 +239,8 @@ enum ath11k_dev_flags { + ATH11K_FLAG_CE_IRQ_ENABLED, + ATH11K_FLAG_EXT_IRQ_ENABLED, + ATH11K_FLAG_FIXED_MEM_RGN, ++ ATH11K_FLAG_DEVICE_INIT_DONE, ++ ATH11K_FLAG_MULTI_MSI_VECTORS, + }; + + enum ath11k_monitor_flags { +@@ -728,6 +730,14 @@ struct ath11k_bus_params { + bool static_window_map; + }; + ++struct ath11k_pci_ops { ++ int (*wakeup)(struct ath11k_base *ab); ++ void (*release)(struct ath11k_base *ab); ++ int (*get_msi_irq)(struct ath11k_base *ab, unsigned int vector); ++ void (*window_write32)(struct ath11k_base *ab, u32 offset, u32 value); ++ u32 (*window_read32)(struct ath11k_base *ab, u32 offset); ++}; ++ + /* IPQ8074 HW channel counters frequency value in hertz */ + #define IPQ8074_CC_FREQ_HERTZ 320000 + +@@ -925,6 +935,8 @@ struct ath11k_base { + u32 addr_lo; + u32 addr_hi; + } msi; ++ ++ const struct ath11k_pci_ops *ops; + } pci; + + /* must be last */ +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #ifndef ATH11K_HW_H +@@ -189,7 +190,6 @@ struct ath11k_hw_params { + const struct ath11k_hw_hal_params *hal_params; + bool supports_dynamic_smps_6ghz; + bool alloc_cacheable_memory; +- bool wakeup_mhi; + bool supports_rssi_stats; + bool fw_wmi_diag_event; + bool current_cc_support; +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -273,10 +273,10 @@ static int ath11k_mhi_get_msi(struct ath + for (i = 0; i < num_vectors; i++) { + msi_data = base_vector; + +- if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + msi_data += i; + +- irq[i] = ath11k_pcic_get_msi_irq(ab->dev, msi_data); ++ irq[i] = ath11k_pci_get_msi_irq(ab, msi_data); + } + + ab_pci->mhi_ctrl->irq = irq; +@@ -408,7 +408,7 @@ int ath11k_mhi_register(struct ath11k_pc + return ret; + } + +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; + + if (test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -36,6 +36,85 @@ static const struct pci_device_id ath11k + + MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table); + ++static int ath11k_pci_bus_wake_up(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ ++ return mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); ++} ++ ++static void ath11k_pci_bus_release(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ ++ mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); ++} ++ ++static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset) ++{ ++ struct ath11k_base *ab = ab_pci->ab; ++ ++ u32 window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, offset); ++ ++ lockdep_assert_held(&ab_pci->window_lock); ++ ++ if (window != ab_pci->register_window) { ++ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, ++ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++ ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++ ab_pci->register_window = window; ++ } ++} ++ ++static void ++ath11k_pci_window_write32(struct ath11k_base *ab, u32 offset, u32 value) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ u32 window_start = ATH11K_PCI_WINDOW_START; ++ ++ spin_lock_bh(&ab_pci->window_lock); ++ ath11k_pci_select_window(ab_pci, offset); ++ iowrite32(value, ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ spin_unlock_bh(&ab_pci->window_lock); ++} ++ ++static u32 ath11k_pci_window_read32(struct ath11k_base *ab, u32 offset) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ u32 window_start = ATH11K_PCI_WINDOW_START; ++ u32 val; ++ ++ spin_lock_bh(&ab_pci->window_lock); ++ ath11k_pci_select_window(ab_pci, offset); ++ val = ioread32(ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ spin_unlock_bh(&ab_pci->window_lock); ++ ++ return val; ++} ++ ++int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector) ++{ ++ struct pci_dev *pci_dev = to_pci_dev(ab->dev); ++ ++ return pci_irq_vector(pci_dev, vector); ++} ++ ++static const struct ath11k_pci_ops ath11k_pci_ops_qca6390 = { ++ .wakeup = ath11k_pci_bus_wake_up, ++ .release = ath11k_pci_bus_release, ++ .get_msi_irq = ath11k_pci_get_msi_irq, ++ .window_write32 = ath11k_pci_window_write32, ++ .window_read32 = ath11k_pci_window_read32, ++}; ++ ++static const struct ath11k_pci_ops ath11k_pci_ops_qcn9074 = { ++ .get_msi_irq = ath11k_pci_get_msi_irq, ++ .window_write32 = ath11k_pci_window_write32, ++ .window_read32 = ath11k_pci_window_read32, ++}; ++ + static const struct ath11k_bus_params ath11k_pci_bus_params = { + .mhi_support = true, + .m3_fw_support = true, +@@ -318,8 +397,7 @@ static int ath11k_pci_alloc_msi(struct a + msi_config->total_vectors, + PCI_IRQ_MSI); + if (num_vectors == msi_config->total_vectors) { +- set_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags); +- ab_pci->irq_flags = IRQF_SHARED; ++ set_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags); + } else { + num_vectors = pci_alloc_irq_vectors(ab_pci->pdev, + 1, +@@ -329,9 +407,8 @@ static int ath11k_pci_alloc_msi(struct a + ret = -EINVAL; + goto reset_msi_config; + } +- clear_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags); ++ clear_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags); + ab->pci.msi.config = &msi_config_one_msi; +- ab_pci->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; + ath11k_dbg(ab, ATH11K_DBG_PCI, "request MSI one vector\n"); + } + ath11k_info(ab, "MSI vectors: %d\n", num_vectors); +@@ -487,13 +564,20 @@ static void ath11k_pci_aspm_disable(stru + set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags); + } + ++static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) ++{ ++ if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) ++ pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL, ++ ab_pci->link_ctl); ++} ++ + static int ath11k_pci_power_up(struct ath11k_base *ab) + { + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + int ret; + + ab_pci->register_window = 0; +- clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); ++ clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); + ath11k_pci_sw_reset(ab_pci->ab, true); + + /* Disable ASPM during firmware download due to problems switching +@@ -520,14 +604,14 @@ static void ath11k_pci_power_down(struct + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + /* restore aspm in case firmware bootup fails */ +- ath11k_pcic_aspm_restore(ab_pci); ++ ath11k_pci_aspm_restore(ab_pci); + + ath11k_pci_force_wake(ab_pci->ab); + + ath11k_pci_msi_disable(ab_pci); + + ath11k_mhi_stop(ab_pci); +- clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); ++ clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); + ath11k_pci_sw_reset(ab_pci->ab, false); + } + +@@ -559,8 +643,25 @@ static void ath11k_pci_hif_ce_irq_disabl + ath11k_pcic_ce_irq_disable_sync(ab); + } + ++static int ath11k_pci_start(struct ath11k_base *ab) ++{ ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ++ ++ /* TODO: for now don't restore ASPM in case of single MSI ++ * vector as MHI register reading in M2 causes system hang. ++ */ ++ if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) ++ ath11k_pci_aspm_restore(ab_pci); ++ else ++ ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n"); ++ ++ ath11k_pcic_start(ab); ++ ++ return 0; ++} ++ + static const struct ath11k_hif_ops ath11k_pci_hif_ops = { +- .start = ath11k_pcic_start, ++ .start = ath11k_pci_start, + .stop = ath11k_pcic_stop, + .read32 = ath11k_pcic_read32, + .write32 = ath11k_pcic_write32, +@@ -592,6 +693,15 @@ static void ath11k_pci_read_hw_version(s + *major, *minor); + } + ++static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, ++ const struct cpumask *m) ++{ ++ if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab_pci->ab->dev_flags)) ++ return 0; ++ ++ return irq_set_affinity_hint(ab_pci->pdev->irq, m); ++} ++ + static int ath11k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_dev) + { +@@ -654,9 +764,12 @@ static int ath11k_pci_probe(struct pci_d + ret = -EOPNOTSUPP; + goto err_pci_free_region; + } ++ ++ ab->pci.ops = &ath11k_pci_ops_qca6390; + break; + case QCN9074_DEVICE_ID: + ab->bus_params.static_window_map = true; ++ ab->pci.ops = &ath11k_pci_ops_qcn9074; + ab->hw_rev = ATH11K_HW_QCN9074_HW10; + break; + case WCN6855_DEVICE_ID: +@@ -685,6 +798,8 @@ unsupported_wcn6855_soc: + ret = -EOPNOTSUPP; + goto err_pci_free_region; + } ++ ++ ab->pci.ops = &ath11k_pci_ops_qca6390; + break; + default: + dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n", +@@ -733,6 +848,12 @@ unsupported_wcn6855_soc: + goto err_ce_free; + } + ++ ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0)); ++ if (ret) { ++ ath11k_err(ab, "failed to set irq affinity %d\n", ret); ++ goto err_free_irq; ++ } ++ + /* kernel may allocate a dummy vector before request_irq and + * then allocate a real vector when request_irq is called. + * So get msi_data here again to avoid spurious interrupt +@@ -741,16 +862,19 @@ unsupported_wcn6855_soc: + ret = ath11k_pci_config_msi_data(ab_pci); + if (ret) { + ath11k_err(ab, "failed to config msi_data: %d\n", ret); +- goto err_free_irq; ++ goto err_irq_affinity_cleanup; + } + + ret = ath11k_core_init(ab); + if (ret) { + ath11k_err(ab, "failed to init core: %d\n", ret); +- goto err_free_irq; ++ goto err_irq_affinity_cleanup; + } + return 0; + ++err_irq_affinity_cleanup: ++ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); ++ + err_free_irq: + ath11k_pcic_free_irq(ab); + +@@ -780,7 +904,7 @@ static void ath11k_pci_remove(struct pci + struct ath11k_base *ab = pci_get_drvdata(pdev); + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + +- ath11k_pcic_set_irq_affinity_hint(ab_pci, NULL); ++ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); + + if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath11k_pci_power_down(ab); +--- a/drivers/net/wireless/ath/ath11k/pci.h ++++ b/drivers/net/wireless/ath/ath11k/pci.h +@@ -54,9 +54,7 @@ + #define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4 + + enum ath11k_pci_flags { +- ATH11K_PCI_FLAG_INIT_DONE, + ATH11K_PCI_ASPM_RESTORE, +- ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, + }; + + struct ath11k_pci { +@@ -74,8 +72,6 @@ struct ath11k_pci { + /* enum ath11k_pci_flags */ + unsigned long flags; + u16 link_ctl; +- +- unsigned long irq_flags; + }; + + static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) +@@ -83,4 +79,5 @@ static inline struct ath11k_pci *ath11k_ + return (struct ath11k_pci *)ab->drv_priv; + } + ++int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector); + #endif +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -4,7 +4,6 @@ + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +-#include + #include "core.h" + #include "pcic.h" + #include "debug.h" +@@ -132,29 +131,6 @@ int ath11k_pcic_init_msi_config(struct a + } + EXPORT_SYMBOL(ath11k_pcic_init_msi_config); + +-void ath11k_pcic_aspm_restore(struct ath11k_pci *ab_pci) +-{ +- if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) +- pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL, +- ab_pci->link_ctl); +-} +- +-static inline void ath11k_pcic_select_window(struct ath11k_pci *ab_pci, u32 offset) +-{ +- struct ath11k_base *ab = ab_pci->ab; +- +- u32 window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, offset); +- +- lockdep_assert_held(&ab_pci->window_lock); +- +- if (window != ab_pci->register_window) { +- iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, +- ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +- ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); +- ab_pci->register_window = window; +- } +-} +- + static inline u32 ath11k_pcic_get_window_start(struct ath11k_base *ab, + u32 offset) + { +@@ -174,17 +150,15 @@ static inline u32 ath11k_pcic_get_window + + void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 window_start; + int ret = 0; + + /* for offset beyond BAR + 4K - 32, may +- * need to wakeup MHI to access. ++ * need to wakeup the device to access. + */ +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) +- ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); ++ if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->wakeup) ++ ret = ab->pci.ops->wakeup(ab); + + if (offset < ATH11K_PCI_WINDOW_START) { + iowrite32(value, ab->mem + offset); +@@ -194,38 +168,32 @@ void ath11k_pcic_write32(struct ath11k_b + else + window_start = ATH11K_PCI_WINDOW_START; + +- if (window_start == ATH11K_PCI_WINDOW_START) { +- spin_lock_bh(&ab_pci->window_lock); +- ath11k_pcic_select_window(ab_pci, offset); +- iowrite32(value, ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- spin_unlock_bh(&ab_pci->window_lock); ++ if (window_start == ATH11K_PCI_WINDOW_START && ++ ab->pci.ops->window_write32) { ++ ab->pci.ops->window_write32(ab, offset, value); + } else { + iowrite32(value, ab->mem + window_start + + (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + } + } + +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ++ if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->release && + !ret) +- mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); ++ ab->pci.ops->release(ab); + } + + u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 val, window_start; + int ret = 0; + + /* for offset beyond BAR + 4K - 32, may +- * need to wakeup MHI to access. ++ * need to wakeup the device to access. + */ +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF) +- ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); ++ if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->wakeup) ++ ret = ab->pci.ops->wakeup(ab); + + if (offset < ATH11K_PCI_WINDOW_START) { + val = ioread32(ab->mem + offset); +@@ -235,34 +203,23 @@ u32 ath11k_pcic_read32(struct ath11k_bas + else + window_start = ATH11K_PCI_WINDOW_START; + +- if (window_start == ATH11K_PCI_WINDOW_START) { +- spin_lock_bh(&ab_pci->window_lock); +- ath11k_pcic_select_window(ab_pci, offset); +- val = ioread32(ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- spin_unlock_bh(&ab_pci->window_lock); ++ if (window_start == ATH11K_PCI_WINDOW_START && ++ ab->pci.ops->window_read32) { ++ val = ab->pci.ops->window_read32(ab, offset); + } else { + val = ioread32(ab->mem + window_start + + (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); + } + } + +- if (ab->hw_params.wakeup_mhi && +- test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && +- offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ++ if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && ++ offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->release && + !ret) +- mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); ++ ab->pci.ops->release(ab); + + return val; + } + +-int ath11k_pcic_get_msi_irq(struct device *dev, unsigned int vector) +-{ +- struct pci_dev *pci_dev = to_pci_dev(dev); +- +- return pci_irq_vector(pci_dev, vector); +-} +- + void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, + u32 *msi_addr_hi) + { +@@ -343,13 +300,12 @@ void ath11k_pcic_free_irq(struct ath11k_ + + static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 irq_idx; + + /* In case of one MSI vector, we handle irq enable/disable in a + * uniform way since we only have one irq + */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + return; + + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; +@@ -358,13 +314,12 @@ static void ath11k_pcic_ce_irq_enable(st + + static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 irq_idx; + + /* In case of one MSI vector, we handle irq enable/disable in a + * uniform way since we only have one irq + */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + return; + + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; +@@ -429,13 +384,13 @@ static irqreturn_t ath11k_pcic_ce_interr + + static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); ++ struct ath11k_base *ab = irq_grp->ab; + int i; + + /* In case of one MSI vector, we handle irq enable/disable + * in a uniform way since we only have one irq + */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + return; + + for (i = 0; i < irq_grp->num_irq; i++) +@@ -463,13 +418,13 @@ static void __ath11k_pcic_ext_irq_disabl + + static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab); ++ struct ath11k_base *ab = irq_grp->ab; + int i; + + /* In case of one MSI vector, we handle irq enable/disable in a + * uniform way since we only have one irq + */ +- if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) ++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + return; + + for (i = 0; i < irq_grp->num_irq; i++) +@@ -557,11 +512,22 @@ static irqreturn_t ath11k_pcic_ext_inter + return IRQ_HANDLED; + } + ++static int ++ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector) ++{ ++ if (!ab->pci.ops->get_msi_irq) { ++ WARN_ONCE(1, "get_msi_irq pci op not defined"); ++ return -EOPNOTSUPP; ++ } ++ ++ return ab->pci.ops->get_msi_irq(ab, vector); ++} ++ + static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + int i, j, ret, num_vectors = 0; + u32 user_base_data = 0, base_vector = 0; ++ unsigned long irq_flags; + + ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors, + &user_base_data, +@@ -569,6 +535,10 @@ static int ath11k_pcic_ext_irq_config(st + if (ret < 0) + return ret; + ++ irq_flags = IRQF_SHARED; ++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) ++ irq_flags |= IRQF_NOBALANCING; ++ + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + u32 num_irq = 0; +@@ -596,7 +566,10 @@ static int ath11k_pcic_ext_irq_config(st + for (j = 0; j < irq_grp->num_irq; j++) { + int irq_idx = irq_grp->irqs[j]; + int vector = (i % num_vectors) + base_vector; +- int irq = ath11k_pcic_get_msi_irq(ab->dev, vector); ++ int irq = ath11k_pcic_get_msi_irq(ab, vector); ++ ++ if (irq < 0) ++ return irq; + + ab->irq_num[irq_idx] = irq; + +@@ -605,8 +578,7 @@ static int ath11k_pcic_ext_irq_config(st + + irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); + ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler, +- ab_pci->irq_flags, +- "DP_EXT_IRQ", irq_grp); ++ irq_flags, "DP_EXT_IRQ", irq_grp); + if (ret) { + ath11k_err(ab, "failed request irq %d: %d\n", + vector, ret); +@@ -619,35 +591,24 @@ static int ath11k_pcic_ext_irq_config(st + return 0; + } + +-int ath11k_pcic_set_irq_affinity_hint(struct ath11k_pci *ab_pci, +- const struct cpumask *m) +-{ +- if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- return 0; +- +- return irq_set_affinity_hint(ab_pci->pdev->irq, m); +-} +- + int ath11k_pcic_config_irq(struct ath11k_base *ab) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct ath11k_ce_pipe *ce_pipe; + u32 msi_data_start; + u32 msi_data_count, msi_data_idx; + u32 msi_irq_start; + unsigned int msi_data; + int irq, i, ret, irq_idx; ++ unsigned long irq_flags; + + ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count, + &msi_data_start, &msi_irq_start); + if (ret) + return ret; + +- ret = ath11k_pcic_set_irq_affinity_hint(ab_pci, cpumask_of(0)); +- if (ret) { +- ath11k_err(ab, "failed to set irq affinity %d\n", ret); +- return ret; +- } ++ irq_flags = IRQF_SHARED; ++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) ++ irq_flags |= IRQF_NOBALANCING; + + /* Configure CE irqs */ + for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { +@@ -655,7 +616,10 @@ int ath11k_pcic_config_irq(struct ath11k + continue; + + msi_data = (msi_data_idx % msi_data_count) + msi_irq_start; +- irq = ath11k_pcic_get_msi_irq(ab->dev, msi_data); ++ irq = ath11k_pcic_get_msi_irq(ab, msi_data); ++ if (irq < 0) ++ return irq; ++ + ce_pipe = &ab->ce.ce_pipe[i]; + + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; +@@ -663,12 +627,11 @@ int ath11k_pcic_config_irq(struct ath11k + tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet); + + ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler, +- ab_pci->irq_flags, irq_name[irq_idx], +- ce_pipe); ++ irq_flags, irq_name[irq_idx], ce_pipe); + if (ret) { + ath11k_err(ab, "failed to request irq %d: %d\n", + irq_idx, ret); +- goto err_irq_affinity_cleanup; ++ return ret; + } + + ab->irq_num[irq_idx] = irq; +@@ -679,13 +642,9 @@ int ath11k_pcic_config_irq(struct ath11k + + ret = ath11k_pcic_ext_irq_config(ab); + if (ret) +- goto err_irq_affinity_cleanup; ++ return ret; + + return 0; +- +-err_irq_affinity_cleanup: +- ath11k_pcic_set_irq_affinity_hint(ab_pci, NULL); +- return ret; + } + + void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab) +@@ -730,17 +689,7 @@ void ath11k_pcic_stop(struct ath11k_base + + int ath11k_pcic_start(struct ath11k_base *ab) + { +- struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); +- +- set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); +- +- /* TODO: for now don't restore ASPM in case of single MSI +- * vector as MHI register reading in M2 causes system hang. +- */ +- if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) +- ath11k_pcic_aspm_restore(ab_pci); +- else +- ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n"); ++ set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); + + ath11k_pcic_ce_irqs_enable(ab); + ath11k_ce_rx_post_buf(ab); +--- a/drivers/net/wireless/ath/ath11k/pcic.h ++++ b/drivers/net/wireless/ath/ath11k/pcic.h +@@ -8,7 +8,6 @@ + #define _ATH11K_PCI_CMN_H + + #include "core.h" +-#include "pci.h" + + #define ATH11K_PCI_IRQ_CE0_OFFSET 3 + #define ATH11K_PCI_IRQ_DP_OFFSET 14 +@@ -28,7 +27,6 @@ + int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector); +-int ath11k_pcic_get_msi_irq(struct device *dev, unsigned int vector); + void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value); + u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset); + void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, +@@ -44,8 +42,5 @@ int ath11k_pcic_map_service_to_pipe(stru + u8 *ul_pipe, u8 *dl_pipe); + void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab); + void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab); +-void ath11k_pcic_aspm_restore(struct ath11k_pci *ab_pci); +-int ath11k_pcic_set_irq_affinity_hint(struct ath11k_pci *ab_pci, +- const struct cpumask *m); + int ath11k_pcic_init_msi_config(struct ath11k_base *ab); + #endif diff --git a/package/kernel/mac80211/patches/ath11k/0220-ath11k-Change-max-no-of-active-probe-SSID-and-BSSID-.patch b/package/kernel/mac80211/patches/ath11k/0220-ath11k-Change-max-no-of-active-probe-SSID-and-BSSID-.patch new file mode 100644 index 00000000000000..98259dbb13eb2c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0220-ath11k-Change-max-no-of-active-probe-SSID-and-BSSID-.patch @@ -0,0 +1,70 @@ +From 50dc9ce9f80554a88e33b73c30851acf2be36ed3 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Kathirvel +Date: Fri, 1 Apr 2022 14:53:09 +0300 +Subject: [PATCH] ath11k: Change max no of active probe SSID and BSSID to fw + capability + +The maximum number of SSIDs in a for active probe requests is currently +reported as 16 (WLAN_SCAN_PARAMS_MAX_SSID) when registering the driver. +The scan_req_params structure only has the capacity to hold 10 SSIDs. +This leads to a buffer overflow which can be triggered from +wpa_supplicant in userspace. When copying the SSIDs into the +scan_req_params structure in the ath11k_mac_op_hw_scan route, it can +overwrite the extraie pointer. + +Firmware supports 16 ssid * 4 bssid, for each ssid 4 bssid combo probe +request will be sent, so totally 64 probe requests supported. So +set both max ssid and bssid to 16 and 4 respectively. Remove the +redundant macros of ssid and bssid. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01300-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220329150221.21907-1-quic_kathirve@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.h | 12 ++---------- + 1 file changed, 2 insertions(+), 10 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3089,9 +3089,6 @@ enum scan_dwelltime_adaptive_mode { + SCAN_DWELL_MODE_STATIC = 4 + }; + +-#define WLAN_SCAN_MAX_NUM_SSID 10 +-#define WLAN_SCAN_MAX_NUM_BSSID 10 +- + #define WLAN_SSID_MAX_LEN 32 + + struct element_info { +@@ -3106,7 +3103,6 @@ struct wlan_ssid { + + #define WMI_IE_BITMAP_SIZE 8 + +-#define WMI_SCAN_MAX_NUM_SSID 0x0A + /* prefix used by scan requestor ids on the host */ + #define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000 + +@@ -3114,10 +3110,6 @@ struct wlan_ssid { + /* host cycles through the lower 12 bits to generate ids */ + #define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000 + +-#define WLAN_SCAN_PARAMS_MAX_SSID 16 +-#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +-#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 +- + /* Values lower than this may be refused by some firmware revisions with a scan + * completion with a timedout reason. + */ +@@ -3313,8 +3305,8 @@ struct scan_req_params { + u32 n_probes; + u32 *chan_list; + u32 notify_scan_events; +- struct wlan_ssid ssid[WLAN_SCAN_MAX_NUM_SSID]; +- struct wmi_mac_addr bssid_list[WLAN_SCAN_MAX_NUM_BSSID]; ++ struct wlan_ssid ssid[WLAN_SCAN_PARAMS_MAX_SSID]; ++ struct wmi_mac_addr bssid_list[WLAN_SCAN_PARAMS_MAX_BSSID]; + struct element_info extraie; + struct element_info htcap; + struct element_info vhtcap; diff --git a/package/kernel/mac80211/patches/ath11k/0221-ath11k-Remove-unnecessary-delay-in-ath11k_core_suspe.patch b/package/kernel/mac80211/patches/ath11k/0221-ath11k-Remove-unnecessary-delay-in-ath11k_core_suspe.patch new file mode 100644 index 00000000000000..34d1a4875d9812 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0221-ath11k-Remove-unnecessary-delay-in-ath11k_core_suspe.patch @@ -0,0 +1,36 @@ +From 2dd398dee7aa5ec6b296d9915bbb1c1a76199b4a Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Fri, 1 Apr 2022 14:53:10 +0300 +Subject: [PATCH] ath11k: Remove unnecessary delay in ath11k_core_suspend + +The intended delay in ath11k_core_suspend is introduced in commit +d1b0c33850d2 ("ath11k: implement suspend for QCA6390 PCI devices"), +now with ath11k_mac_wait_tx_complete added in commit ba9177fcef21 +("ath11k: Add basic WoW functionalities"), that delay is not +necessary now, so remove it. + +This is found in code review. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-02431-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220331002105.1162099-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 5 ----- + 1 file changed, 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -453,11 +453,6 @@ int ath11k_core_suspend(struct ath11k_ba + if (!ar || ar->state != ATH11K_STATE_OFF) + return 0; + +- /* TODO: there can frames in queues so for now add delay as a hack. +- * Need to implement to handle and remove this delay. +- */ +- msleep(500); +- + ret = ath11k_dp_rx_pktlog_stop(ab, true); + if (ret) { + ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n", diff --git a/package/kernel/mac80211/patches/ath11k/0222-ath11k-fix-driver-initialization-failure-with-WoW-un.patch b/package/kernel/mac80211/patches/ath11k/0222-ath11k-fix-driver-initialization-failure-with-WoW-un.patch new file mode 100644 index 00000000000000..374d3d70ba14ad --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0222-ath11k-fix-driver-initialization-failure-with-WoW-un.patch @@ -0,0 +1,72 @@ +From 633469e3bac10650ecff421ba6b9603d67da884b Mon Sep 17 00:00:00 2001 +From: Nagarajan Maran +Date: Fri, 1 Apr 2022 14:53:10 +0300 +Subject: [PATCH] ath11k: fix driver initialization failure with WoW + unsupported hw + +In the "ath11k_wow_init", error value "EINVAL" is returned +when the check for firmware support of WoW feature fails, +which in turn stops the driver initialization. + +Warning message: +[ 31.040144] ------------[ cut here ]------------ +[ 31.040185] WARNING: CPU: 1 PID: 51 at drivers/net/wireless/ath/ath11k/wow.c:813 ath11k_wow_init+0xc8/0x13a8 [ath11k] +[ 31.043846] Modules linked in: ath11k_pci ath11k qmi_helpers +[ 31.054341] CPU: 1 PID: 51 Comm: kworker/u8:1 Tainted: G W 5.17.0-wt-ath-594817-ga7f6aa925cf8-dirty #17 +[ 31.060078] Hardware name: Qualcomm Technologies, Inc. IPQ8074/AP-HK10-C2 (DT) +[ 31.070578] Workqueue: ath11k_qmi_driver_event ath11k_qmi_driver_event_work [ath11k] +[ 31.077782] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) +[ 31.085676] pc : ath11k_wow_init+0xc8/0x13a8 [ath11k] +[ 31.092359] lr : ath11k_mac_register+0x548/0xb98 [ath11k] +[ 31.097567] sp : ffff80000aa13c40 +[ 31.102944] x29: ffff80000aa13c40 x28: ffff800009184390 x27: ffff000002959f20 +[ 31.106251] x26: ffff000002828000 x25: ffff000002830000 x24: ffff000002830000 +[ 31.113369] x23: ffff000002820000 x22: ffff00000282854c x21: 0000000000000000 +[ 31.120487] x20: ffff00000295cf20 x19: ffff000002828540 x18: 0000000000000031 +[ 31.127605] x17: 0000000000000004 x16: ffff0000028285fc x15: ffff00000295b040 +[ 31.134723] x14: 0000000000000067 x13: ffff00000282859c x12: 000000000000000d +[ 31.141840] x11: 0000000000000018 x10: 0000000000000004 x9 : 0000000000000000 +[ 31.148959] x8 : ffff00000289d680 x7 : 0000000000000000 x6 : 000000000000003f +[ 31.156077] x5 : 0000000000000040 x4 : 0000000000000000 x3 : ffff000002820968 +[ 31.163196] x2 : 0000000000000080 x1 : 0080008af9981779 x0 : ffff000002959f20 +[ 31.170314] Call trace: +[ 31.177421] ath11k_wow_init+0xc8/0x13a8 [ath11k] +[ 31.179684] ath11k_core_qmi_firmware_ready+0x430/0x5e0 [ath11k] +[ 31.184548] ath11k_qmi_driver_event_work+0x16c/0x4f8 [ath11k] +[ 31.190623] process_one_work+0x134/0x350 +[ 31.196262] worker_thread+0x12c/0x450 +[ 31.200340] kthread+0xf4/0x110 +[ 31.203986] ret_from_fork+0x10/0x20 +[ 31.207026] ---[ end trace 0000000000000000 ]--- +[ 31.210894] ath11k_pci 0000:01:00.0: failed to init wow: -22 +[ 31.215467] ath11k_pci 0000:01:00.0: failed register the radio with mac80211: -22 +[ 31.221117] ath11k_pci 0000:01:00.0: failed to create pdev core: -22 + +Fix this by returning value "0" when FW doesn't support WoW +to allow driver to proceed with initialize sequence and also +remove the unnecessary "WARN_ON". + + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Fixes: ba9177fcef21 ("ath11k: Add basic WoW functionalities") +Signed-off-by: Nagarajan Maran +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220331073110.3846-1-quic_nmaran@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wow.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -810,8 +810,8 @@ exit: + + int ath11k_wow_init(struct ath11k *ar) + { +- if (WARN_ON(!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))) +- return -EINVAL; ++ if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map)) ++ return 0; + + ar->wow.wowlan_support = ath11k_wowlan_support; + diff --git a/package/kernel/mac80211/patches/ath11k/0223-ath11k-mhi-remove-state-machine.patch b/package/kernel/mac80211/patches/ath11k/0223-ath11k-mhi-remove-state-machine.patch new file mode 100644 index 00000000000000..0ddec2ffe6a261 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0223-ath11k-mhi-remove-state-machine.patch @@ -0,0 +1,278 @@ +From 121210ec935c47b076709974d95360f5e9c9b869 Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Fri, 1 Apr 2022 20:30:40 +0300 +Subject: [PATCH] ath11k: mhi: remove state machine + +State machines are difficult to understand and in this case it's just useless, +which is shown by the diffstat. So remove it entirely to make the code simpler. + +No functional changes. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220401173042.17467-2-kvalo@kernel.org +--- + drivers/net/wireless/ath/ath11k/mhi.c | 194 ++------------------------ + drivers/net/wireless/ath/ath11k/mhi.h | 13 -- + drivers/net/wireless/ath/ath11k/pci.h | 2 +- + 3 files changed, 11 insertions(+), 198 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -465,195 +465,17 @@ void ath11k_mhi_unregister(struct ath11k + mhi_free_controller(mhi_ctrl); + } + +-static char *ath11k_mhi_state_to_str(enum ath11k_mhi_state mhi_state) +-{ +- switch (mhi_state) { +- case ATH11K_MHI_INIT: +- return "INIT"; +- case ATH11K_MHI_DEINIT: +- return "DEINIT"; +- case ATH11K_MHI_POWER_ON: +- return "POWER_ON"; +- case ATH11K_MHI_POWER_OFF: +- return "POWER_OFF"; +- case ATH11K_MHI_FORCE_POWER_OFF: +- return "FORCE_POWER_OFF"; +- case ATH11K_MHI_SUSPEND: +- return "SUSPEND"; +- case ATH11K_MHI_RESUME: +- return "RESUME"; +- case ATH11K_MHI_TRIGGER_RDDM: +- return "TRIGGER_RDDM"; +- case ATH11K_MHI_RDDM_DONE: +- return "RDDM_DONE"; +- default: +- return "UNKNOWN"; +- } +-}; +- +-static void ath11k_mhi_set_state_bit(struct ath11k_pci *ab_pci, +- enum ath11k_mhi_state mhi_state) +-{ +- struct ath11k_base *ab = ab_pci->ab; +- +- switch (mhi_state) { +- case ATH11K_MHI_INIT: +- set_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state); +- break; +- case ATH11K_MHI_DEINIT: +- clear_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state); +- break; +- case ATH11K_MHI_POWER_ON: +- set_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state); +- break; +- case ATH11K_MHI_POWER_OFF: +- case ATH11K_MHI_FORCE_POWER_OFF: +- clear_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state); +- clear_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state); +- clear_bit(ATH11K_MHI_RDDM_DONE, &ab_pci->mhi_state); +- break; +- case ATH11K_MHI_SUSPEND: +- set_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state); +- break; +- case ATH11K_MHI_RESUME: +- clear_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state); +- break; +- case ATH11K_MHI_TRIGGER_RDDM: +- set_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state); +- break; +- case ATH11K_MHI_RDDM_DONE: +- set_bit(ATH11K_MHI_RDDM_DONE, &ab_pci->mhi_state); +- break; +- default: +- ath11k_err(ab, "unhandled mhi state (%d)\n", mhi_state); +- } +-} +- +-static int ath11k_mhi_check_state_bit(struct ath11k_pci *ab_pci, +- enum ath11k_mhi_state mhi_state) +-{ +- struct ath11k_base *ab = ab_pci->ab; +- +- switch (mhi_state) { +- case ATH11K_MHI_INIT: +- if (!test_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state)) +- return 0; +- break; +- case ATH11K_MHI_DEINIT: +- case ATH11K_MHI_POWER_ON: +- if (test_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state) && +- !test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state)) +- return 0; +- break; +- case ATH11K_MHI_FORCE_POWER_OFF: +- if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state)) +- return 0; +- break; +- case ATH11K_MHI_POWER_OFF: +- case ATH11K_MHI_SUSPEND: +- if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state) && +- !test_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state)) +- return 0; +- break; +- case ATH11K_MHI_RESUME: +- if (test_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state)) +- return 0; +- break; +- case ATH11K_MHI_TRIGGER_RDDM: +- if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state) && +- !test_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state)) +- return 0; +- break; +- case ATH11K_MHI_RDDM_DONE: +- return 0; +- default: +- ath11k_err(ab, "unhandled mhi state: %s(%d)\n", +- ath11k_mhi_state_to_str(mhi_state), mhi_state); +- } +- +- ath11k_err(ab, "failed to set mhi state %s(%d) in current mhi state (0x%lx)\n", +- ath11k_mhi_state_to_str(mhi_state), mhi_state, +- ab_pci->mhi_state); +- +- return -EINVAL; +-} +- +-static int ath11k_mhi_set_state(struct ath11k_pci *ab_pci, +- enum ath11k_mhi_state mhi_state) +-{ +- struct ath11k_base *ab = ab_pci->ab; +- int ret; +- +- ret = ath11k_mhi_check_state_bit(ab_pci, mhi_state); +- if (ret) +- goto out; +- +- ath11k_dbg(ab, ATH11K_DBG_PCI, "setting mhi state: %s(%d)\n", +- ath11k_mhi_state_to_str(mhi_state), mhi_state); +- +- switch (mhi_state) { +- case ATH11K_MHI_INIT: +- ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl); +- break; +- case ATH11K_MHI_DEINIT: +- mhi_unprepare_after_power_down(ab_pci->mhi_ctrl); +- ret = 0; +- break; +- case ATH11K_MHI_POWER_ON: +- ret = mhi_sync_power_up(ab_pci->mhi_ctrl); +- break; +- case ATH11K_MHI_POWER_OFF: +- mhi_power_down(ab_pci->mhi_ctrl, true); +- ret = 0; +- break; +- case ATH11K_MHI_FORCE_POWER_OFF: +- mhi_power_down(ab_pci->mhi_ctrl, false); +- ret = 0; +- break; +- case ATH11K_MHI_SUSPEND: +- ret = mhi_pm_suspend(ab_pci->mhi_ctrl); +- break; +- case ATH11K_MHI_RESUME: +- /* Do force MHI resume as some devices like QCA6390, WCN6855 +- * are not in M3 state but they are functional. So just ignore +- * the MHI state while resuming. +- */ +- ret = mhi_pm_resume_force(ab_pci->mhi_ctrl); +- break; +- case ATH11K_MHI_TRIGGER_RDDM: +- ret = mhi_force_rddm_mode(ab_pci->mhi_ctrl); +- break; +- case ATH11K_MHI_RDDM_DONE: +- break; +- default: +- ath11k_err(ab, "unhandled MHI state (%d)\n", mhi_state); +- ret = -EINVAL; +- } +- +- if (ret) +- goto out; +- +- ath11k_mhi_set_state_bit(ab_pci, mhi_state); +- +- return 0; +- +-out: +- ath11k_err(ab, "failed to set mhi state: %s(%d)\n", +- ath11k_mhi_state_to_str(mhi_state), mhi_state); +- return ret; +-} +- + int ath11k_mhi_start(struct ath11k_pci *ab_pci) + { + int ret; + + ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS; + +- ret = ath11k_mhi_set_state(ab_pci, ATH11K_MHI_INIT); ++ ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl); + if (ret) + goto out; + +- ret = ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_ON); ++ ret = mhi_sync_power_up(ab_pci->mhi_ctrl); + if (ret) + goto out; + +@@ -665,16 +487,20 @@ out: + + void ath11k_mhi_stop(struct ath11k_pci *ab_pci) + { +- ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_OFF); +- ath11k_mhi_set_state(ab_pci, ATH11K_MHI_DEINIT); ++ mhi_power_down(ab_pci->mhi_ctrl, true); ++ mhi_unprepare_after_power_down(ab_pci->mhi_ctrl); + } + + void ath11k_mhi_suspend(struct ath11k_pci *ab_pci) + { +- ath11k_mhi_set_state(ab_pci, ATH11K_MHI_SUSPEND); ++ mhi_pm_suspend(ab_pci->mhi_ctrl); + } + + void ath11k_mhi_resume(struct ath11k_pci *ab_pci) + { +- ath11k_mhi_set_state(ab_pci, ATH11K_MHI_RESUME); ++ /* Do force MHI resume as some devices like QCA6390, WCN6855 ++ * are not in M3 state but they are functional. So just ignore ++ * the MHI state while resuming. ++ */ ++ mhi_pm_resume_force(ab_pci->mhi_ctrl); + } +--- a/drivers/net/wireless/ath/ath11k/mhi.h ++++ b/drivers/net/wireless/ath/ath11k/mhi.h +@@ -16,19 +16,6 @@ + #define MHICTRL 0x38 + #define MHICTRL_RESET_MASK 0x2 + +-enum ath11k_mhi_state { +- ATH11K_MHI_INIT, +- ATH11K_MHI_DEINIT, +- ATH11K_MHI_POWER_ON, +- ATH11K_MHI_POWER_OFF, +- ATH11K_MHI_FORCE_POWER_OFF, +- ATH11K_MHI_SUSPEND, +- ATH11K_MHI_RESUME, +- ATH11K_MHI_TRIGGER_RDDM, +- ATH11K_MHI_RDDM, +- ATH11K_MHI_RDDM_DONE, +-}; +- + int ath11k_mhi_start(struct ath11k_pci *ar_pci); + void ath11k_mhi_stop(struct ath11k_pci *ar_pci); + int ath11k_mhi_register(struct ath11k_pci *ar_pci); +--- a/drivers/net/wireless/ath/ath11k/pci.h ++++ b/drivers/net/wireless/ath/ath11k/pci.h +@@ -63,7 +63,7 @@ struct ath11k_pci { + u16 dev_id; + char amss_path[100]; + struct mhi_controller *mhi_ctrl; +- unsigned long mhi_state; ++ const struct ath11k_msi_config *msi_config; + u32 register_window; + + /* protects register_window above */ diff --git a/package/kernel/mac80211/patches/ath11k/0224-ath11k-mhi-add-error-handling-for-suspend-and-resume.patch b/package/kernel/mac80211/patches/ath11k/0224-ath11k-mhi-add-error-handling-for-suspend-and-resume.patch new file mode 100644 index 00000000000000..ae60753ba19eb5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0224-ath11k-mhi-add-error-handling-for-suspend-and-resume.patch @@ -0,0 +1,95 @@ +From 3e80fcbca37221cd1e5a33eea4b0f215f66a7a00 Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Tue, 5 Apr 2022 11:26:39 +0300 +Subject: [PATCH] ath11k: mhi: add error handling for suspend and resume + +While reviewing the mhi.c I noticed we were just ignoring the errors coming +from MHI subsystem during suspend and resume. Add proper checks and warning +messages. Also pass the error value to callers. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220401173042.17467-3-kvalo@kernel.org +--- + drivers/net/wireless/ath/ath11k/mhi.c | 26 ++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/mhi.h | 4 ++-- + drivers/net/wireless/ath/ath11k/pci.c | 8 ++------ + 3 files changed, 26 insertions(+), 12 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -491,16 +491,34 @@ void ath11k_mhi_stop(struct ath11k_pci * + mhi_unprepare_after_power_down(ab_pci->mhi_ctrl); + } + +-void ath11k_mhi_suspend(struct ath11k_pci *ab_pci) ++int ath11k_mhi_suspend(struct ath11k_pci *ab_pci) + { +- mhi_pm_suspend(ab_pci->mhi_ctrl); ++ struct ath11k_base *ab = ab_pci->ab; ++ int ret; ++ ++ ret = mhi_pm_suspend(ab_pci->mhi_ctrl); ++ if (ret) { ++ ath11k_warn(ab, "failed to suspend mhi: %d", ret); ++ return ret; ++ } ++ ++ return 0; + } + +-void ath11k_mhi_resume(struct ath11k_pci *ab_pci) ++int ath11k_mhi_resume(struct ath11k_pci *ab_pci) + { ++ struct ath11k_base *ab = ab_pci->ab; ++ int ret; ++ + /* Do force MHI resume as some devices like QCA6390, WCN6855 + * are not in M3 state but they are functional. So just ignore + * the MHI state while resuming. + */ +- mhi_pm_resume_force(ab_pci->mhi_ctrl); ++ ret = mhi_pm_resume_force(ab_pci->mhi_ctrl); ++ if (ret) { ++ ath11k_warn(ab, "failed to resume mhi: %d", ret); ++ return ret; ++ } ++ ++ return 0; + } +--- a/drivers/net/wireless/ath/ath11k/mhi.h ++++ b/drivers/net/wireless/ath/ath11k/mhi.h +@@ -23,7 +23,7 @@ void ath11k_mhi_unregister(struct ath11k + void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab); + void ath11k_mhi_clear_vector(struct ath11k_base *ab); + +-void ath11k_mhi_suspend(struct ath11k_pci *ar_pci); +-void ath11k_mhi_resume(struct ath11k_pci *ar_pci); ++int ath11k_mhi_suspend(struct ath11k_pci *ar_pci); ++int ath11k_mhi_resume(struct ath11k_pci *ar_pci); + + #endif +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -619,18 +619,14 @@ static int ath11k_pci_hif_suspend(struct + { + struct ath11k_pci *ar_pci = ath11k_pci_priv(ab); + +- ath11k_mhi_suspend(ar_pci); +- +- return 0; ++ return ath11k_mhi_suspend(ar_pci); + } + + static int ath11k_pci_hif_resume(struct ath11k_base *ab) + { + struct ath11k_pci *ar_pci = ath11k_pci_priv(ab); + +- ath11k_mhi_resume(ar_pci); +- +- return 0; ++ return ath11k_mhi_resume(ar_pci); + } + + static void ath11k_pci_hif_ce_irq_enable(struct ath11k_base *ab) diff --git a/package/kernel/mac80211/patches/ath11k/0225-ath11k-mhi-remove-unnecessary-goto-from-ath11k_mhi_s.patch b/package/kernel/mac80211/patches/ath11k/0225-ath11k-mhi-remove-unnecessary-goto-from-ath11k_mhi_s.patch new file mode 100644 index 00000000000000..362a0fc5302276 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0225-ath11k-mhi-remove-unnecessary-goto-from-ath11k_mhi_s.patch @@ -0,0 +1,52 @@ +From b9e34ba6b314780a47ac40f450ec04f18be85b5e Mon Sep 17 00:00:00 2001 +From: Kalle Valo +Date: Tue, 5 Apr 2022 11:26:44 +0300 +Subject: [PATCH] ath11k: mhi: remove unnecessary goto from ath11k_mhi_start() + +No need to have goto for a return statement, so simplify the code. While at it, +print warning messages if power up calls fail. + +No functional changes. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2 + +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220401173042.17467-4-kvalo@kernel.org +--- + drivers/net/wireless/ath/ath11k/mhi.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mhi.c ++++ b/drivers/net/wireless/ath/ath11k/mhi.c +@@ -467,22 +467,24 @@ void ath11k_mhi_unregister(struct ath11k + + int ath11k_mhi_start(struct ath11k_pci *ab_pci) + { ++ struct ath11k_base *ab = ab_pci->ab; + int ret; + + ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS; + + ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl); +- if (ret) +- goto out; ++ if (ret) { ++ ath11k_warn(ab, "failed to prepare mhi: %d", ret); ++ return ret; ++ } + + ret = mhi_sync_power_up(ab_pci->mhi_ctrl); +- if (ret) +- goto out; ++ if (ret) { ++ ath11k_warn(ab, "failed to power up mhi: %d", ret); ++ return ret; ++ } + + return 0; +- +-out: +- return ret; + } + + void ath11k_mhi_stop(struct ath11k_pci *ab_pci) diff --git a/package/kernel/mac80211/patches/ath11k/0226-ath11k-Fix-spelling-mistake-reseting-resetting.patch b/package/kernel/mac80211/patches/ath11k/0226-ath11k-Fix-spelling-mistake-reseting-resetting.patch new file mode 100644 index 00000000000000..057cc9e684c825 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0226-ath11k-Fix-spelling-mistake-reseting-resetting.patch @@ -0,0 +1,25 @@ +From 405342ebea2ab776df070ee54c308f1f59723844 Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Thu, 7 Apr 2022 11:28:20 +0100 +Subject: [PATCH] ath11k: Fix spelling mistake "reseting" -> "resetting" + +There is a spelling mistake in an ath11k_warn message. Fix it. + +Signed-off-by: Colin Ian King +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220407102820.613881-1-colin.i.king@gmail.com +--- + drivers/net/wireless/ath/ath11k/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1567,7 +1567,7 @@ static void ath11k_core_reset(struct wor + * completed, then the second reset worker will destroy the previous one, + * thus below is to avoid that. + */ +- ath11k_warn(ab, "already reseting count %d\n", reset_count); ++ ath11k_warn(ab, "already resetting count %d\n", reset_count); + + reinit_completion(&ab->reset_complete); + time_left = wait_for_completion_timeout(&ab->reset_complete, diff --git a/package/kernel/mac80211/patches/ath11k/0227-ath11k-add-support-for-extended-wmi-service-bit.patch b/package/kernel/mac80211/patches/ath11k/0227-ath11k-add-support-for-extended-wmi-service-bit.patch new file mode 100644 index 00000000000000..6043a2ce29acb9 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0227-ath11k-add-support-for-extended-wmi-service-bit.patch @@ -0,0 +1,165 @@ +From e2e23a791745a194aa8e6fb4983c2e7b408ab6e1 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Tue, 12 Apr 2022 16:15:50 +0300 +Subject: [PATCH] ath11k: add support for extended wmi service bit + +When the WMI service bits are reported from firmware they are divided into +multiple segments, with 128 bits in each segment. The first segment is +processed by ath11k_wmi_service_bitmap_copy(), the second segment is processed +by ath11k_service_available_event() with WMI_TAG_SERVICE_AVAILABLE_EVENT. When +the service bit exceed 256 bits, then firmware reports it by tag +WMI_TAG_ARRAY_UINT32 in WMI_SERVICE_AVAILABLE_EVENTID. + +Currently ath11k does not process the third segment. Upcoming features +need to know if firmware support is available for the features, so add +processing of the third segment. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Wen Gong +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220401120948.1312956-2-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 81 ++++++++++++++++----------- + drivers/net/wireless/ath/ath11k/wmi.h | 9 ++- + 2 files changed, 56 insertions(+), 34 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 3af24b18204e..a5876e0378fe 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7297,47 +7297,64 @@ static void ath11k_vdev_install_key_compl_event(struct ath11k_base *ab, + rcu_read_unlock(); + } + +-static void ath11k_service_available_event(struct ath11k_base *ab, struct sk_buff *skb) ++static int ath11k_wmi_tlv_services_parser(struct ath11k_base *ab, ++ u16 tag, u16 len, ++ const void *ptr, void *data) + { +- const void **tb; + const struct wmi_service_available_event *ev; +- int ret; ++ u32 *wmi_ext2_service_bitmap; + int i, j; + +- tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); +- if (IS_ERR(tb)) { +- ret = PTR_ERR(tb); +- ath11k_warn(ab, "failed to parse tlv: %d\n", ret); +- return; +- } ++ switch (tag) { ++ case WMI_TAG_SERVICE_AVAILABLE_EVENT: ++ ev = (struct wmi_service_available_event *)ptr; ++ for (i = 0, j = WMI_MAX_SERVICE; ++ i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT_SERVICE; ++ i++) { ++ do { ++ if (ev->wmi_service_segment_bitmap[i] & ++ BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) ++ set_bit(j, ab->wmi_ab.svc_map); ++ } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); ++ } + +- ev = tb[WMI_TAG_SERVICE_AVAILABLE_EVENT]; +- if (!ev) { +- ath11k_warn(ab, "failed to fetch svc available ev"); +- kfree(tb); +- return; +- } ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wmi_ext_service_bitmap 0:0x%04x, 1:0x%04x, 2:0x%04x, 3:0x%04x", ++ ev->wmi_service_segment_bitmap[0], ++ ev->wmi_service_segment_bitmap[1], ++ ev->wmi_service_segment_bitmap[2], ++ ev->wmi_service_segment_bitmap[3]); ++ break; ++ case WMI_TAG_ARRAY_UINT32: ++ wmi_ext2_service_bitmap = (u32 *)ptr; ++ for (i = 0, j = WMI_MAX_EXT_SERVICE; ++ i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT2_SERVICE; ++ i++) { ++ do { ++ if (wmi_ext2_service_bitmap[i] & ++ BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) ++ set_bit(j, ab->wmi_ab.svc_map); ++ } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); ++ } + +- /* TODO: Use wmi_service_segment_offset information to get the service +- * especially when more services are advertised in multiple sevice +- * available events. +- */ +- for (i = 0, j = WMI_MAX_SERVICE; +- i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT_SERVICE; +- i++) { +- do { +- if (ev->wmi_service_segment_bitmap[i] & +- BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) +- set_bit(j, ab->wmi_ab.svc_map); +- } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wmi_ext2_service__bitmap 0:0x%04x, 1:0x%04x, 2:0x%04x, 3:0x%04x", ++ wmi_ext2_service_bitmap[0], wmi_ext2_service_bitmap[1], ++ wmi_ext2_service_bitmap[2], wmi_ext2_service_bitmap[3]); ++ break; + } ++ return 0; ++} + +- ath11k_dbg(ab, ATH11K_DBG_WMI, +- "wmi_ext_service_bitmap 0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x", +- ev->wmi_service_segment_bitmap[0], ev->wmi_service_segment_bitmap[1], +- ev->wmi_service_segment_bitmap[2], ev->wmi_service_segment_bitmap[3]); ++static void ath11k_service_available_event(struct ath11k_base *ab, struct sk_buff *skb) ++{ ++ int ret; + +- kfree(tb); ++ ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, ++ ath11k_wmi_tlv_services_parser, ++ NULL); ++ if (ret) ++ ath11k_warn(ab, "failed to parse services available tlv %d\n", ret); + } + + static void ath11k_peer_assoc_conf_event(struct ath11k_base *ab, struct sk_buff *skb) +diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h +index c9ffefea549c..4ff7e71409ce 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -1992,6 +1992,7 @@ enum wmi_tlv_service { + WMI_TLV_SERVICE_ACK_TIMEOUT = 126, + WMI_TLV_SERVICE_PDEV_BSS_CHANNEL_INFO_64 = 127, + ++ /* The first 128 bits */ + WMI_MAX_SERVICE = 128, + + WMI_TLV_SERVICE_CHAN_LOAD_INFO = 128, +@@ -2084,7 +2085,11 @@ enum wmi_tlv_service { + WMI_TLV_SERVICE_EXT2_MSG = 220, + WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249, + +- WMI_MAX_EXT_SERVICE ++ /* The second 128 bits */ ++ WMI_MAX_EXT_SERVICE = 256, ++ ++ /* The third 128 bits */ ++ WMI_MAX_EXT2_SERVICE = 384 + }; + + enum { +@@ -5370,7 +5375,7 @@ struct ath11k_wmi_base { + + struct completion service_ready; + struct completion unified_ready; +- DECLARE_BITMAP(svc_map, WMI_MAX_EXT_SERVICE); ++ DECLARE_BITMAP(svc_map, WMI_MAX_EXT2_SERVICE); + wait_queue_head_t tx_credits_wq; + const struct wmi_peer_flags_map *peer_flags; + u32 num_mem_chunks; +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0228-ath11k-Add-support-for-SAR.patch b/package/kernel/mac80211/patches/ath11k/0228-ath11k-Add-support-for-SAR.patch new file mode 100644 index 00000000000000..9bdcc60f4e202a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0228-ath11k-Add-support-for-SAR.patch @@ -0,0 +1,362 @@ +From 652f69ed9c1b4f8ef4ea9d6738f1e6263a5ff26d Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Tue, 12 Apr 2022 16:15:50 +0300 +Subject: [PATCH] ath11k: Add support for SAR + +Add ath11k_mac_op_set_bios_sar_specs() to ath11k_ops, this function is called +when user space application calls NL80211_CMD_SET_SAR_SPECS. ath11k also +registers SAR type and frequency ranges to wiphy so user space can query SAR +capabilities. + +This feature is currently enabled for WCN6855. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-02431-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220401120948.1312956-3-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 6 +++ + drivers/net/wireless/ath/ath11k/hw.c | 20 ++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 2 + + drivers/net/wireless/ath/ath11k/mac.c | 67 ++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 70 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 27 ++++++++++ + 6 files changed, 192 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 1537ec0ae2e7..f011db4bcd2b 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -101,6 +101,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .current_cc_support = false, + .dbr_debug_support = true, + .global_reset = false, ++ .bios_sar_capa = NULL, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -167,6 +168,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .current_cc_support = false, + .dbr_debug_support = true, + .global_reset = false, ++ .bios_sar_capa = NULL, + }, + { + .name = "qca6390 hw2.0", +@@ -232,6 +234,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, ++ .bios_sar_capa = NULL, + }, + { + .name = "qcn9074 hw1.0", +@@ -297,6 +300,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .current_cc_support = false, + .dbr_debug_support = true, + .global_reset = false, ++ .bios_sar_capa = NULL, + }, + { + .name = "wcn6855 hw2.0", +@@ -362,6 +366,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, ++ .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855, + }, + { + .name = "wcn6855 hw2.1", +@@ -426,6 +431,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, ++ .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855, + }, + }; + +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index d1b0e76d9ec2..46449a18b5cb 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2154,3 +2154,23 @@ const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = { + const struct ath11k_hw_hal_params ath11k_hw_hal_params_qca6390 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW1_BM, + }; ++ ++static const struct cfg80211_sar_freq_ranges ath11k_hw_sar_freq_ranges_wcn6855[] = { ++ {.start_freq = 2402, .end_freq = 2482 }, /* 2G ch1~ch13 */ ++ {.start_freq = 5150, .end_freq = 5250 }, /* 5G UNII-1 ch32~ch48 */ ++ {.start_freq = 5250, .end_freq = 5725 }, /* 5G UNII-2 ch50~ch144 */ ++ {.start_freq = 5725, .end_freq = 5810 }, /* 5G UNII-3 ch149~ch161 */ ++ {.start_freq = 5815, .end_freq = 5895 }, /* 5G UNII-4 ch163~ch177 */ ++ {.start_freq = 5925, .end_freq = 6165 }, /* 6G UNII-5 Ch1, Ch2 ~ Ch41 */ ++ {.start_freq = 6165, .end_freq = 6425 }, /* 6G UNII-5 ch45~ch93 */ ++ {.start_freq = 6425, .end_freq = 6525 }, /* 6G UNII-6 ch97~ch113 */ ++ {.start_freq = 6525, .end_freq = 6705 }, /* 6G UNII-7 ch117~ch149 */ ++ {.start_freq = 6705, .end_freq = 6875 }, /* 6G UNII-7 ch153~ch185 */ ++ {.start_freq = 6875, .end_freq = 7125 }, /* 6G UNII-8 ch189~ch233 */ ++}; ++ ++const struct cfg80211_sar_capa ath11k_hw_sar_capa_wcn6855 = { ++ .type = NL80211_SAR_TYPE_POWER, ++ .num_freq_ranges = (ARRAY_SIZE(ath11k_hw_sar_freq_ranges_wcn6855)), ++ .freq_ranges = ath11k_hw_sar_freq_ranges_wcn6855, ++}; +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 08f958c03ec4..b7ece3d5678c 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -195,6 +195,7 @@ struct ath11k_hw_params { + bool current_cc_support; + bool dbr_debug_support; + bool global_reset; ++ const struct cfg80211_sar_capa *bios_sar_capa; + }; + + struct ath11k_hw_ops { +@@ -380,4 +381,5 @@ static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type) + return "unknown"; + } + ++extern const struct cfg80211_sar_capa ath11k_hw_sar_capa_wcn6855; + #endif +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 175a4ae752f3..12fd30742664 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8273,6 +8273,68 @@ static void ath11k_mac_op_set_rekey_data(struct ieee80211_hw *hw, + mutex_unlock(&ar->conf_mutex); + } + ++static int ath11k_mac_op_set_bios_sar_specs(struct ieee80211_hw *hw, ++ const struct cfg80211_sar_specs *sar) ++{ ++ struct ath11k *ar = hw->priv; ++ const struct cfg80211_sar_sub_specs *sspec = sar->sub_specs; ++ int ret, index; ++ u8 *sar_tbl; ++ u32 i; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ if (!test_bit(WMI_TLV_SERVICE_BIOS_SAR_SUPPORT, ar->ab->wmi_ab.svc_map) || ++ !ar->ab->hw_params.bios_sar_capa) { ++ ret = -EOPNOTSUPP; ++ goto exit; ++ } ++ ++ if (!sar || sar->type != NL80211_SAR_TYPE_POWER || ++ sar->num_sub_specs == 0) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = ath11k_wmi_pdev_set_bios_geo_table_param(ar); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set geo table: %d\n", ret); ++ goto exit; ++ } ++ ++ sar_tbl = kzalloc(BIOS_SAR_TABLE_LEN, GFP_KERNEL); ++ if (!sar_tbl) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ for (i = 0; i < sar->num_sub_specs; i++) { ++ if (sspec->freq_range_index >= (BIOS_SAR_TABLE_LEN >> 1)) { ++ ath11k_warn(ar->ab, "Ignore bad frequency index %u, max allowed %u\n", ++ sspec->freq_range_index, BIOS_SAR_TABLE_LEN >> 1); ++ continue; ++ } ++ ++ /* chain0 and chain1 share same power setting */ ++ sar_tbl[sspec->freq_range_index] = sspec->power; ++ index = sspec->freq_range_index + (BIOS_SAR_TABLE_LEN >> 1); ++ sar_tbl[index] = sspec->power; ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "sar tbl[%d] = %d\n", ++ sspec->freq_range_index, sar_tbl[sspec->freq_range_index]); ++ sspec++; ++ } ++ ++ ret = ath11k_wmi_pdev_set_bios_sar_table_param(ar, sar_tbl); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to set sar power: %d", ret); ++ ++ kfree(sar_tbl); ++exit: ++ mutex_unlock(&ar->conf_mutex); ++ ++ return ret; ++} ++ + static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .start = ath11k_mac_op_start, +@@ -8324,6 +8386,7 @@ static const struct ieee80211_ops ath11k_ops = { + .ipv6_addr_change = ath11k_mac_op_ipv6_changed, + #endif + ++ .set_sar_specs = ath11k_mac_op_set_bios_sar_specs, + }; + + static void ath11k_mac_update_ch_list(struct ath11k *ar, +@@ -8746,6 +8809,10 @@ static int __ath11k_mac_register(struct ath11k *ar) + ieee80211_hw_set(ar->hw, SUPPORT_FAST_XMIT); + } + ++ if (test_bit(WMI_TLV_SERVICE_BIOS_SAR_SUPPORT, ar->ab->wmi_ab.svc_map) && ++ ab->hw_params.bios_sar_capa) ++ ar->hw->wiphy->sar_capa = ab->hw_params.bios_sar_capa; ++ + ret = ieee80211_register_hw(ar->hw); + if (ret) { + ath11k_err(ar->ab, "ieee80211 registration failed: %d\n", ret); +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index a5876e0378fe..dcf31bdad47e 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -8883,3 +8883,73 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar, + arvif->vdev_id); + return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + } ++ ++int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val) ++{ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_pdev_set_sar_table_cmd *cmd; ++ struct wmi_tlv *tlv; ++ struct sk_buff *skb; ++ u8 *buf_ptr; ++ u32 len, sar_len_aligned, rsvd_len_aligned; ++ ++ sar_len_aligned = roundup(BIOS_SAR_TABLE_LEN, sizeof(u32)); ++ rsvd_len_aligned = roundup(BIOS_SAR_RSVD1_LEN, sizeof(u32)); ++ len = sizeof(*cmd) + ++ TLV_HDR_SIZE + sar_len_aligned + ++ TLV_HDR_SIZE + rsvd_len_aligned; ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_pdev_set_sar_table_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ cmd->pdev_id = ar->pdev->pdev_id; ++ cmd->sar_len = BIOS_SAR_TABLE_LEN; ++ cmd->rsvd_len = BIOS_SAR_RSVD1_LEN; ++ ++ buf_ptr = skb->data + sizeof(*cmd); ++ tlv = (struct wmi_tlv *)buf_ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | ++ FIELD_PREP(WMI_TLV_LEN, sar_len_aligned); ++ buf_ptr += TLV_HDR_SIZE; ++ memcpy(buf_ptr, sar_val, BIOS_SAR_TABLE_LEN); ++ ++ buf_ptr += sar_len_aligned; ++ tlv = (struct wmi_tlv *)buf_ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | ++ FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned); ++ ++ return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID); ++} ++ ++int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_pdev_set_geo_table_cmd *cmd; ++ struct wmi_tlv *tlv; ++ struct sk_buff *skb; ++ u8 *buf_ptr; ++ u32 len, rsvd_len_aligned; ++ ++ rsvd_len_aligned = roundup(BIOS_SAR_RSVD2_LEN, sizeof(u32)); ++ len = sizeof(*cmd) + TLV_HDR_SIZE + rsvd_len_aligned; ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_pdev_set_geo_table_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ cmd->pdev_id = ar->pdev->pdev_id; ++ cmd->rsvd_len = BIOS_SAR_RSVD2_LEN; ++ ++ buf_ptr = skb->data + sizeof(*cmd); ++ tlv = (struct wmi_tlv *)buf_ptr; ++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | ++ FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned); ++ ++ return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID); ++} +diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h +index 4ff7e71409ce..7600e9a52da8 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -285,6 +285,11 @@ enum wmi_tlv_cmd_id { + WMI_PDEV_SET_SRG_OBSS_BSSID_ENABLE_BITMAP_CMDID, + WMI_PDEV_SET_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMDID, + WMI_PDEV_SET_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMDID, ++ WMI_PDEV_GET_TPC_STATS_CMDID, ++ WMI_PDEV_ENABLE_DURATION_BASED_TX_MODE_SELECTION_CMDID, ++ WMI_PDEV_GET_DPD_STATUS_CMDID, ++ WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID, ++ WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID, + WMI_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_VDEV), + WMI_VDEV_DELETE_CMDID, + WMI_VDEV_START_REQUEST_CMDID, +@@ -1859,6 +1864,8 @@ enum wmi_tlv_tag { + WMI_TAG_PDEV_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, + WMI_TAG_PDEV_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMD, + WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, ++ WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, ++ WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD, + WMI_TAG_MAX + }; + +@@ -2087,6 +2094,7 @@ enum wmi_tlv_service { + + /* The second 128 bits */ + WMI_MAX_EXT_SERVICE = 256, ++ WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326, + + /* The third 128 bits */ + WMI_MAX_EXT2_SERVICE = 384 +@@ -5882,6 +5890,23 @@ struct wmi_gtk_rekey_offload_cmd { + u8 replay_ctr[GTK_REPLAY_COUNTER_BYTES]; + } __packed; + ++#define BIOS_SAR_TABLE_LEN (22) ++#define BIOS_SAR_RSVD1_LEN (6) ++#define BIOS_SAR_RSVD2_LEN (18) ++ ++struct wmi_pdev_set_sar_table_cmd { ++ u32 tlv_header; ++ u32 pdev_id; ++ u32 sar_len; ++ u32 rsvd_len; ++} __packed; ++ ++struct wmi_pdev_set_geo_table_cmd { ++ u32 tlv_header; ++ u32 pdev_id; ++ u32 rsvd_len; ++} __packed; ++ + int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); + struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); +@@ -6060,5 +6085,7 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar, + struct ath11k_vif *arvif, bool enable); + int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar, + struct ath11k_vif *arvif); ++int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val); ++int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar); + + #endif +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0229-ath11k-fix-missing-unlock-on-error-in-ath11k_wow_op_.patch b/package/kernel/mac80211/patches/ath11k/0229-ath11k-fix-missing-unlock-on-error-in-ath11k_wow_op_.patch new file mode 100644 index 00000000000000..230052c7c44ad3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0229-ath11k-fix-missing-unlock-on-error-in-ath11k_wow_op_.patch @@ -0,0 +1,33 @@ +From 605194411d7379645ed44e447c4b804a0b476109 Mon Sep 17 00:00:00 2001 +From: Yang Yingliang +Date: Tue, 12 Apr 2022 16:15:53 +0300 +Subject: [PATCH] ath11k: fix missing unlock on error in ath11k_wow_op_resume() + +Add the missing unlock before return from function ath11k_wow_op_resume() +in the error handling case. + +Fixes: 90bf5c8d0f7e ("ath11k: purge rx pktlog when entering WoW") +Reported-by: Hulk Robot +Signed-off-by: Yang Yingliang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220408030912.3087293-1-yangyingliang@huawei.com +--- + drivers/net/wireless/ath/ath11k/wow.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c +index 6c2611f93739..9d088cebef03 100644 +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -758,7 +758,7 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw) + ret = ath11k_dp_rx_pktlog_start(ar->ab); + if (ret) { + ath11k_warn(ar->ab, "failed to start rx pktlog from wow: %d\n", ret); +- return ret; ++ goto exit; + } + + ret = ath11k_wow_wakeup(ar->ab); +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0230-ath11k-Fix-build-warning-without-CONFIG_IPV6.patch b/package/kernel/mac80211/patches/ath11k/0230-ath11k-Fix-build-warning-without-CONFIG_IPV6.patch new file mode 100644 index 00000000000000..91482ac731c9f2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0230-ath11k-Fix-build-warning-without-CONFIG_IPV6.patch @@ -0,0 +1,45 @@ +From 67888630addeaa2ae1c04d8d020daccbd26d05d9 Mon Sep 17 00:00:00 2001 +From: YueHaibing +Date: Mon, 11 Apr 2022 10:08:43 +0800 +Subject: [PATCH] ath11k: Fix build warning without CONFIG_IPV6 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +drivers/net/wireless/ath/ath11k/mac.c:8175:13: error: ‘ath11k_mac_op_ipv6_changed’ defined but not used [-Werror=unused-function] + static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, + ^~~~~~~~~~~~~~~~~~~~~~~~~~ + +Wrap it with #ifdef block to fix this. + +Fixes: c3c36bfe998b ("ath11k: support ARP and NS offload") +Signed-off-by: YueHaibing +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220411020843.10284-1-yuehaibing@huawei.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 12fd30742664..c987f365c63a 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8145,6 +8145,7 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, + } + } + ++#if IS_ENABLED(CONFIG_IPV6) + static void ath11k_generate_ns_mc_addr(struct ath11k *ar, + struct ath11k_arp_ns_offload *offload) + { +@@ -8239,6 +8240,7 @@ static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, + /* generate ns multicast address */ + ath11k_generate_ns_mc_addr(ar, offload); + } ++#endif + + static void ath11k_mac_op_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0231-ath11k-Do-not-put-HW-in-DBS-mode-for-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0231-ath11k-Do-not-put-HW-in-DBS-mode-for-WCN6750.patch new file mode 100644 index 00000000000000..2c91198e162a12 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0231-ath11k-Do-not-put-HW-in-DBS-mode-for-WCN6750.patch @@ -0,0 +1,58 @@ +From b6f6301041a3f27cb3085abb395c2607ac71671c Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Wed, 6 Apr 2022 15:11:03 +0530 +Subject: [PATCH] ath11k: Do not put HW in DBS mode for WCN6750 + +Though WCN6750 is a single PDEV device, it is not a +DBS solution. So, do not put HW in DBS mode for WCN6750. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00573-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220406094107.17878-10-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 2 +- + drivers/net/wireless/ath/ath11k/wmi.c | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index f011db4bcd2b..06958f358307 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1213,7 +1213,7 @@ static int ath11k_core_start(struct ath11k_base *ab, + } + + /* put hardware to DBS mode */ +- if (ab->hw_params.single_pdev_only) { ++ if (ab->hw_params.single_pdev_only && ab->hw_params.num_rxmda_per_pdev > 1) { + ret = ath11k_wmi_set_hw_mode(ab, WMI_HOST_HW_MODE_DBS); + if (ret) { + ath11k_err(ab, "failed to send dbs mode: %d\n", ret); +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index dcf31bdad47e..d7243819d9bd 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved. + */ + #include + #include +@@ -8225,7 +8226,7 @@ int ath11k_wmi_attach(struct ath11k_base *ab) + ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; + + /* It's overwritten when service_ext_ready is handled */ +- if (ab->hw_params.single_pdev_only) ++ if (ab->hw_params.single_pdev_only && ab->hw_params.num_rxmda_per_pdev > 1) + ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_SINGLE; + + /* TODO: Init remaining wmi soc resources required */ +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0232-ath11k-WMI-changes-to-support-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0232-ath11k-WMI-changes-to-support-WCN6750.patch new file mode 100644 index 00000000000000..80750045cf25ff --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0232-ath11k-WMI-changes-to-support-WCN6750.patch @@ -0,0 +1,59 @@ +From 95959d702ede5fffd1abfcfa739b5179828cfe24 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Wed, 6 Apr 2022 15:11:04 +0530 +Subject: [PATCH] ath11k: WMI changes to support WCN6750 + +WCN6750 is a single PDEV non-DBS chip which supports 2G, 5G and 6G bands. +It is a single LMAC device which can be either hooked to 2G/5G/6G bands. +Add WMI changes to support WCN6750. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00573-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220406094107.17878-11-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index d7243819d9bd..3c0ac1e29479 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -391,6 +391,10 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, + ab->target_pdev_ids[ab->target_pdev_count].pdev_id = mac_phy_caps->pdev_id; + ab->target_pdev_count++; + ++ if (!(mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) && ++ !(mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP)) ++ return -EINVAL; ++ + /* Take non-zero tx/rx chainmask. If tx/rx chainmask differs from + * band to band for a single radio, need to see how this should be + * handled. +@@ -398,7 +402,9 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) { + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_2g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_2g; +- } else if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP) { ++ } ++ ++ if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP) { + pdev_cap->vht_cap = mac_phy_caps->vht_cap_info_5g; + pdev_cap->vht_mcs = mac_phy_caps->vht_supp_mcs_5g; + pdev_cap->he_mcs = mac_phy_caps->he_supp_mcs_5g; +@@ -408,8 +414,6 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, + WMI_NSS_RATIO_ENABLE_DISABLE_GET(mac_phy_caps->nss_ratio); + pdev_cap->nss_ratio_info = + WMI_NSS_RATIO_INFO_GET(mac_phy_caps->nss_ratio); +- } else { +- return -EINVAL; + } + + /* tx/rx chainmask reported from fw depends on the actual hw chains used, +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0233-ath11k-Update-WBM-idle-ring-HP-after-FW-mode-on.patch b/package/kernel/mac80211/patches/ath11k/0233-ath11k-Update-WBM-idle-ring-HP-after-FW-mode-on.patch new file mode 100644 index 00000000000000..974f719b5aa667 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0233-ath11k-Update-WBM-idle-ring-HP-after-FW-mode-on.patch @@ -0,0 +1,148 @@ +From 33b67a4b4e64275b6f2cbc4318f1596c70659111 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Wed, 6 Apr 2022 15:11:05 +0530 +Subject: [PATCH] ath11k: Update WBM idle ring HP after FW mode on + +Currently, WBM idle ring HP is updated much before the shadow +configuration is sent to the FW. Any update to the shadow +registers before FW mode on request would not be reflected +on to the actual HW registers failing to bring up the device. +Send FW mode ON QMI request before WBM idle ring HP update +to fix this problem. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00573-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220406094107.17878-12-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ce.c | 4 +-- + drivers/net/wireless/ath/ath11k/core.c | 45 ++++++++++++++++++-------- + 2 files changed, 32 insertions(+), 17 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c +index aaa7b05ff49d..c14c51f38709 100644 +--- a/drivers/net/wireless/ath/ath11k/ce.c ++++ b/drivers/net/wireless/ath/ath11k/ce.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include "dp_rx.h" +@@ -918,9 +919,6 @@ int ath11k_ce_init_pipes(struct ath11k_base *ab) + int i; + int ret; + +- ath11k_ce_get_shadow_config(ab, &ab->qmi.ce_cfg.shadow_reg_v2, +- &ab->qmi.ce_cfg.shadow_reg_v2_len); +- + for (i = 0; i < ab->hw_params.ce_count; i++) { + pipe = &ab->ce.ce_pipe[i]; + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 06958f358307..36d265454e26 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1124,21 +1124,14 @@ static void ath11k_core_pdev_destroy(struct ath11k_base *ab) + ath11k_debugfs_pdev_destroy(ab); + } + +-static int ath11k_core_start(struct ath11k_base *ab, +- enum ath11k_firmware_mode mode) ++static int ath11k_core_start(struct ath11k_base *ab) + { + int ret; + +- ret = ath11k_qmi_firmware_start(ab, mode); +- if (ret) { +- ath11k_err(ab, "failed to attach wmi: %d\n", ret); +- return ret; +- } +- + ret = ath11k_wmi_attach(ab); + if (ret) { + ath11k_err(ab, "failed to attach wmi: %d\n", ret); +- goto err_firmware_stop; ++ return ret; + } + + ret = ath11k_htc_init(ab); +@@ -1238,8 +1231,23 @@ static int ath11k_core_start(struct ath11k_base *ab, + ath11k_hif_stop(ab); + err_wmi_detach: + ath11k_wmi_detach(ab); +-err_firmware_stop: +- ath11k_qmi_firmware_stop(ab); ++ ++ return ret; ++} ++ ++static int ath11k_core_start_firmware(struct ath11k_base *ab, ++ enum ath11k_firmware_mode mode) ++{ ++ int ret; ++ ++ ath11k_ce_get_shadow_config(ab, &ab->qmi.ce_cfg.shadow_reg_v2, ++ &ab->qmi.ce_cfg.shadow_reg_v2_len); ++ ++ ret = ath11k_qmi_firmware_start(ab, mode); ++ if (ret) { ++ ath11k_err(ab, "failed to send firmware start: %d\n", ret); ++ return ret; ++ } + + return ret; + } +@@ -1269,16 +1277,22 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) + { + int ret; + ++ ret = ath11k_core_start_firmware(ab, ATH11K_FIRMWARE_MODE_NORMAL); ++ if (ret) { ++ ath11k_err(ab, "failed to start firmware: %d\n", ret); ++ return ret; ++ } ++ + ret = ath11k_ce_init_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to initialize CE: %d\n", ret); +- return ret; ++ goto err_firmware_stop; + } + + ret = ath11k_dp_alloc(ab); + if (ret) { + ath11k_err(ab, "failed to init DP: %d\n", ret); +- return ret; ++ goto err_firmware_stop; + } + + switch (ath11k_crypto_mode) { +@@ -1299,7 +1313,7 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) + set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); + + mutex_lock(&ab->core_lock); +- ret = ath11k_core_start(ab, ATH11K_FIRMWARE_MODE_NORMAL); ++ ret = ath11k_core_start(ab); + if (ret) { + ath11k_err(ab, "failed to start core: %d\n", ret); + goto err_dp_free; +@@ -1328,6 +1342,9 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) + err_dp_free: + ath11k_dp_free(ab); + mutex_unlock(&ab->core_lock); ++err_firmware_stop: ++ ath11k_qmi_firmware_stop(ab); ++ + return ret; + } + +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0234-ath11k-disable-spectral-scan-during-spectral-deinit.patch b/package/kernel/mac80211/patches/ath11k/0234-ath11k-disable-spectral-scan-during-spectral-deinit.patch new file mode 100644 index 00000000000000..e34b676eb3c19c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0234-ath11k-disable-spectral-scan-during-spectral-deinit.patch @@ -0,0 +1,99 @@ +From 161c64de239c7018e0295e7e0520a19f00aa32dc Mon Sep 17 00:00:00 2001 +From: Hari Chandrakanthan +Date: Sat, 23 Apr 2022 12:36:47 +0300 +Subject: [PATCH] ath11k: disable spectral scan during spectral deinit + +When ath11k modules are removed using rmmod with spectral scan enabled, +crash is observed. Different crash trace is observed for each crash. + +Send spectral scan disable WMI command to firmware before cleaning +the spectral dbring in the spectral_deinit API to avoid this crash. + +call trace from one of the crash observed: +[ 1252.880802] Unable to handle kernel NULL pointer dereference at virtual address 00000008 +[ 1252.882722] pgd = 0f42e886 +[ 1252.890955] [00000008] *pgd=00000000 +[ 1252.893478] Internal error: Oops: 5 [#1] PREEMPT SMP ARM +[ 1253.093035] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.89 #0 +[ 1253.115261] Hardware name: Generic DT based system +[ 1253.121149] PC is at ath11k_spectral_process_data+0x434/0x574 [ath11k] +[ 1253.125940] LR is at 0x88e31017 +[ 1253.132448] pc : [<7f9387b8>] lr : [<88e31017>] psr: a0000193 +[ 1253.135488] sp : 80d01bc8 ip : 00000001 fp : 970e0000 +[ 1253.141737] r10: 88e31000 r9 : 970ec000 r8 : 00000080 +[ 1253.146946] r7 : 94734040 r6 : a0000113 r5 : 00000057 r4 : 00000000 +[ 1253.152159] r3 : e18cb694 r2 : 00000217 r1 : 1df1f000 r0 : 00000001 +[ 1253.158755] Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user +[ 1253.165266] Control: 10c0383d Table: 5e71006a DAC: 00000055 +[ 1253.172472] Process swapper/0 (pid: 0, stack limit = 0x60870141) +[ 1253.458055] [<7f9387b8>] (ath11k_spectral_process_data [ath11k]) from [<7f917fdc>] (ath11k_dbring_buffer_release_event+0x214/0x2e4 [ath11k]) +[ 1253.466139] [<7f917fdc>] (ath11k_dbring_buffer_release_event [ath11k]) from [<7f8ea3c4>] (ath11k_wmi_tlv_op_rx+0x1840/0x29cc [ath11k]) +[ 1253.478807] [<7f8ea3c4>] (ath11k_wmi_tlv_op_rx [ath11k]) from [<7f8fe868>] (ath11k_htc_rx_completion_handler+0x180/0x4e0 [ath11k]) +[ 1253.490699] [<7f8fe868>] (ath11k_htc_rx_completion_handler [ath11k]) from [<7f91308c>] (ath11k_ce_per_engine_service+0x2c4/0x3b4 [ath11k]) +[ 1253.502386] [<7f91308c>] (ath11k_ce_per_engine_service [ath11k]) from [<7f9a4198>] (ath11k_pci_ce_tasklet+0x28/0x80 [ath11k_pci]) +[ 1253.514811] [<7f9a4198>] (ath11k_pci_ce_tasklet [ath11k_pci]) from [<8032227c>] (tasklet_action_common.constprop.2+0x64/0xe8) +[ 1253.526476] [<8032227c>] (tasklet_action_common.constprop.2) from [<803021e8>] (__do_softirq+0x130/0x2d0) +[ 1253.537756] [<803021e8>] (__do_softirq) from [<80322610>] (irq_exit+0xcc/0xe8) +[ 1253.547304] [<80322610>] (irq_exit) from [<8036a4a4>] (__handle_domain_irq+0x60/0xb4) +[ 1253.554428] [<8036a4a4>] (__handle_domain_irq) from [<805eb348>] (gic_handle_irq+0x4c/0x90) +[ 1253.562321] [<805eb348>] (gic_handle_irq) from [<80301a78>] (__irq_svc+0x58/0x8c) + +Tested-on: QCN6122 hw1.0 AHB WLAN.HK.2.6.0.1-00851-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Hari Chandrakanthan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1649396345-349-1-git-send-email-quic_haric@quicinc.com +--- + drivers/net/wireless/ath/ath11k/spectral.c | 17 +++++++++-------- + 1 file changed, 9 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/spectral.c b/drivers/net/wireless/ath/ath11k/spectral.c +index 2b18871d5f7c..516a7b4cd180 100644 +--- a/drivers/net/wireless/ath/ath11k/spectral.c ++++ b/drivers/net/wireless/ath/ath11k/spectral.c +@@ -212,7 +212,10 @@ static int ath11k_spectral_scan_config(struct ath11k *ar, + return -ENODEV; + + arvif->spectral_enabled = (mode != ATH11K_SPECTRAL_DISABLED); ++ ++ spin_lock_bh(&ar->spectral.lock); + ar->spectral.mode = mode; ++ spin_unlock_bh(&ar->spectral.lock); + + ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id, + ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR, +@@ -843,9 +846,6 @@ static inline void ath11k_spectral_ring_free(struct ath11k *ar) + { + struct ath11k_spectral *sp = &ar->spectral; + +- if (!sp->enabled) +- return; +- + ath11k_dbring_srng_cleanup(ar, &sp->rx_ring); + ath11k_dbring_buf_cleanup(ar, &sp->rx_ring); + } +@@ -897,15 +897,16 @@ void ath11k_spectral_deinit(struct ath11k_base *ab) + if (!sp->enabled) + continue; + +- ath11k_spectral_debug_unregister(ar); +- ath11k_spectral_ring_free(ar); ++ mutex_lock(&ar->conf_mutex); ++ ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED); ++ mutex_unlock(&ar->conf_mutex); + + spin_lock_bh(&sp->lock); +- +- sp->mode = ATH11K_SPECTRAL_DISABLED; + sp->enabled = false; +- + spin_unlock_bh(&sp->lock); ++ ++ ath11k_spectral_debug_unregister(ar); ++ ath11k_spectral_ring_free(ar); + } + } + +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0235-ath11k-read-country-code-from-SMBIOS-for-WCN6855-QCA.patch b/package/kernel/mac80211/patches/ath11k/0235-ath11k-read-country-code-from-SMBIOS-for-WCN6855-QCA.patch new file mode 100644 index 00000000000000..58568ca6d5db52 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0235-ath11k-read-country-code-from-SMBIOS-for-WCN6855-QCA.patch @@ -0,0 +1,150 @@ +From 66721bb4bbf25e990e003a2303ef86ce34556bac Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 20 Apr 2022 22:35:01 -0400 +Subject: [PATCH] ath11k: read country code from SMBIOS for WCN6855/QCA6390 + +This read the country code from SMBIOS and send the country code +to firmware, firmware will indicate the regulatory domain info of the +country code and then ath11k will use the info. + +dmesg: +[ 1242.637173] ath11k_pci 0000:02:00.0: chip_id 0x2 chip_family 0xb board_id 0xff soc_id 0x400c0200 +[ 1242.637176] ath11k_pci 0000:02:00.0: fw_version 0x110b09e5 fw_build_timestamp 2021-06-22 09:32 fw_build_id QC_IMAGE_VERSION_STRING=WLAN.HSP.1.1-02533-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +[ 1242.637253] ath11k_pci 0000:02:00.0: worldwide regdomain setting from SMBIOS +[ 1242.637259] ath11k_pci 0000:02:00.0: bdf variant name not found. +[ 1242.637261] ath11k_pci 0000:02:00.0: SMBIOS bdf variant name not set. +[ 1242.637263] ath11k_pci 0000:02:00.0: DT bdf variant name not set. +[ 1242.927543] ath11k_pci 0000:02:00.0: set current country pdev id 0 alpha2 00 + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220421023501.32167-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 26 ++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/core.h | 30 ++++++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/mac.c | 11 ++++++++++ + 3 files changed, 63 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 36d265454e26..7e074b7716e7 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -544,7 +544,7 @@ int ath11k_core_resume(struct ath11k_base *ab) + } + EXPORT_SYMBOL(ath11k_core_resume); + +-static void ath11k_core_check_bdfext(const struct dmi_header *hdr, void *data) ++static void ath11k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data) + { + struct ath11k_base *ab = data; + const char *magic = ATH11K_SMBIOS_BDF_EXT_MAGIC; +@@ -566,6 +566,28 @@ static void ath11k_core_check_bdfext(const struct dmi_header *hdr, void *data) + return; + } + ++ spin_lock_bh(&ab->base_lock); ++ ++ switch (smbios->country_code_flag) { ++ case ATH11K_SMBIOS_CC_ISO: ++ ab->new_alpha2[0] = (smbios->cc_code >> 8) & 0xff; ++ ab->new_alpha2[1] = smbios->cc_code & 0xff; ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot smbios cc_code %c%c\n", ++ ab->new_alpha2[0], ab->new_alpha2[1]); ++ break; ++ case ATH11K_SMBIOS_CC_WW: ++ ab->new_alpha2[0] = '0'; ++ ab->new_alpha2[1] = '0'; ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot smbios worldwide regdomain\n"); ++ break; ++ default: ++ ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot ignore smbios country code setting %d\n", ++ smbios->country_code_flag); ++ break; ++ } ++ ++ spin_unlock_bh(&ab->base_lock); ++ + if (!smbios->bdf_enabled) { + ath11k_dbg(ab, ATH11K_DBG_BOOT, "bdf variant name not found.\n"); + return; +@@ -605,7 +627,7 @@ static void ath11k_core_check_bdfext(const struct dmi_header *hdr, void *data) + int ath11k_core_check_smbios(struct ath11k_base *ab) + { + ab->qmi.target.bdf_ext[0] = '\0'; +- dmi_walk(ath11k_core_check_bdfext, ab); ++ dmi_walk(ath11k_core_check_cc_code_bdfext, ab); + + if (ab->qmi.target.bdf_ext[0] == '\0') + return -ENODATA; +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index fa299bfb4efc..7a505531acf9 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -169,12 +169,38 @@ struct ath11k_ext_irq_grp { + struct net_device napi_ndev; + }; + ++enum ath11k_smbios_cc_type { ++ /* disable country code setting from SMBIOS */ ++ ATH11K_SMBIOS_CC_DISABLE = 0, ++ ++ /* set country code by ANSI country name, based on ISO3166-1 alpha2 */ ++ ATH11K_SMBIOS_CC_ISO = 1, ++ ++ /* worldwide regdomain */ ++ ATH11K_SMBIOS_CC_WW = 2, ++}; ++ + struct ath11k_smbios_bdf { + struct dmi_header hdr; +- u32 padding; ++ ++ u8 features_disabled; ++ ++ /* enum ath11k_smbios_cc_type */ ++ u8 country_code_flag; ++ ++ /* To set specific country, you need to set country code ++ * flag=ATH11K_SMBIOS_CC_ISO first, then if country is United ++ * States, then country code value = 0x5553 ("US",'U' = 0x55, 'S'= ++ * 0x53). To set country to INDONESIA, then country code value = ++ * 0x4944 ("IN", 'I'=0x49, 'D'=0x44). If country code flag = ++ * ATH11K_SMBIOS_CC_WW, then you can use worldwide regulatory ++ * setting. ++ */ ++ u16 cc_code; ++ + u8 bdf_enabled; + u8 bdf_ext[]; +-}; ++} __packed; + + #define HEHANDLE_CAP_PHYINFO_SIZE 3 + #define HECAP_PHYINFO_SIZE 9 +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index c987f365c63a..c025bcb25c00 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8836,6 +8836,17 @@ static int __ath11k_mac_register(struct ath11k *ar) + goto err_unregister_hw; + } + ++ if (ab->hw_params.current_cc_support && ab->new_alpha2[0]) { ++ struct wmi_set_current_country_params set_current_param = {}; ++ ++ memcpy(&set_current_param.alpha2, ab->new_alpha2, 2); ++ memcpy(&ar->alpha2, ab->new_alpha2, 2); ++ ret = ath11k_wmi_send_set_current_country_cmd(ar, &set_current_param); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "failed set cc code for mac register: %d\n", ret); ++ } ++ + ret = ath11k_debugfs_register(ar); + if (ret) { + ath11k_err(ar->ab, "debugfs registration failed: %d\n", ret); +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0236-ath11k-Don-t-use-GFP_KERNEL-in-atomic-context.patch b/package/kernel/mac80211/patches/ath11k/0236-ath11k-Don-t-use-GFP_KERNEL-in-atomic-context.patch new file mode 100644 index 00000000000000..325d494a6bcb3c --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0236-ath11k-Don-t-use-GFP_KERNEL-in-atomic-context.patch @@ -0,0 +1,76 @@ +From eee645eccfc4edc9fb7dcef1b381e406c5dd4d14 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Fri, 29 Apr 2022 09:09:25 +0300 +Subject: [PATCH] ath11k: Don't use GFP_KERNEL in atomic context + +We are seeing below warning: +... +kernel: [ 5720.362941] BUG: sleeping function called from invalid context at include/linux/sched/mm.h:197 +kernel: [ 5720.362943] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 0, name: swapper/4 +kernel: [ 5720.362947] CPU: 4 PID: 0 Comm: swapper/4 Tainted: G W 5.10.90 #18 4fa489e3e5c16043994f416310c2f60eff666320 +kernel: [ 5720.362949] Hardware name: Google Nipperkin/Nipperkin, BIOS Google_Nipperkin.14316.0.0 10/30/2021 +kernel: [ 5720.362950] Call Trace: +kernel: [ 5720.362953] +kernel: [ 5720.362959] dump_stack+0x9c/0xe7 +kernel: [ 5720.362964] ___might_sleep+0x14a/0x160 +kernel: [ 5720.362967] kmem_cache_alloc+0x46/0x226 +kernel: [ 5720.362970] ? __alloc_skb+0x6c/0x19e +kernel: [ 5720.362972] __alloc_skb+0x6c/0x19e +kernel: [ 5720.362985] cfg80211_gtk_rekey_notify+0xa2/0x21d [cfg80211 2c8b5aee0416e7d010d70c332a47990fc843c1c5] +kernel: [ 5720.362995] ath11k_wmi_gtk_offload_status_event+0x102/0x155 [ath11k 4c6bb5f7331c81199d56a7e37bdc10030f167838] +kernel: [ 5720.363002] ath11k_wmi_tlv_op_rx+0x301/0x51b [ath11k 4c6bb5f7331c81199d56a7e37bdc10030f167838] +kernel: [ 5720.363009] ath11k_htc_rx_completion_handler+0xee/0x3f5 [ath11k 4c6bb5f7331c81199d56a7e37bdc10030f167838] +kernel: [ 5720.363017] ath11k_ce_per_engine_service+0x2aa/0x32c [ath11k 4c6bb5f7331c81199d56a7e37bdc10030f167838] +kernel: [ 5720.363024] ath11k_pci_ce_tasklet+0x1a/0x30 [ath11k_pci 9acc399855ea172aa14a892c0bfdba0ce22d6f07] +kernel: [ 5720.363028] tasklet_action_common+0x8d/0x9f +kernel: [ 5720.363032] __do_softirq+0x163/0x29a +kernel: [ 5720.363035] asm_call_irq_on_stack+0x12/0x20 +kernel: [ 5720.363037] +kernel: [ 5720.363041] do_softirq_own_stack+0x3c/0x48 +kernel: [ 5720.363043] __irq_exit_rcu+0x9b/0x9d +kernel: [ 5720.363046] common_interrupt+0xc9/0x14d +kernel: [ 5720.363049] asm_common_interrupt+0x1e/0x40 +kernel: [ 5720.363054] RIP: 0010:cpuidle_enter_state+0x1c5/0x2ac +kernel: [ 5720.363056] Code: 84 f6 4c 8b 75 c0 74 1e 48 c7 45 c8 00 00 00 00 9c 8f 45 c8 0f ba 65 c8 09 0f 82 d1 00 00 00 31 ff e8 4a bb 6c ff fb 45 85 e4 <78> 47 44 89 e0 48 6b d0 68 49 8b 4c 16 48 48 2b 5d b8 49 89 5d 18 +kernel: [ 5720.363058] RSP: 0018:ffffa7e640157e78 EFLAGS: 00000206 +kernel: [ 5720.363060] RAX: ffff9807ddf29b40 RBX: 00000533e033584c RCX: 00000533e033584c +kernel: [ 5720.363062] RDX: 0000000000000004 RSI: 0000000000000000 RDI: 0000000000000000 +kernel: [ 5720.363063] RBP: ffffa7e640157ec0 R08: 0000000000000002 R09: 00000533e171bb7a +kernel: [ 5720.363064] R10: 0000000000000900 R11: fffffffffffffffe R12: 0000000000000003 +kernel: [ 5720.363065] R13: ffff9804c2ef6000 R14: ffffffffbe9a7bd0 R15: 0000000000000003 +kernel: [ 5720.363069] ? cpuidle_enter_state+0x19a/0x2ac +kernel: [ 5720.363072] cpuidle_enter+0x2e/0x3d +kernel: [ 5720.363074] do_idle+0x163/0x1ee +kernel: [ 5720.363076] cpu_startup_entry+0x1d/0x1f +kernel: [ 5720.363078] secondary_startup_64_no_verify+0xb1/0xbb +... + +This is because GFP_KERNEL is used by ath11k_wmi_gtk_offload_status_event +while in atomic context. Fix it by using GFP_ATOMIC instead. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Fixes: a16d9b50cfba ("ath11k: support GTK rekey offload") +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220427120033.1046759-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 3c0ac1e29479..598ec061694f 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7844,7 +7844,7 @@ static void ath11k_wmi_gtk_offload_status_event(struct ath11k_base *ab, + replay_ctr_be = cpu_to_be64(replay_ctr); + + ieee80211_gtk_rekey_notify(arvif->vif, arvif->bssid, +- (void *)&replay_ctr_be, GFP_KERNEL); ++ (void *)&replay_ctr_be, GFP_ATOMIC); + + kfree(tb); + } +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0237-ath11k-change-management-tx-queue-to-avoid-connectio.patch b/package/kernel/mac80211/patches/ath11k/0237-ath11k-change-management-tx-queue-to-avoid-connectio.patch new file mode 100644 index 00000000000000..6d550adb7918a2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0237-ath11k-change-management-tx-queue-to-avoid-connectio.patch @@ -0,0 +1,51 @@ +From 3a597f0d425b2160ce4278c3e1c8384bec8ccfb4 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Fri, 29 Apr 2022 16:15:02 +0300 +Subject: [PATCH] ath11k: change management tx queue to avoid connection timed + out + +In the phase of wlan load, it has hw scan and 11d scan which sent to +firmware by ath11k, then hw scan and 11d scan will use about 14 seconds, +and meanwhile ath11k_reg_update_chan_list() is running in workqueue of +ath11k_base, and wait for 11d scan/hw scan finished. When the hw scan +finished, mac80211 will start to connect and send management packet, +at this moment, ath11k_reg_update_chan_list() is still waiting for 11d +scan finished, so wmi_mgmt_tx_work of ath11k will not run and thus the +tx management packet also not send out and lead authentication timed +out. + +log: +INFO kernel: [ 187.885322] wlan0: authenticate with 72:6c:57:43:9f:90 +INFO kernel: [ 187.937266] wlan0: send auth to 72:6c:57:43:9f:90 (try 1/3) +INFO kernel: [ 188.626944] wlan0: send auth to 72:6c:57:43:9f:90 (try 2/3) +INFO kernel: [ 189.650999] wlan0: send auth to 72:6c:57:43:9f:90 (try 3/3) +INFO kernel: [ 190.651917] wlan0: authentication with 72:6c:57:43:9f:90 timed out + +Change wmi_mgmt_tx_work to another queue workqueue_aux of ath11k_base, +then connection success. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220428023320.4007-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index c025bcb25c00..769319604340 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5592,7 +5592,7 @@ static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb, + + skb_queue_tail(q, skb); + atomic_inc(&ar->num_pending_mgmt_tx); +- queue_work(ar->ab->workqueue, &ar->wmi_mgmt_tx_work); ++ queue_work(ar->ab->workqueue_aux, &ar->wmi_mgmt_tx_work); + + return 0; + } +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0239-ath11k-Move-parameters-in-bus_params-to-hw_params.patch b/package/kernel/mac80211/patches/ath11k/0239-ath11k-Move-parameters-in-bus_params-to-hw_params.patch new file mode 100644 index 00000000000000..04fd2ee294e3c9 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0239-ath11k-Move-parameters-in-bus_params-to-hw_params.patch @@ -0,0 +1,391 @@ +From 92c1858e4399edc093a41cbf8f74874bdab59da7 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:34:55 +0530 +Subject: [PATCH] ath11k: Move parameters in bus_params to hw_params + +In ath11k, bus_params were added with an intention to hold +parameters related to bus (AHB/PCI), but this is not true +as some bus parameters being different between chipsets of +the same bus. + +With the addition of WCN6750 to ath11k, bus parameters are +going to be entirely different among AHB devices. Therefore, +it is wise to move bus_params to hw_params and get rid of +bus_params entirely. + +Also, mhi_support parameter is not used anywhere in the driver, +remove it from bus_params. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-3-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ahb.c | 11 ++-------- + drivers/net/wireless/ath/ath11k/core.c | 28 +++++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/core.h | 12 +---------- + drivers/net/wireless/ath/ath11k/hw.h | 4 ++++ + drivers/net/wireless/ath/ath11k/pci.c | 14 +++---------- + drivers/net/wireless/ath/ath11k/pcic.c | 4 ++-- + drivers/net/wireless/ath/ath11k/qmi.c | 23 +++++++++++---------- + 7 files changed, 49 insertions(+), 47 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c +index f407d4af2074..49d79cfbf21c 100644 +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -28,13 +29,6 @@ static const struct of_device_id ath11k_ahb_of_match[] = { + + MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match); + +-static const struct ath11k_bus_params ath11k_ahb_bus_params = { +- .mhi_support = false, +- .m3_fw_support = false, +- .fixed_bdf_addr = true, +- .fixed_mem_region = true, +-}; +- + #define ATH11K_IRQ_CE0_OFFSET 4 + + static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { +@@ -685,8 +679,7 @@ static int ath11k_ahb_probe(struct platform_device *pdev) + } + + ab = ath11k_core_alloc(&pdev->dev, sizeof(struct ath11k_ahb), +- ATH11K_BUS_AHB, +- &ath11k_ahb_bus_params); ++ ATH11K_BUS_AHB); + if (!ab) { + dev_err(&pdev->dev, "failed to allocate ath11k base\n"); + return -ENOMEM; +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 7e074b7716e7..19b5bb06c7e8 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -102,6 +102,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dbr_debug_support = true, + .global_reset = false, + .bios_sar_capa = NULL, ++ .m3_fw_support = false, ++ .fixed_bdf_addr = true, ++ .fixed_mem_region = true, ++ .static_window_map = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -169,6 +173,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dbr_debug_support = true, + .global_reset = false, + .bios_sar_capa = NULL, ++ .m3_fw_support = false, ++ .fixed_bdf_addr = true, ++ .fixed_mem_region = true, ++ .static_window_map = false, + }, + { + .name = "qca6390 hw2.0", +@@ -235,6 +243,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dbr_debug_support = false, + .global_reset = true, + .bios_sar_capa = NULL, ++ .m3_fw_support = true, ++ .fixed_bdf_addr = false, ++ .fixed_mem_region = false, ++ .static_window_map = false, + }, + { + .name = "qcn9074 hw1.0", +@@ -301,6 +313,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dbr_debug_support = true, + .global_reset = false, + .bios_sar_capa = NULL, ++ .m3_fw_support = true, ++ .fixed_bdf_addr = false, ++ .fixed_mem_region = false, ++ .static_window_map = true, + }, + { + .name = "wcn6855 hw2.0", +@@ -367,6 +383,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dbr_debug_support = false, + .global_reset = true, + .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855, ++ .m3_fw_support = true, ++ .fixed_bdf_addr = false, ++ .fixed_mem_region = false, ++ .static_window_map = false, + }, + { + .name = "wcn6855 hw2.1", +@@ -432,6 +452,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dbr_debug_support = false, + .global_reset = true, + .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855, ++ .m3_fw_support = true, ++ .fixed_bdf_addr = false, ++ .fixed_mem_region = false, ++ .static_window_map = false, + }, + }; + +@@ -1730,8 +1754,7 @@ void ath11k_core_free(struct ath11k_base *ab) + EXPORT_SYMBOL(ath11k_core_free); + + struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, +- enum ath11k_bus bus, +- const struct ath11k_bus_params *bus_params) ++ enum ath11k_bus bus) + { + struct ath11k_base *ab; + +@@ -1770,7 +1793,6 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, + init_completion(&ab->wow.wakeup_completed); + + ab->dev = dev; +- ab->bus_params = *bus_params; + ab->hif.bus = bus; + + return ab; +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index 7a505531acf9..d8ab28413d95 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -748,14 +748,6 @@ struct ath11k_board_data { + size_t len; + }; + +-struct ath11k_bus_params { +- bool mhi_support; +- bool m3_fw_support; +- bool fixed_bdf_addr; +- bool fixed_mem_region; +- bool static_window_map; +-}; +- + struct ath11k_pci_ops { + int (*wakeup)(struct ath11k_base *ab); + void (*release)(struct ath11k_base *ab); +@@ -887,7 +879,6 @@ struct ath11k_base { + int bd_api; + + struct ath11k_hw_params hw_params; +- struct ath11k_bus_params bus_params; + + const struct firmware *cal_file; + +@@ -1135,8 +1126,7 @@ int ath11k_core_pre_init(struct ath11k_base *ab); + int ath11k_core_init(struct ath11k_base *ath11k); + void ath11k_core_deinit(struct ath11k_base *ath11k); + struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, +- enum ath11k_bus bus, +- const struct ath11k_bus_params *bus_params); ++ enum ath11k_bus bus); + void ath11k_core_free(struct ath11k_base *ath11k); + int ath11k_core_fetch_bdf(struct ath11k_base *ath11k, + struct ath11k_board_data *bd); +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index b7ece3d5678c..09fa020bfee4 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -196,6 +196,10 @@ struct ath11k_hw_params { + bool dbr_debug_support; + bool global_reset; + const struct cfg80211_sar_capa *bios_sar_capa; ++ bool m3_fw_support; ++ bool fixed_bdf_addr; ++ bool fixed_mem_region; ++ bool static_window_map; + }; + + struct ath11k_hw_ops { +diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c +index 024661a17008..dedf1b88ddf6 100644 +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -115,13 +115,6 @@ static const struct ath11k_pci_ops ath11k_pci_ops_qcn9074 = { + .window_read32 = ath11k_pci_window_read32, + }; + +-static const struct ath11k_bus_params ath11k_pci_bus_params = { +- .mhi_support = true, +- .m3_fw_support = true, +- .fixed_bdf_addr = false, +- .fixed_mem_region = false, +-}; +- + static const struct ath11k_msi_config msi_config_one_msi = { + .total_vectors = 1, + .total_users = 4, +@@ -593,7 +586,7 @@ static int ath11k_pci_power_up(struct ath11k_base *ab) + return ret; + } + +- if (ab->bus_params.static_window_map) ++ if (ab->hw_params.static_window_map) + ath11k_pci_select_static_window(ab_pci); + + return 0; +@@ -706,8 +699,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev, + u32 soc_hw_version_major, soc_hw_version_minor, addr; + int ret; + +- ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI, +- &ath11k_pci_bus_params); ++ ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI); ++ + if (!ab) { + dev_err(&pdev->dev, "failed to allocate ath11k base\n"); + return -ENOMEM; +@@ -764,7 +757,6 @@ static int ath11k_pci_probe(struct pci_dev *pdev, + ab->pci.ops = &ath11k_pci_ops_qca6390; + break; + case QCN9074_DEVICE_ID: +- ab->bus_params.static_window_map = true; + ab->pci.ops = &ath11k_pci_ops_qcn9074; + ab->hw_rev = ATH11K_HW_QCN9074_HW10; + break; +diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c +index 63c678aea29e..7a920d65023f 100644 +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -163,7 +163,7 @@ void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value) + if (offset < ATH11K_PCI_WINDOW_START) { + iowrite32(value, ab->mem + offset); + } else { +- if (ab->bus_params.static_window_map) ++ if (ab->hw_params.static_window_map) + window_start = ath11k_pcic_get_window_start(ab, offset); + else + window_start = ATH11K_PCI_WINDOW_START; +@@ -198,7 +198,7 @@ u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) + if (offset < ATH11K_PCI_WINDOW_START) { + val = ioread32(ab->mem + offset); + } else { +- if (ab->bus_params.static_window_map) ++ if (ab->hw_params.static_window_map) + window_start = ath11k_pcic_get_window_start(ab, offset); + else + window_start = ATH11K_PCI_WINDOW_START; +diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c +index 0442faa3b7af..c89e76108237 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -1648,7 +1649,7 @@ static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) + req.bdf_support_valid = 1; + req.bdf_support = 1; + +- if (ab->bus_params.m3_fw_support) { ++ if (ab->hw_params.m3_fw_support) { + req.m3_support_valid = 1; + req.m3_support = 1; + req.m3_cache_support_valid = 1; +@@ -1803,7 +1804,7 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab) + * failure to FW and FW will then request mulitple blocks of small + * chunk size memory. + */ +- if (!(ab->bus_params.fixed_mem_region || ++ if (!(ab->hw_params.fixed_mem_region || + test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && + ab->qmi.target_mem_delayed) { + delayed = true; +@@ -1873,7 +1874,7 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) + int i; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { +- if ((ab->bus_params.fixed_mem_region || ++ if ((ab->hw_params.fixed_mem_region || + test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && + ab->qmi.target_mem[i].iaddr) + iounmap(ab->qmi.target_mem[i].iaddr); +@@ -2124,7 +2125,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, + + memset(&resp, 0, sizeof(resp)); + +- if (ab->bus_params.fixed_bdf_addr) { ++ if (ab->hw_params.fixed_bdf_addr) { + 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"); +@@ -2153,7 +2154,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, + req->end = 1; + } + +- if (ab->bus_params.fixed_bdf_addr || ++ if (ab->hw_params.fixed_bdf_addr || + type == ATH11K_QMI_FILE_TYPE_EEPROM) { + req->data_valid = 0; + req->end = 1; +@@ -2162,7 +2163,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, + memcpy(req->data, temp, req->data_len); + } + +- if (ab->bus_params.fixed_bdf_addr) { ++ if (ab->hw_params.fixed_bdf_addr) { + if (type == ATH11K_QMI_FILE_TYPE_CALDATA) + bdf_addr += ab->hw_params.fw.cal_offset; + +@@ -2201,7 +2202,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, + goto err_iounmap; + } + +- if (ab->bus_params.fixed_bdf_addr || ++ if (ab->hw_params.fixed_bdf_addr || + type == ATH11K_QMI_FILE_TYPE_EEPROM) { + remaining = 0; + } else { +@@ -2214,7 +2215,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, + } + + err_iounmap: +- if (ab->bus_params.fixed_bdf_addr) ++ if (ab->hw_params.fixed_bdf_addr) + iounmap(bdf_addr); + + err_free_req: +@@ -2353,7 +2354,7 @@ static void ath11k_qmi_m3_free(struct ath11k_base *ab) + { + struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; + +- if (!ab->bus_params.m3_fw_support || !m3_mem->vaddr) ++ if (!ab->hw_params.m3_fw_support || !m3_mem->vaddr) + return; + + dma_free_coherent(ab->dev, m3_mem->size, +@@ -2373,7 +2374,7 @@ static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + +- if (ab->bus_params.m3_fw_support) { ++ if (ab->hw_params.m3_fw_support) { + ret = ath11k_qmi_m3_load(ab); + if (ret) { + ath11k_err(ab, "failed to load m3 firmware: %d", ret); +@@ -2792,7 +2793,7 @@ static void ath11k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl, + msg->mem_seg[i].type, msg->mem_seg[i].size); + } + +- if (ab->bus_params.fixed_mem_region || ++ if (ab->hw_params.fixed_mem_region || + test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { + ret = ath11k_qmi_assign_target_mem_chunk(ab); + if (ret) { +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0240-ath11k-Add-HW-params-for-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0240-ath11k-Add-HW-params-for-WCN6750.patch new file mode 100644 index 00000000000000..e57d06667e06e3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0240-ath11k-Add-HW-params-for-WCN6750.patch @@ -0,0 +1,199 @@ +From d1e1edfde0352321af52599ad447b71c91bc6e76 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:34:56 +0530 +Subject: [PATCH] ath11k: Add HW params for WCN6750 + +WCN6750 is a PCIe based solution that is attached to and enumerated +by the WPSS (Wireless Processor SubSystem) Q6 processor. + +Though it is a PCIe device, since it is not attached to APSS processor +(Application Processor SubSystem), APSS will be unaware of such a decice +and hence it is registered to the APSS processor as a platform device(AHB). +Because of this hybrid nature, it is called as a hybrid bus device. + +A new variable hybrid_bus_type is defined in hw_params to indicate the +hybrid nature of the device. + +Add HW params for WCN6750. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-4-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 73 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/qmi.h | 2 + + 4 files changed, 77 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 19b5bb06c7e8..1220514e19bf 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -106,6 +106,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_bdf_addr = true, + .fixed_mem_region = true, + .static_window_map = false, ++ .hybrid_bus_type = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -177,6 +178,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_bdf_addr = true, + .fixed_mem_region = true, + .static_window_map = false, ++ .hybrid_bus_type = false, + }, + { + .name = "qca6390 hw2.0", +@@ -247,6 +249,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, ++ .hybrid_bus_type = false, + }, + { + .name = "qcn9074 hw1.0", +@@ -317,6 +320,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = true, ++ .hybrid_bus_type = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -387,6 +391,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, ++ .hybrid_bus_type = false, + }, + { + .name = "wcn6855 hw2.1", +@@ -456,6 +461,74 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, ++ .hybrid_bus_type = false, ++ }, ++ { ++ .name = "wcn6750 hw1.0", ++ .hw_rev = ATH11K_HW_WCN6750_HW10, ++ .fw = { ++ .dir = "WCN6750/hw1.0", ++ .board_size = 256 * 1024, ++ .cal_offset = 128 * 1024, ++ }, ++ .max_radios = 1, ++ .bdf_addr = 0x4B0C0000, ++ .ring_mask = &ath11k_hw_ring_mask_qca6390, ++ .internal_sleep_clock = false, ++ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750, ++ .host_ce_config = ath11k_host_ce_config_qca6390, ++ .ce_count = 9, ++ .target_ce_config = ath11k_target_ce_config_wlan_qca6390, ++ .target_ce_count = 9, ++ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, ++ .svc_to_ce_map_len = 14, ++ .rfkill_pin = 0, ++ .rfkill_cfg = 0, ++ .rfkill_on_level = 0, ++ .single_pdev_only = true, ++ .rxdma1_enable = false, ++ .num_rxmda_per_pdev = 1, ++ .rx_mac_buf_ring = true, ++ .vdev_start_delay = true, ++ .htt_peer_map_v2 = false, ++ ++ .spectral = { ++ .fft_sz = 0, ++ .fft_pad_sz = 0, ++ .summary_pad_sz = 0, ++ .fft_hdr_len = 0, ++ .max_fft_bins = 0, ++ }, ++ ++ .interface_modes = BIT(NL80211_IFTYPE_STATION) | ++ BIT(NL80211_IFTYPE_AP), ++ .supports_monitor = false, ++ .supports_shadow_regs = true, ++ .idle_ps = true, ++ .supports_sta_ps = true, ++ .cold_boot_calib = false, ++ .fw_mem_mode = 0, ++ .num_vdevs = 16 + 1, ++ .num_peers = 512, ++ .supports_suspend = false, ++ .supports_regdb = true, ++ .fix_l1ss = false, ++ .credit_flow = true, ++ .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, ++ .hal_params = &ath11k_hw_hal_params_qca6390, ++ .supports_dynamic_smps_6ghz = false, ++ .alloc_cacheable_memory = false, ++ .supports_rssi_stats = true, ++ .fw_wmi_diag_event = false, ++ .current_cc_support = true, ++ .dbr_debug_support = false, ++ .global_reset = false, ++ .bios_sar_capa = NULL, ++ .m3_fw_support = false, ++ .fixed_bdf_addr = false, ++ .fixed_mem_region = false, ++ .static_window_map = true, ++ .hybrid_bus_type = true, + }, + }; + +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index d8ab28413d95..fdea44627125 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -140,6 +140,7 @@ enum ath11k_hw_rev { + ATH11K_HW_QCN9074_HW10, + ATH11K_HW_WCN6855_HW20, + ATH11K_HW_WCN6855_HW21, ++ ATH11K_HW_WCN6750_HW10, + }; + + enum ath11k_firmware_mode { +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 09fa020bfee4..03eb5dfd4a5e 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -200,6 +200,7 @@ struct ath11k_hw_params { + bool fixed_bdf_addr; + bool fixed_mem_region; + bool static_window_map; ++ bool hybrid_bus_type; + }; + + struct ath11k_hw_ops { +diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h +index 61678de56ac7..e0201e184733 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #ifndef ATH11K_QMI_H +@@ -20,6 +21,7 @@ + #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01 + #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_WLANFW_MAX_TIMESTAMP_LEN_V01 32 + #define ATH11K_QMI_RESP_LEN_MAX 8192 + #define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0241-ath11k-Add-register-access-logic-for-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0241-ath11k-Add-register-access-logic-for-WCN6750.patch new file mode 100644 index 00000000000000..ab21aef7deae38 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0241-ath11k-Add-register-access-logic-for-WCN6750.patch @@ -0,0 +1,198 @@ +From 56c8ccf331bd2ebf8b85f70efb4844803ef3f768 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:34:57 +0530 +Subject: [PATCH] ath11k: Add register access logic for WCN6750 + +WCN6750 uses static window mapping to access the HW registers. +Unlike QCN9074 which uses 3rd window for UMAC and 2nd window +for CE register access, WCN6750 uses 1st window for UMAC +and 2nd window for CE registers. + +Also, refactor the code so that WCN6750 can use the existing +ath11k_pci_read32/write32() APIs for accessing the registers. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-5-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 14 +++++++ + drivers/net/wireless/ath/ath11k/hw.h | 2 + + drivers/net/wireless/ath/ath11k/pcic.c | 54 +++++++++----------------- + 3 files changed, 35 insertions(+), 35 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 1220514e19bf..3be4327b4d9c 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -107,6 +107,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_mem_region = true, + .static_window_map = false, + .hybrid_bus_type = false, ++ .dp_window_idx = 0, ++ .ce_window_idx = 0, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -179,6 +181,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_mem_region = true, + .static_window_map = false, + .hybrid_bus_type = false, ++ .dp_window_idx = 0, ++ .ce_window_idx = 0, + }, + { + .name = "qca6390 hw2.0", +@@ -250,6 +254,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, ++ .dp_window_idx = 0, ++ .ce_window_idx = 0, + }, + { + .name = "qcn9074 hw1.0", +@@ -321,6 +327,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_mem_region = false, + .static_window_map = true, + .hybrid_bus_type = false, ++ .dp_window_idx = 3, ++ .ce_window_idx = 2, + }, + { + .name = "wcn6855 hw2.0", +@@ -392,6 +400,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, ++ .dp_window_idx = 0, ++ .ce_window_idx = 0, + }, + { + .name = "wcn6855 hw2.1", +@@ -462,6 +472,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, ++ .dp_window_idx = 0, ++ .ce_window_idx = 0, + }, + { + .name = "wcn6750 hw1.0", +@@ -529,6 +541,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .fixed_mem_region = false, + .static_window_map = true, + .hybrid_bus_type = true, ++ .dp_window_idx = 1, ++ .ce_window_idx = 2, + }, + }; + +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 03eb5dfd4a5e..b63538084215 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -201,6 +201,8 @@ struct ath11k_hw_params { + bool fixed_mem_region; + bool static_window_map; + bool hybrid_bus_type; ++ u8 dp_window_idx; ++ u8 ce_window_idx; + }; + + struct ath11k_hw_ops { +diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c +index 7a920d65023f..46cf96d3e1d2 100644 +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -134,16 +134,13 @@ EXPORT_SYMBOL(ath11k_pcic_init_msi_config); + static inline u32 ath11k_pcic_get_window_start(struct ath11k_base *ab, + u32 offset) + { +- u32 window_start; ++ u32 window_start = 0; + +- /* If offset lies within DP register range, use 3rd window */ + if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK) +- window_start = 3 * ATH11K_PCI_WINDOW_START; +- /* If offset lies within CE register range, use 2nd window */ +- else if ((offset ^ HAL_CE_WFSS_CE_REG_BASE) < ATH11K_PCI_WINDOW_RANGE_MASK) +- window_start = 2 * ATH11K_PCI_WINDOW_START; +- else +- window_start = ATH11K_PCI_WINDOW_START; ++ window_start = ab->hw_params.dp_window_idx * ATH11K_PCI_WINDOW_START; ++ else if ((offset ^ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab)) < ++ ATH11K_PCI_WINDOW_RANGE_MASK) ++ window_start = ab->hw_params.ce_window_idx * ATH11K_PCI_WINDOW_START; + + return window_start; + } +@@ -162,19 +159,12 @@ void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value) + + if (offset < ATH11K_PCI_WINDOW_START) { + iowrite32(value, ab->mem + offset); +- } else { +- if (ab->hw_params.static_window_map) +- window_start = ath11k_pcic_get_window_start(ab, offset); +- else +- window_start = ATH11K_PCI_WINDOW_START; +- +- if (window_start == ATH11K_PCI_WINDOW_START && +- ab->pci.ops->window_write32) { +- ab->pci.ops->window_write32(ab, offset, value); +- } else { +- iowrite32(value, ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- } ++ } else if (ab->hw_params.static_window_map) { ++ window_start = ath11k_pcic_get_window_start(ab, offset); ++ iowrite32(value, ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ } else if (ab->pci.ops->window_write32) { ++ ab->pci.ops->window_write32(ab, offset, value); + } + + if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && +@@ -185,7 +175,8 @@ void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value) + + u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) + { +- u32 val, window_start; ++ u32 val = 0; ++ u32 window_start; + int ret = 0; + + /* for offset beyond BAR + 4K - 32, may +@@ -197,19 +188,12 @@ u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) + + if (offset < ATH11K_PCI_WINDOW_START) { + val = ioread32(ab->mem + offset); +- } else { +- if (ab->hw_params.static_window_map) +- window_start = ath11k_pcic_get_window_start(ab, offset); +- else +- window_start = ATH11K_PCI_WINDOW_START; +- +- if (window_start == ATH11K_PCI_WINDOW_START && +- ab->pci.ops->window_read32) { +- val = ab->pci.ops->window_read32(ab, offset); +- } else { +- val = ioread32(ab->mem + window_start + +- (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); +- } ++ } else if (ab->hw_params.static_window_map) { ++ window_start = ath11k_pcic_get_window_start(ab, offset); ++ val = ioread32(ab->mem + window_start + ++ (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); ++ } else if (ab->pci.ops->window_read32) { ++ val = ab->pci.ops->window_read32(ab, offset); + } + + if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0242-ath11k-Fetch-device-information-via-QMI-for-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0242-ath11k-Fetch-device-information-via-QMI-for-WCN6750.patch new file mode 100644 index 00000000000000..0ef8cd3237496e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0242-ath11k-Fetch-device-information-via-QMI-for-WCN6750.patch @@ -0,0 +1,253 @@ +From 676f8905fff904ea3e4ee52086a18ab54912b410 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:34:58 +0530 +Subject: [PATCH] ath11k: Fetch device information via QMI for WCN6750 + +Since WPPS Q6 does the PCIe enumeration of WCN6750, device +information like BAR and BAR size is not known to the APPS +processor (Application Processor SubSystem). In order to +fetch these details, a QMI message called device info request +will be sent to the target. Therefore, add logic to fetch +BAR details from the target. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-6-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 144 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/qmi.h | 24 ++++- + 2 files changed, 164 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c +index c89e76108237..ad422256f1c6 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -13,6 +13,8 @@ + #include + #include + #include ++#include ++#include + + #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 + #define HOST_CSTATE_BIT 0x04 +@@ -749,6 +751,68 @@ static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { + }, + }; + ++static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { ++ { ++ .data_type = QMI_EOTI, ++ .array_type = NO_ARRAY, ++ .tlv_type = QMI_COMMON_TLV_TYPE, ++ }, ++}; ++ ++static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { ++ { ++ .data_type = QMI_STRUCT, ++ .elem_len = 1, ++ .elem_size = sizeof(struct qmi_response_type_v01), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x02, ++ .offset = offsetof(struct qmi_wlanfw_device_info_resp_msg_v01, ++ resp), ++ .ei_array = qmi_response_type_v01_ei, ++ }, ++ { ++ .data_type = QMI_OPT_FLAG, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x10, ++ .offset = offsetof(struct qmi_wlanfw_device_info_resp_msg_v01, ++ bar_addr_valid), ++ }, ++ { ++ .data_type = QMI_UNSIGNED_8_BYTE, ++ .elem_len = 1, ++ .elem_size = sizeof(u64), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x10, ++ .offset = offsetof(struct qmi_wlanfw_device_info_resp_msg_v01, ++ bar_addr), ++ }, ++ { ++ .data_type = QMI_OPT_FLAG, ++ .elem_len = 1, ++ .elem_size = sizeof(u8), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x11, ++ .offset = offsetof(struct qmi_wlanfw_device_info_resp_msg_v01, ++ bar_size_valid), ++ }, ++ { ++ .data_type = QMI_UNSIGNED_4_BYTE, ++ .elem_len = 1, ++ .elem_size = sizeof(u32), ++ .array_type = NO_ARRAY, ++ .tlv_type = 0x11, ++ .offset = offsetof(struct qmi_wlanfw_device_info_resp_msg_v01, ++ bar_size), ++ }, ++ { ++ .data_type = QMI_EOTI, ++ .array_type = NO_ARRAY, ++ .tlv_type = QMI_COMMON_TLV_TYPE, ++ }, ++}; ++ + static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, +@@ -2008,6 +2072,80 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) + return 0; + } + ++static int ath11k_qmi_request_device_info(struct ath11k_base *ab) ++{ ++ struct qmi_wlanfw_device_info_req_msg_v01 req = {}; ++ struct qmi_wlanfw_device_info_resp_msg_v01 resp = {}; ++ struct qmi_txn txn; ++ void __iomem *bar_addr_va; ++ int ret; ++ ++ /* device info message req is only sent for hybrid bus devices */ ++ if (!ab->hw_params.hybrid_bus_type) ++ return 0; ++ ++ ret = qmi_txn_init(&ab->qmi.handle, &txn, ++ qmi_wlfw_device_info_resp_msg_v01_ei, &resp); ++ if (ret < 0) ++ goto out; ++ ++ ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, ++ QMI_WLANFW_DEVICE_INFO_REQ_V01, ++ QMI_WLANFW_DEVICE_INFO_REQ_MSG_V01_MAX_LEN, ++ qmi_wlanfw_device_info_req_msg_v01_ei, &req); ++ if (ret < 0) { ++ qmi_txn_cancel(&txn); ++ ath11k_warn(ab, "failed to send qmi target device info request: %d\n", ++ ret); ++ goto out; ++ } ++ ++ ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); ++ if (ret < 0) { ++ ath11k_warn(ab, "failed to wait qmi target device info request: %d\n", ++ ret); ++ goto out; ++ } ++ ++ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ++ ath11k_warn(ab, "qmi device info request failed: %d %d\n", ++ resp.resp.result, resp.resp.error); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!resp.bar_addr_valid || !resp.bar_size_valid) { ++ ath11k_warn(ab, "qmi device info response invalid: %d %d\n", ++ resp.resp.result, resp.resp.error); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!resp.bar_addr || ++ resp.bar_size != ATH11K_QMI_DEVICE_BAR_SIZE) { ++ ath11k_warn(ab, "qmi device info invalid address and size: %llu %u\n", ++ resp.bar_addr, resp.bar_size); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ bar_addr_va = devm_ioremap(ab->dev, resp.bar_addr, resp.bar_size); ++ ++ if (!bar_addr_va) { ++ ath11k_warn(ab, "qmi device info ioremap failed\n"); ++ ab->mem_len = 0; ++ ret = -EIO; ++ goto out; ++ } ++ ++ ab->mem = bar_addr_va; ++ ab->mem_len = resp.bar_size; ++ ++ return 0; ++out: ++ return ret; ++} ++ + static int ath11k_qmi_request_target_cap(struct ath11k_base *ab) + { + struct qmi_wlanfw_cap_req_msg_v01 req; +@@ -2749,6 +2887,12 @@ static int ath11k_qmi_event_load_bdf(struct ath11k_qmi *qmi) + return ret; + } + ++ ret = ath11k_qmi_request_device_info(ab); ++ if (ret < 0) { ++ ath11k_warn(ab, "failed to request qmi device info: %d\n", ret); ++ return ret; ++ } ++ + if (ab->hw_params.supports_regdb) + ath11k_qmi_load_bdf_qmi(ab, true); + +diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h +index e0201e184733..c24e6995cca3 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -38,6 +38,8 @@ + #define ATH11K_FIRMWARE_MODE_OFF 4 + #define ATH11K_COLD_BOOT_FW_RESET_DELAY (40 * HZ) + ++#define ATH11K_QMI_DEVICE_BAR_SIZE 0x200000 ++ + struct ath11k_base; + + enum ath11k_qmi_file_type { +@@ -287,10 +289,12 @@ struct qmi_wlanfw_fw_cold_cal_done_ind_msg_v01 { + char placeholder; + }; + +-#define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 +-#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 235 +-#define QMI_WLANFW_CAP_REQ_V01 0x0024 +-#define QMI_WLANFW_CAP_RESP_V01 0x0024 ++#define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 ++#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 235 ++#define QMI_WLANFW_CAP_REQ_V01 0x0024 ++#define QMI_WLANFW_CAP_RESP_V01 0x0024 ++#define QMI_WLANFW_DEVICE_INFO_REQ_V01 0x004C ++#define QMI_WLANFW_DEVICE_INFO_REQ_MSG_V01_MAX_LEN 0 + + enum qmi_wlanfw_pipedir_enum_v01 { + QMI_WLFW_PIPEDIR_NONE_V01 = 0, +@@ -383,6 +387,18 @@ struct qmi_wlanfw_cap_req_msg_v01 { + char placeholder; + }; + ++struct qmi_wlanfw_device_info_req_msg_v01 { ++ char placeholder; ++}; ++ ++struct qmi_wlanfw_device_info_resp_msg_v01 { ++ struct qmi_response_type_v01 resp; ++ u64 bar_addr; ++ u32 bar_size; ++ u8 bar_addr_valid; ++ u8 bar_size_valid; ++}; ++ + #define QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN 6182 + #define QMI_WLANFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_LEN 7 + #define QMI_WLANFW_BDF_DOWNLOAD_RESP_V01 0x0025 +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0243-ath11k-Add-QMI-changes-for-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0243-ath11k-Add-QMI-changes-for-WCN6750.patch new file mode 100644 index 00000000000000..514f221f4b54af --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0243-ath11k-Add-QMI-changes-for-WCN6750.patch @@ -0,0 +1,228 @@ +From 73d3e71306fe864d9667e8d37f731e93a91e2040 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:34:59 +0530 +Subject: [PATCH] ath11k: Add QMI changes for WCN6750 + +In the case of WCN6750, FW doesn't request for DDR memory +via QMI, instead it uses a fixed 12MB reserved Memory region +in the DDR which is called as MSA region. As a result, QMI +message sequence is not same as other ath11k supported devices. + +Also, M3 firmware will be bundled into the FW and will be +downloaded to the target as part of Q6 boot. + +This is the QMI flow in the case of WCN6750, + +1) QMI firmware indication REQ/RESP +2) QMI host capability REQ/RESP +3) QMI target capability REQ/RESP +4) QMI device info REQ/RESP +5) QMI BDF download +6) QMI FW ready + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-7-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 7 +++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/qmi.c | 76 ++++++++++++++++---------- + 3 files changed, 56 insertions(+), 28 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 3be4327b4d9c..64cf87fd6a7f 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -109,6 +109,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .hybrid_bus_type = false, + .dp_window_idx = 0, + .ce_window_idx = 0, ++ .fixed_fw_mem = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -183,6 +184,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .hybrid_bus_type = false, + .dp_window_idx = 0, + .ce_window_idx = 0, ++ .fixed_fw_mem = false, + }, + { + .name = "qca6390 hw2.0", +@@ -256,6 +258,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .hybrid_bus_type = false, + .dp_window_idx = 0, + .ce_window_idx = 0, ++ .fixed_fw_mem = false, + }, + { + .name = "qcn9074 hw1.0", +@@ -329,6 +332,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .hybrid_bus_type = false, + .dp_window_idx = 3, + .ce_window_idx = 2, ++ .fixed_fw_mem = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -402,6 +406,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .hybrid_bus_type = false, + .dp_window_idx = 0, + .ce_window_idx = 0, ++ .fixed_fw_mem = false, + }, + { + .name = "wcn6855 hw2.1", +@@ -474,6 +479,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .hybrid_bus_type = false, + .dp_window_idx = 0, + .ce_window_idx = 0, ++ .fixed_fw_mem = false, + }, + { + .name = "wcn6750 hw1.0", +@@ -543,6 +549,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .hybrid_bus_type = true, + .dp_window_idx = 1, + .ce_window_idx = 2, ++ .fixed_fw_mem = true, + }, + }; + +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index b63538084215..b5a4758a6bc5 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -203,6 +203,7 @@ struct ath11k_hw_params { + bool hybrid_bus_type; + u8 dp_window_idx; + u8 ce_window_idx; ++ bool fixed_fw_mem; + }; + + struct ath11k_hw_ops { +diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c +index ad422256f1c6..d1e945074bc1 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1799,10 +1799,6 @@ static int ath11k_qmi_fw_ind_register_send(struct ath11k_base *ab) + req->client_id = QMI_WLANFW_CLIENT_ID; + req->fw_ready_enable_valid = 1; + req->fw_ready_enable = 1; +- req->request_mem_enable_valid = 1; +- req->request_mem_enable = 1; +- req->fw_mem_ready_enable_valid = 1; +- req->fw_mem_ready_enable = 1; + req->cal_done_enable_valid = 1; + req->cal_done_enable = 1; + req->fw_init_done_enable_valid = 1; +@@ -1811,6 +1807,17 @@ static int ath11k_qmi_fw_ind_register_send(struct ath11k_base *ab) + req->pin_connect_result_enable_valid = 0; + req->pin_connect_result_enable = 0; + ++ /* WCN6750 doesn't request for DDR memory via QMI, ++ * instead it uses a fixed 12MB reserved memory ++ * region in DDR. ++ */ ++ if (!ab->hw_params.fixed_fw_mem) { ++ req->request_mem_enable_valid = 1; ++ req->request_mem_enable = 1; ++ req->fw_mem_ready_enable_valid = 1; ++ req->fw_mem_ready_enable = 1; ++ } ++ + ret = qmi_txn_init(handle, &txn, + qmi_wlanfw_ind_register_resp_msg_v01_ei, resp); + if (ret < 0) +@@ -2840,27 +2847,6 @@ ath11k_qmi_driver_event_post(struct ath11k_qmi *qmi, + return 0; + } + +-static int ath11k_qmi_event_server_arrive(struct ath11k_qmi *qmi) +-{ +- struct ath11k_base *ab = qmi->ab; +- int ret; +- +- ret = ath11k_qmi_fw_ind_register_send(ab); +- if (ret < 0) { +- ath11k_warn(ab, "failed to send qmi firmware indication: %d\n", +- ret); +- return ret; +- } +- +- ret = ath11k_qmi_host_cap_send(ab); +- if (ret < 0) { +- ath11k_warn(ab, "failed to send qmi host cap: %d\n", ret); +- return ret; +- } +- +- return ret; +-} +- + static int ath11k_qmi_event_mem_request(struct ath11k_qmi *qmi) + { + struct ath11k_base *ab = qmi->ab; +@@ -2902,9 +2888,33 @@ static int ath11k_qmi_event_load_bdf(struct ath11k_qmi *qmi) + return ret; + } + +- ret = ath11k_qmi_wlanfw_m3_info_send(ab); ++ return 0; ++} ++ ++static int ath11k_qmi_event_server_arrive(struct ath11k_qmi *qmi) ++{ ++ struct ath11k_base *ab = qmi->ab; ++ int ret; ++ ++ ret = ath11k_qmi_fw_ind_register_send(ab); ++ if (ret < 0) { ++ ath11k_warn(ab, "failed to send qmi firmware indication: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = ath11k_qmi_host_cap_send(ab); + if (ret < 0) { +- ath11k_warn(ab, "failed to send qmi m3 info req: %d\n", ret); ++ ath11k_warn(ab, "failed to send qmi host cap: %d\n", ret); ++ return ret; ++ } ++ ++ if (!ab->hw_params.fixed_fw_mem) ++ return ret; ++ ++ ret = ath11k_qmi_event_load_bdf(qmi); ++ if (ret < 0) { ++ ath11k_warn(ab, "qmi failed to download BDF:%d\n", ret); + return ret; + } + +@@ -3104,8 +3114,18 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) + break; + case ATH11K_QMI_EVENT_FW_MEM_READY: + ret = ath11k_qmi_event_load_bdf(qmi); +- if (ret < 0) ++ if (ret < 0) { + set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); ++ break; ++ } ++ ++ ret = ath11k_qmi_wlanfw_m3_info_send(ab); ++ if (ret < 0) { ++ ath11k_warn(ab, ++ "failed to send qmi m3 info req: %d\n", ret); ++ set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); ++ } ++ + break; + case ATH11K_QMI_EVENT_FW_READY: + clear_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0244-ath11k-HAL-changes-to-support-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0244-ath11k-HAL-changes-to-support-WCN6750.patch new file mode 100644 index 00000000000000..5dd2519cbd6890 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0244-ath11k-HAL-changes-to-support-WCN6750.patch @@ -0,0 +1,409 @@ +From 49890d9c93d5abf21babda2b495c7d3014fb9c98 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:35:00 +0530 +Subject: [PATCH] ath11k: HAL changes to support WCN6750 + +Add HAL changes required to support WCN6750. Offsets of some registers +for WCN6750 are different from other supported devices; move such +register offsets to platform specific ath11k_hw_regs. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-8-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 2 + + drivers/net/wireless/ath/ath11k/hal.c | 15 +-- + drivers/net/wireless/ath/ath11k/hal.h | 15 +-- + drivers/net/wireless/ath/ath11k/hw.c | 134 +++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 10 ++ + 5 files changed, 163 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 64cf87fd6a7f..7385e1c60ca1 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -491,8 +491,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + }, + .max_radios = 1, + .bdf_addr = 0x4B0C0000, ++ .hw_ops = &wcn6750_ops, + .ring_mask = &ath11k_hw_ring_mask_qca6390, + .internal_sleep_clock = false, ++ .regs = &wcn6750_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750, + .host_ce_config = ath11k_host_ce_config_qca6390, + .ce_count = 9, +diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c +index 2ec09ae90080..1dba7b9e0bda 100644 +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + #include + #include "hal_tx.h" +@@ -1082,10 +1083,10 @@ static void ath11k_hal_srng_update_hp_tp_addr(struct ath11k_base *ab, + srng = &hal->srng_list[ring_id]; + + if (srng_config->ring_dir == HAL_SRNG_DIR_DST) +- srng->u.dst_ring.tp_addr = (u32 *)(HAL_SHADOW_REG(shadow_cfg_idx) + ++ srng->u.dst_ring.tp_addr = (u32 *)(HAL_SHADOW_REG(ab, shadow_cfg_idx) + + (unsigned long)ab->mem); + else +- srng->u.src_ring.hp_addr = (u32 *)(HAL_SHADOW_REG(shadow_cfg_idx) + ++ srng->u.src_ring.hp_addr = (u32 *)(HAL_SHADOW_REG(ab, shadow_cfg_idx) + + (unsigned long)ab->mem); + } + +@@ -1120,7 +1121,7 @@ int ath11k_hal_srng_update_shadow_config(struct ath11k_base *ab, + ath11k_dbg(ab, ATH11k_DBG_HAL, + "target_reg %x, shadow reg 0x%x shadow_idx 0x%x, ring_type %d, ring num %d", + target_reg, +- HAL_SHADOW_REG(shadow_cfg_idx), ++ HAL_SHADOW_REG(ab, shadow_cfg_idx), + shadow_cfg_idx, + ring_type, ring_num); + +@@ -1193,12 +1194,12 @@ static int ath11k_hal_srng_create_config(struct ath11k_base *ab) + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_HP(ab); + + s = &hal->srng_config[HAL_REO_REINJECT]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB; +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP; ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB(ab); ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP(ab); + + s = &hal->srng_config[HAL_REO_CMD]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB; +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP; ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB(ab); ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP(ab); + + s = &hal->srng_config[HAL_REO_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_RING_BASE_LSB(ab); +diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h +index a7d9b4c551ad..1aadb1566df8 100644 +--- a/drivers/net/wireless/ath/ath11k/hal.h ++++ b/drivers/net/wireless/ath/ath11k/hal.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #ifndef ATH11K_HAL_H +@@ -31,12 +32,12 @@ struct ath11k_base; + #define HAL_DSCP_TID_TBL_SIZE 24 + + /* calculate the register address from bar0 of shadow register x */ +-#define HAL_SHADOW_BASE_ADDR 0x000008fc ++#define HAL_SHADOW_BASE_ADDR(ab) ab->hw_params.regs->hal_shadow_base_addr + #define HAL_SHADOW_NUM_REGS 36 + #define HAL_HP_OFFSET_IN_REG_START 1 + #define HAL_OFFSET_FROM_HP_TO_TP 4 + +-#define HAL_SHADOW_REG(x) (HAL_SHADOW_BASE_ADDR + (4 * (x))) ++#define HAL_SHADOW_REG(ab, x) (HAL_SHADOW_BASE_ADDR(ab) + (4 * (x))) + + /* WCSS Relative address */ + #define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000 +@@ -180,16 +181,18 @@ struct ath11k_base; + #define HAL_REO_TCL_RING_HP(ab) ab->hw_params.regs->hal_reo_tcl_ring_hp + + /* REO CMD R0 address */ +-#define HAL_REO_CMD_RING_BASE_LSB 0x00000194 ++#define HAL_REO_CMD_RING_BASE_LSB(ab) \ ++ ab->hw_params.regs->hal_reo_cmd_ring_base_lsb + + /* REO CMD R2 address */ +-#define HAL_REO_CMD_HP 0x00003020 ++#define HAL_REO_CMD_HP(ab) ab->hw_params.regs->hal_reo_cmd_ring_hp + + /* SW2REO R0 address */ +-#define HAL_SW2REO_RING_BASE_LSB 0x000001ec ++#define HAL_SW2REO_RING_BASE_LSB(ab) \ ++ ab->hw_params.regs->hal_sw2reo_ring_base_lsb + + /* SW2REO R2 address */ +-#define HAL_SW2REO_RING_HP 0x00003028 ++#define HAL_SW2REO_RING_HP(ab) ab->hw_params.regs->hal_sw2reo_ring_hp + + /* CE ring R0 address */ + #define HAL_CE_DST_RING_BASE_LSB 0x00000000 +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 46449a18b5cb..a9f5c4e63d62 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -1014,6 +1015,13 @@ const struct ath11k_hw_ops wcn6855_ops = { + .rx_desc_mpdu_start_addr2 = ath11k_hw_wcn6855_rx_desc_mpdu_start_addr2, + }; + ++const struct ath11k_hw_ops wcn6750_ops = { ++ .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id, ++ .wmi_init_config = ath11k_init_wmi_config_qca6390, ++ .mac_id_to_pdev_id = ath11k_hw_mac_id_to_pdev_id_qca6390, ++ .mac_id_to_srng_id = ath11k_hw_mac_id_to_srng_id_qca6390, ++}; ++ + #define ATH11K_TX_RING_MASK_0 0x1 + #define ATH11K_TX_RING_MASK_1 0x2 + #define ATH11K_TX_RING_MASK_2 0x4 +@@ -1908,10 +1916,18 @@ const struct ath11k_hw_regs ipq8074_regs = { + .hal_reo_tcl_ring_base_lsb = 0x000003fc, + .hal_reo_tcl_ring_hp = 0x00003058, + ++ /* REO CMD ring address */ ++ .hal_reo_cmd_ring_base_lsb = 0x00000194, ++ .hal_reo_cmd_ring_hp = 0x00003020, ++ + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x00000504, + .hal_reo_status_hp = 0x00003070, + ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x000001ec, ++ .hal_sw2reo_ring_hp = 0x00003028, ++ + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x00a00000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x00a01000, +@@ -1932,6 +1948,9 @@ const struct ath11k_hw_regs ipq8074_regs = { + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x0, + .pcie_pcs_osc_dtct_config_base = 0x0, ++ ++ /* Shadow register area */ ++ .hal_shadow_base_addr = 0x0, + }; + + const struct ath11k_hw_regs qca6390_regs = { +@@ -1979,10 +1998,18 @@ const struct ath11k_hw_regs qca6390_regs = { + .hal_reo_tcl_ring_base_lsb = 0x000003a4, + .hal_reo_tcl_ring_hp = 0x00003050, + ++ /* REO CMD ring address */ ++ .hal_reo_cmd_ring_base_lsb = 0x00000194, ++ .hal_reo_cmd_ring_hp = 0x00003020, ++ + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x000004ac, + .hal_reo_status_hp = 0x00003068, + ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x000001ec, ++ .hal_sw2reo_ring_hp = 0x00003028, ++ + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x00a00000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x00a01000, +@@ -2003,6 +2030,9 @@ const struct ath11k_hw_regs qca6390_regs = { + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x01e0c0ac, + .pcie_pcs_osc_dtct_config_base = 0x01e0c628, ++ ++ /* Shadow register area */ ++ .hal_shadow_base_addr = 0x000008fc, + }; + + const struct ath11k_hw_regs qcn9074_regs = { +@@ -2050,10 +2080,18 @@ const struct ath11k_hw_regs qcn9074_regs = { + .hal_reo_tcl_ring_base_lsb = 0x000003fc, + .hal_reo_tcl_ring_hp = 0x00003058, + ++ /* REO CMD ring address */ ++ .hal_reo_cmd_ring_base_lsb = 0x00000194, ++ .hal_reo_cmd_ring_hp = 0x00003020, ++ + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x00000504, + .hal_reo_status_hp = 0x00003070, + ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x000001ec, ++ .hal_sw2reo_ring_hp = 0x00003028, ++ + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x01b80000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000, +@@ -2074,6 +2112,9 @@ const struct ath11k_hw_regs qcn9074_regs = { + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x01e0e0a8, + .pcie_pcs_osc_dtct_config_base = 0x01e0f45c, ++ ++ /* Shadow register area */ ++ .hal_shadow_base_addr = 0x0, + }; + + const struct ath11k_hw_regs wcn6855_regs = { +@@ -2121,10 +2162,18 @@ const struct ath11k_hw_regs wcn6855_regs = { + .hal_reo_tcl_ring_base_lsb = 0x00000454, + .hal_reo_tcl_ring_hp = 0x00003060, + ++ /* REO CMD ring address */ ++ .hal_reo_cmd_ring_base_lsb = 0x00000194, ++ .hal_reo_cmd_ring_hp = 0x00003020, ++ + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x0000055c, + .hal_reo_status_hp = 0x00003078, + ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x000001ec, ++ .hal_sw2reo_ring_hp = 0x00003028, ++ + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x1b80000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x1b81000, +@@ -2145,6 +2194,91 @@ const struct ath11k_hw_regs wcn6855_regs = { + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x01e0c0ac, + .pcie_pcs_osc_dtct_config_base = 0x01e0c628, ++ ++ /* Shadow register area */ ++ .hal_shadow_base_addr = 0x000008fc, ++}; ++ ++const struct ath11k_hw_regs wcn6750_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, ++ ++ /* 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, ++ ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x0000013c, ++ .hal_sw2reo_ring_hp = 0x00003018, ++ ++ /* 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, ++ ++ /* PCIe base address */ ++ .pcie_qserdes_sysclk_en_sel = 0x0, ++ .pcie_pcs_osc_dtct_config_base = 0x0, ++ ++ /* Shadow register area */ ++ .hal_shadow_base_addr = 0x00000504, + }; + + const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = { +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index b5a4758a6bc5..6d588cd80093 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -253,6 +253,7 @@ extern const struct ath11k_hw_ops ipq6018_ops; + extern const struct ath11k_hw_ops qca6390_ops; + extern const struct ath11k_hw_ops qcn9074_ops; + extern const struct ath11k_hw_ops wcn6855_ops; ++extern const struct ath11k_hw_ops wcn6750_ops; + + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390; +@@ -355,6 +356,12 @@ struct ath11k_hw_regs { + u32 hal_reo_status_ring_base_lsb; + u32 hal_reo_status_hp; + ++ u32 hal_reo_cmd_ring_base_lsb; ++ u32 hal_reo_cmd_ring_hp; ++ ++ u32 hal_sw2reo_ring_base_lsb; ++ u32 hal_sw2reo_ring_hp; ++ + u32 hal_seq_wcss_umac_ce0_src_reg; + u32 hal_seq_wcss_umac_ce0_dst_reg; + u32 hal_seq_wcss_umac_ce1_src_reg; +@@ -370,12 +377,15 @@ struct ath11k_hw_regs { + + u32 pcie_qserdes_sysclk_en_sel; + u32 pcie_pcs_osc_dtct_config_base; ++ ++ u32 hal_shadow_base_addr; + }; + + extern const struct ath11k_hw_regs ipq8074_regs; + extern const struct ath11k_hw_regs qca6390_regs; + extern const struct ath11k_hw_regs qcn9074_regs; + extern const struct ath11k_hw_regs wcn6855_regs; ++extern const struct ath11k_hw_regs wcn6750_regs; + + static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type) + { +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0245-ath11k-Datapath-changes-to-support-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0245-ath11k-Datapath-changes-to-support-WCN6750.patch new file mode 100644 index 00000000000000..6afe1bcd389150 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0245-ath11k-Datapath-changes-to-support-WCN6750.patch @@ -0,0 +1,85 @@ +From e67ba19739177f95706c1d4a53c884c89973b843 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:35:01 +0530 +Subject: [PATCH] ath11k: Datapath changes to support WCN6750 + +HAL RX descriptor for WCN6750 is same as QCN9074, this +means that the size of the HAL RX decriptor and the DP +APIs that WCN6750 requires to enable datapath should be +initialized with that of QCN9074's RX descriptor size +and the DP APIs respectively. There is one change wrt to +REO configuration though, REO configuration for WCN6750 +follows WCN6855, therefore use reo_setup() of WCN6855 +for WCN6750. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-9-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/hw.c | 32 ++++++++++++++++++++++++++ + 2 files changed, 33 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 7385e1c60ca1..01e1d494b527 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -531,6 +531,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = false, ++ .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index a9f5c4e63d62..09ce357f0f0d 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -1020,6 +1020,38 @@ const struct ath11k_hw_ops wcn6750_ops = { + .wmi_init_config = ath11k_init_wmi_config_qca6390, + .mac_id_to_pdev_id = ath11k_hw_mac_id_to_pdev_id_qca6390, + .mac_id_to_srng_id = ath11k_hw_mac_id_to_srng_id_qca6390, ++ .tx_mesh_enable = ath11k_hw_qcn9074_tx_mesh_enable, ++ .rx_desc_get_first_msdu = ath11k_hw_qcn9074_rx_desc_get_first_msdu, ++ .rx_desc_get_last_msdu = ath11k_hw_qcn9074_rx_desc_get_last_msdu, ++ .rx_desc_get_l3_pad_bytes = ath11k_hw_qcn9074_rx_desc_get_l3_pad_bytes, ++ .rx_desc_get_hdr_status = ath11k_hw_qcn9074_rx_desc_get_hdr_status, ++ .rx_desc_encrypt_valid = ath11k_hw_qcn9074_rx_desc_encrypt_valid, ++ .rx_desc_get_encrypt_type = ath11k_hw_qcn9074_rx_desc_get_encrypt_type, ++ .rx_desc_get_decap_type = ath11k_hw_qcn9074_rx_desc_get_decap_type, ++ .rx_desc_get_mesh_ctl = ath11k_hw_qcn9074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ldpc_support = ath11k_hw_qcn9074_rx_desc_get_ldpc_support, ++ .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, ++ .rx_desc_get_mpdu_fc_valid = ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, ++ .rx_desc_get_mpdu_start_seq_no = ath11k_hw_qcn9074_rx_desc_get_mpdu_start_seq_no, ++ .rx_desc_get_msdu_len = ath11k_hw_qcn9074_rx_desc_get_msdu_len, ++ .rx_desc_get_msdu_sgi = ath11k_hw_qcn9074_rx_desc_get_msdu_sgi, ++ .rx_desc_get_msdu_rate_mcs = ath11k_hw_qcn9074_rx_desc_get_msdu_rate_mcs, ++ .rx_desc_get_msdu_rx_bw = ath11k_hw_qcn9074_rx_desc_get_msdu_rx_bw, ++ .rx_desc_get_msdu_freq = ath11k_hw_qcn9074_rx_desc_get_msdu_freq, ++ .rx_desc_get_msdu_pkt_type = ath11k_hw_qcn9074_rx_desc_get_msdu_pkt_type, ++ .rx_desc_get_msdu_nss = ath11k_hw_qcn9074_rx_desc_get_msdu_nss, ++ .rx_desc_get_mpdu_tid = ath11k_hw_qcn9074_rx_desc_get_mpdu_tid, ++ .rx_desc_get_mpdu_peer_id = ath11k_hw_qcn9074_rx_desc_get_mpdu_peer_id, ++ .rx_desc_copy_attn_end_tlv = ath11k_hw_qcn9074_rx_desc_copy_attn_end, ++ .rx_desc_get_mpdu_start_tag = ath11k_hw_qcn9074_rx_desc_get_mpdu_start_tag, ++ .rx_desc_get_mpdu_ppdu_id = ath11k_hw_qcn9074_rx_desc_get_mpdu_ppdu_id, ++ .rx_desc_set_msdu_len = ath11k_hw_qcn9074_rx_desc_set_msdu_len, ++ .rx_desc_get_attention = ath11k_hw_qcn9074_rx_desc_get_attention, ++ .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, ++ .reo_setup = ath11k_hw_wcn6855_reo_setup, ++ .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, ++ .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, + }; + + #define ATH11K_TX_RING_MASK_0 0x1 +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0246-ath11k-Add-support-for-WCN6750-device.patch b/package/kernel/mac80211/patches/ath11k/0246-ath11k-Add-support-for-WCN6750-device.patch new file mode 100644 index 00000000000000..ad3e361a0446c1 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0246-ath11k-Add-support-for-WCN6750-device.patch @@ -0,0 +1,432 @@ +From 00402f49d26ffe991892bba76ccbdfed26538824 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 29 Apr 2022 22:35:02 +0530 +Subject: [PATCH] ath11k: Add support for WCN6750 device + +WCN6750 is non-DBS 2x2 11AX chipset. Unlike QCA6390 which is a +DBS (dual band simultaneous) solution (2 LMACs), WCN6750 has a +single LMAC supporting 2G, 5G and 6G bands but will operate only +on one band at any given point. + +WCN6750 is a PCIe based solution, but it is attached to the WPSS +(Wireless Processor SubSystem) Q6 processor, hence it is enumerated +by the Q6 processor. It is registered to the APSS processor +(Application Processor SubSystem) as a platform device(AHB) and +remoteproc APIs are used to boot up or shutdown the device like +other AHB devices. + +Also, Device information like BAR and it's size is not known to the +APSS processor as the chip is enumerated by WPSS Q6. These details +are fetched over QMI. + +STA and AP modes are supported. Verified basic connectivity and ping +in both the modes. + +An important point to note is that though WCN6750 is a PCIe device, +it cannot be attached to any other platform except on Qualcomm +Snapdragon SoCs due to the aforementioned reasons. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220429170502.20080-10-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/Makefile | 5 +- + drivers/net/wireless/ath/ath11k/ahb.c | 142 +++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/pcic.c | 23 ++++ + 4 files changed, 158 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile +index 0ebfe41d6143..cc47e0114595 100644 +--- a/drivers/net/wireless/ath/ath11k/Makefile ++++ b/drivers/net/wireless/ath/ath11k/Makefile +@@ -16,7 +16,8 @@ ath11k-y += core.o \ + ce.o \ + peer.o \ + dbring.o \ +- hw.o ++ hw.o \ ++ pcic.o + + ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o + ath11k-$(CPTCFG_NL80211_TESTMODE) += testmode.o +@@ -29,7 +30,7 @@ obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o + ath11k_ahb-y += ahb.o + + obj-$(CPTCFG_ATH11K_PCI) += ath11k_pci.o +-ath11k_pci-y += mhi.o pci.o pcic.o ++ath11k_pci-y += mhi.o pci.o + + # for tracing framework to find trace.h + CFLAGS_trace.o := -I$(src) +diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c +index 49d79cfbf21c..050bda828966 100644 +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -13,6 +13,7 @@ + #include "debug.h" + #include "hif.h" + #include ++#include "pcic.h" + + static const struct of_device_id ath11k_ahb_of_match[] = { + /* TODO: Should we change the compatible string to something similar +@@ -24,6 +25,9 @@ static const struct of_device_id ath11k_ahb_of_match[] = { + { .compatible = "qcom,ipq6018-wifi", + .data = (void *)ATH11K_HW_IPQ6018_HW10, + }, ++ { .compatible = "qcom,wcn6750-wifi", ++ .data = (void *)ATH11K_HW_WCN6750_HW10, ++ }, + { } + }; + +@@ -128,6 +132,16 @@ enum ext_irq_num { + tcl2host_status_ring, + }; + ++static int ++ath11k_ahb_get_msi_irq_wcn6750(struct ath11k_base *ab, unsigned int vector) ++{ ++ return ab->pci.msi.irqs[vector]; ++} ++ ++static const struct ath11k_pci_ops ath11k_ahb_pci_ops_wcn6750 = { ++ .get_msi_irq = ath11k_ahb_get_msi_irq_wcn6750, ++}; ++ + static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset) + { + return ioread32(ab->mem + offset); +@@ -395,6 +409,9 @@ static void ath11k_ahb_free_irq(struct ath11k_base *ab) + int irq_idx; + int i; + ++ if (ab->hw_params.hybrid_bus_type) ++ return ath11k_pcic_free_irq(ab); ++ + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; +@@ -549,6 +566,9 @@ static int ath11k_ahb_config_irq(struct ath11k_base *ab) + int irq, irq_idx, i; + int ret; + ++ if (ab->hw_params.hybrid_bus_type) ++ return ath11k_pcic_config_irq(ab); ++ + /* Configure CE irqs */ + for (i = 0; i < ab->hw_params.ce_count; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; +@@ -618,7 +638,7 @@ static int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id + return 0; + } + +-static const struct ath11k_hif_ops ath11k_ahb_hif_ops = { ++static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = { + .start = ath11k_ahb_start, + .stop = ath11k_ahb_stop, + .read32 = ath11k_ahb_read32, +@@ -630,6 +650,20 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops = { + .power_up = ath11k_ahb_power_up, + }; + ++static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = { ++ .start = ath11k_pcic_start, ++ .stop = ath11k_pcic_stop, ++ .read32 = ath11k_pcic_read32, ++ .write32 = ath11k_pcic_write32, ++ .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, ++}; ++ + static int ath11k_core_get_rproc(struct ath11k_base *ab) + { + struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); +@@ -652,12 +686,84 @@ static int ath11k_core_get_rproc(struct ath11k_base *ab) + return 0; + } + ++static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab) ++{ ++ struct platform_device *pdev = ab->pdev; ++ phys_addr_t msi_addr_pa; ++ dma_addr_t msi_addr_iova; ++ struct resource *res; ++ int int_prop; ++ int ret; ++ int i; ++ ++ ret = ath11k_pcic_init_msi_config(ab); ++ if (ret) { ++ ath11k_err(ab, "failed to init msi config: %d\n", ret); ++ return ret; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ ath11k_err(ab, "failed to fetch msi_addr\n"); ++ return -ENOENT; ++ } ++ ++ msi_addr_pa = res->start; ++ msi_addr_iova = dma_map_resource(ab->dev, msi_addr_pa, PAGE_SIZE, ++ DMA_FROM_DEVICE, 0); ++ if (dma_mapping_error(ab->dev, msi_addr_iova)) ++ return -ENOMEM; ++ ++ ab->pci.msi.addr_lo = lower_32_bits(msi_addr_iova); ++ 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) ++ return ret; ++ ++ ab->pci.msi.ep_base_data = int_prop + 32; ++ ++ for (i = 0; i < ab->pci.msi.config->total_vectors; i++) { ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, i); ++ if (!res) ++ return -ENODEV; ++ ++ ab->pci.msi.irqs[i] = res->start; ++ } ++ ++ set_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags); ++ ++ return 0; ++} ++ ++static int ath11k_ahb_setup_resources(struct ath11k_base *ab) ++{ ++ struct platform_device *pdev = ab->pdev; ++ struct resource *mem_res; ++ void __iomem *mem; ++ ++ if (ab->hw_params.hybrid_bus_type) ++ return ath11k_ahb_setup_msi_resources(ab); ++ ++ mem = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res); ++ if (IS_ERR(mem)) { ++ dev_err(&pdev->dev, "ioremap error\n"); ++ return PTR_ERR(mem); ++ } ++ ++ ab->mem = mem; ++ ab->mem_len = resource_size(mem_res); ++ ++ return 0; ++} ++ + static int ath11k_ahb_probe(struct platform_device *pdev) + { + struct ath11k_base *ab; + const struct of_device_id *of_id; +- struct resource *mem_res; +- void __iomem *mem; ++ const struct ath11k_hif_ops *hif_ops; ++ const struct ath11k_pci_ops *pci_ops; ++ enum ath11k_hw_rev hw_rev; + int ret; + + of_id = of_match_device(ath11k_ahb_of_match, &pdev->dev); +@@ -666,10 +772,21 @@ static int ath11k_ahb_probe(struct platform_device *pdev) + return -EINVAL; + } + +- mem = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res); +- if (IS_ERR(mem)) { +- dev_err(&pdev->dev, "ioremap error\n"); +- return PTR_ERR(mem); ++ hw_rev = (enum ath11k_hw_rev)of_id->data; ++ ++ switch (hw_rev) { ++ case ATH11K_HW_IPQ8074: ++ case ATH11K_HW_IPQ6018_HW10: ++ hif_ops = &ath11k_ahb_hif_ops_ipq8074; ++ pci_ops = NULL; ++ break; ++ case ATH11K_HW_WCN6750_HW10: ++ hif_ops = &ath11k_ahb_hif_ops_wcn6750; ++ pci_ops = &ath11k_ahb_pci_ops_wcn6750; ++ break; ++ default: ++ dev_err(&pdev->dev, "unsupported device type %d\n", hw_rev); ++ return -EOPNOTSUPP; + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); +@@ -685,13 +802,16 @@ static int ath11k_ahb_probe(struct platform_device *pdev) + return -ENOMEM; + } + +- ab->hif.ops = &ath11k_ahb_hif_ops; ++ ab->hif.ops = hif_ops; ++ ab->pci.ops = pci_ops; + ab->pdev = pdev; +- ab->hw_rev = (enum ath11k_hw_rev)of_id->data; +- ab->mem = mem; +- ab->mem_len = resource_size(mem_res); ++ ab->hw_rev = hw_rev; + platform_set_drvdata(pdev, ab); + ++ ret = ath11k_ahb_setup_resources(ab); ++ if (ret) ++ goto err_core_free; ++ + ret = ath11k_core_pre_init(ab); + if (ret) + goto err_core_free; +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index fdea44627125..95bca0b078b1 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -950,6 +950,7 @@ struct ath11k_base { + struct { + const struct ath11k_msi_config *config; + u32 ep_base_data; ++ u32 irqs[32]; + u32 addr_lo; + u32 addr_hi; + } msi; +diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c +index 46cf96d3e1d2..cf12b98c480d 100644 +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -106,6 +106,15 @@ static const struct ath11k_msi_config ath11k_msi_config[] = { + }, + .hw_rev = ATH11K_HW_WCN6855_HW21, + }, ++ { ++ .total_vectors = 28, ++ .total_users = 2, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "CE", .num_vectors = 10, .base_vector = 0 }, ++ { .name = "DP", .num_vectors = 18, .base_vector = 10 }, ++ }, ++ .hw_rev = ATH11K_HW_WCN6750_HW10, ++ }, + }; + + int ath11k_pcic_init_msi_config(struct ath11k_base *ab) +@@ -172,6 +181,7 @@ void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value) + !ret) + ab->pci.ops->release(ab); + } ++EXPORT_SYMBOL(ath11k_pcic_write32); + + u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) + { +@@ -203,6 +213,7 @@ u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) + + return val; + } ++EXPORT_SYMBOL(ath11k_pcic_read32); + + void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, + u32 *msi_addr_hi) +@@ -210,6 +221,7 @@ void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, + *msi_addr_lo = ab->pci.msi.addr_lo; + *msi_addr_hi = ab->pci.msi.addr_hi; + } ++EXPORT_SYMBOL(ath11k_pcic_get_msi_address); + + int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, + int *num_vectors, u32 *user_base_data, +@@ -237,6 +249,7 @@ int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, + + return -EINVAL; + } ++EXPORT_SYMBOL(ath11k_pcic_get_user_msi_assignment); + + void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx) + { +@@ -253,6 +266,7 @@ void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx) + } + *msi_idx = msi_data_idx; + } ++EXPORT_SYMBOL(ath11k_pcic_get_ce_msi_idx); + + static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab) + { +@@ -281,6 +295,7 @@ void ath11k_pcic_free_irq(struct ath11k_base *ab) + + ath11k_pcic_free_ext_irq(ab); + } ++EXPORT_SYMBOL(ath11k_pcic_free_irq); + + static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) + { +@@ -431,6 +446,7 @@ void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab) + ath11k_pcic_ext_grp_enable(irq_grp); + } + } ++EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable); + + static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab) + { +@@ -451,6 +467,7 @@ void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab) + __ath11k_pcic_ext_irq_disable(ab); + ath11k_pcic_sync_ext_irqs(ab); + } ++EXPORT_SYMBOL(ath11k_pcic_ext_irq_disable); + + static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget) + { +@@ -630,6 +647,7 @@ int ath11k_pcic_config_irq(struct ath11k_base *ab) + + return 0; + } ++EXPORT_SYMBOL(ath11k_pcic_config_irq); + + void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab) + { +@@ -643,6 +661,7 @@ void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab) + ath11k_pcic_ce_irq_enable(ab, i); + } + } ++EXPORT_SYMBOL(ath11k_pcic_ce_irqs_enable); + + static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab) + { +@@ -664,12 +683,14 @@ void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab) + ath11k_pcic_sync_ce_irqs(ab); + ath11k_pcic_kill_tasklets(ab); + } ++EXPORT_SYMBOL(ath11k_pcic_ce_irq_disable_sync); + + void ath11k_pcic_stop(struct ath11k_base *ab) + { + ath11k_pcic_ce_irq_disable_sync(ab); + ath11k_ce_cleanup_pipes(ab); + } ++EXPORT_SYMBOL(ath11k_pcic_stop); + + int ath11k_pcic_start(struct ath11k_base *ab) + { +@@ -680,6 +701,7 @@ int ath11k_pcic_start(struct ath11k_base *ab) + + return 0; + } ++EXPORT_SYMBOL(ath11k_pcic_start); + + int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) +@@ -723,3 +745,4 @@ int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + + return 0; + } ++EXPORT_SYMBOL(ath11k_pcic_map_service_to_pipe); +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0247-ath11k-Add-support-for-targets-without-trustzone.patch b/package/kernel/mac80211/patches/ath11k/0247-ath11k-Add-support-for-targets-without-trustzone.patch new file mode 100644 index 00000000000000..1dc7a622d522d8 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0247-ath11k-Add-support-for-targets-without-trustzone.patch @@ -0,0 +1,264 @@ +From f9eec4947add999e1251bf14365a48a655b786a4 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Wed, 4 May 2022 07:34:15 +0300 +Subject: [PATCH] ath11k: Add support for targets without trustzone + +Add the support to attach WCN6750 and map iommu domain +for targets which do not have the support of TrustZone. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00573-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220328062032.28881-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ahb.c | 178 +++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/ahb.h | 9 ++ + 2 files changed, 186 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c +index 050bda828966..fa11807f48a9 100644 +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -9,6 +9,8 @@ + #include + #include + #include ++#include ++#include + #include "ahb.h" + #include "debug.h" + #include "hif.h" +@@ -757,6 +759,172 @@ static int ath11k_ahb_setup_resources(struct ath11k_base *ab) + return 0; + } + ++static int ath11k_ahb_setup_msa_resources(struct ath11k_base *ab) ++{ ++ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); ++ struct device *dev = ab->dev; ++ struct device_node *node; ++ struct resource r; ++ int ret; ++ ++ node = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (!node) ++ return -ENOENT; ++ ++ ret = of_address_to_resource(node, 0, &r); ++ of_node_put(node); ++ if (ret) { ++ dev_err(dev, "failed to resolve msa fixed region\n"); ++ return ret; ++ } ++ ++ ab_ahb->fw.msa_paddr = r.start; ++ ab_ahb->fw.msa_size = resource_size(&r); ++ ++ node = of_parse_phandle(dev->of_node, "memory-region", 1); ++ if (!node) ++ return -ENOENT; ++ ++ ret = of_address_to_resource(node, 0, &r); ++ of_node_put(node); ++ if (ret) { ++ dev_err(dev, "failed to resolve ce fixed region\n"); ++ return ret; ++ } ++ ++ ab_ahb->fw.ce_paddr = r.start; ++ ab_ahb->fw.ce_size = resource_size(&r); ++ ++ return 0; ++} ++ ++static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) ++{ ++ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); ++ struct device *host_dev = ab->dev; ++ struct platform_device_info info = {0}; ++ struct iommu_domain *iommu_dom; ++ struct platform_device *pdev; ++ struct device_node *node; ++ int ret; ++ ++ /* Chipsets not requiring MSA need not initialize ++ * MSA resources, return success in such cases. ++ */ ++ if (!ab->hw_params.fixed_fw_mem) ++ return 0; ++ ++ ret = ath11k_ahb_setup_msa_resources(ab); ++ if (ret) { ++ ath11k_err(ab, "failed to setup msa resources\n"); ++ return ret; ++ } ++ ++ node = of_get_child_by_name(host_dev->of_node, "wifi-firmware"); ++ if (!node) { ++ ab_ahb->fw.use_tz = true; ++ return 0; ++ } ++ ++ info.fwnode = &node->fwnode; ++ info.parent = host_dev; ++ info.name = node->name; ++ info.dma_mask = DMA_BIT_MASK(32); ++ ++ pdev = platform_device_register_full(&info); ++ if (IS_ERR(pdev)) { ++ of_node_put(node); ++ return PTR_ERR(pdev); ++ } ++ ++ ret = of_dma_configure(&pdev->dev, node, true); ++ if (ret) { ++ ath11k_err(ab, "dma configure fail: %d\n", ret); ++ goto err_unregister; ++ } ++ ++ ab_ahb->fw.dev = &pdev->dev; ++ ++ iommu_dom = iommu_domain_alloc(&platform_bus_type); ++ if (!iommu_dom) { ++ ath11k_err(ab, "failed to allocate iommu domain\n"); ++ ret = -ENOMEM; ++ goto err_unregister; ++ } ++ ++ ret = iommu_attach_device(iommu_dom, ab_ahb->fw.dev); ++ if (ret) { ++ ath11k_err(ab, "could not attach device: %d\n", ret); ++ goto err_iommu_free; ++ } ++ ++ ret = iommu_map(iommu_dom, ab_ahb->fw.msa_paddr, ++ ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size, ++ IOMMU_READ | IOMMU_WRITE); ++ if (ret) { ++ ath11k_err(ab, "failed to map firmware region: %d\n", ret); ++ goto err_iommu_detach; ++ } ++ ++ ret = iommu_map(iommu_dom, ab_ahb->fw.ce_paddr, ++ ab_ahb->fw.ce_paddr, ab_ahb->fw.ce_size, ++ IOMMU_READ | IOMMU_WRITE); ++ if (ret) { ++ ath11k_err(ab, "failed to map firmware CE region: %d\n", ret); ++ goto err_iommu_unmap; ++ } ++ ++ ab_ahb->fw.use_tz = false; ++ ab_ahb->fw.iommu_domain = iommu_dom; ++ of_node_put(node); ++ ++ return 0; ++ ++err_iommu_unmap: ++ iommu_unmap(iommu_dom, ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size); ++ ++err_iommu_detach: ++ iommu_detach_device(iommu_dom, ab_ahb->fw.dev); ++ ++err_iommu_free: ++ iommu_domain_free(iommu_dom); ++ ++err_unregister: ++ platform_device_unregister(pdev); ++ of_node_put(node); ++ ++ return ret; ++} ++ ++static int ath11k_ahb_fw_resource_deinit(struct ath11k_base *ab) ++{ ++ struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); ++ struct iommu_domain *iommu; ++ size_t unmapped_size; ++ ++ if (ab_ahb->fw.use_tz) ++ return 0; ++ ++ iommu = ab_ahb->fw.iommu_domain; ++ ++ unmapped_size = iommu_unmap(iommu, ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size); ++ if (unmapped_size != ab_ahb->fw.msa_size) ++ ath11k_err(ab, "failed to unmap firmware: %zu\n", ++ unmapped_size); ++ ++ unmapped_size = iommu_unmap(iommu, ab_ahb->fw.ce_paddr, ab_ahb->fw.ce_size); ++ if (unmapped_size != ab_ahb->fw.ce_size) ++ ath11k_err(ab, "failed to unmap firmware CE memory: %zu\n", ++ unmapped_size); ++ ++ iommu_detach_device(iommu, ab_ahb->fw.dev); ++ iommu_domain_free(iommu); ++ ++ platform_device_unregister(to_platform_device(ab_ahb->fw.dev)); ++ ++ return 0; ++} ++ + static int ath11k_ahb_probe(struct platform_device *pdev) + { + struct ath11k_base *ab; +@@ -816,10 +984,14 @@ static int ath11k_ahb_probe(struct platform_device *pdev) + if (ret) + goto err_core_free; + +- ret = ath11k_hal_srng_init(ab); ++ ret = ath11k_ahb_fw_resources_init(ab); + if (ret) + goto err_core_free; + ++ ret = ath11k_hal_srng_init(ab); ++ if (ret) ++ goto err_fw_deinit; ++ + ret = ath11k_ce_alloc_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret); +@@ -856,6 +1028,9 @@ static int ath11k_ahb_probe(struct platform_device *pdev) + err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + ++err_fw_deinit: ++ ath11k_ahb_fw_resource_deinit(ab); ++ + err_core_free: + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); +@@ -891,6 +1066,7 @@ static int ath11k_ahb_remove(struct platform_device *pdev) + qmi_fail: + ath11k_ahb_free_irq(ab); + ath11k_hal_srng_deinit(ab); ++ ath11k_ahb_fw_resource_deinit(ab); + ath11k_ce_free_pipes(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 51e6e4a5f686..58a945411c5b 100644 +--- a/drivers/net/wireless/ath/ath11k/ahb.h ++++ b/drivers/net/wireless/ath/ath11k/ahb.h +@@ -12,6 +12,15 @@ struct ath11k_base; + + struct ath11k_ahb { + struct rproc *tgt_rproc; ++ struct { ++ struct device *dev; ++ struct iommu_domain *iommu_domain; ++ dma_addr_t msa_paddr; ++ u32 msa_size; ++ dma_addr_t ce_paddr; ++ u32 ce_size; ++ bool use_tz; ++ } fw; + }; + + static inline struct ath11k_ahb *ath11k_ahb_priv(struct ath11k_base *ab) +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0249-ath11k-Fix-RX-de-fragmentation-issue-on-WCN6750.patch b/package/kernel/mac80211/patches/ath11k/0249-ath11k-Fix-RX-de-fragmentation-issue-on-WCN6750.patch new file mode 100644 index 00000000000000..f98c4a9b957a09 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0249-ath11k-Fix-RX-de-fragmentation-issue-on-WCN6750.patch @@ -0,0 +1,125 @@ +From 22cc687326e049fe294da118d9a067538c0066cb Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Wed, 4 May 2022 14:09:00 +0530 +Subject: [PATCH] ath11k: Fix RX de-fragmentation issue on WCN6750 + +The offset of REO register where the RX fragment destination ring +is configured is different in WCN6750 as compared to WCN6855. +Due to this differnce in offsets, on WCN6750, fragment destination +ring will be configured incorrectly, leading to RX fragments not +getting delivered to the driver. Fix this by defining HW specific +offsets for the REO MISC CTL register. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00192-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220504083900.31513-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/hal.h | 2 +- + drivers/net/wireless/ath/ath11k/hw.c | 23 +++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 3 files changed, 23 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h +index 1aadb1566df8..110c337ddf33 100644 +--- a/drivers/net/wireless/ath/ath11k/hal.h ++++ b/drivers/net/wireless/ath/ath11k/hal.h +@@ -121,7 +121,7 @@ struct ath11k_base; + #define HAL_REO1_DEST_RING_CTRL_IX_1 0x00000008 + #define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c + #define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010 +-#define HAL_REO1_MISC_CTL 0x00000630 ++#define HAL_REO1_MISC_CTL(ab) ab->hw_params.regs->hal_reo1_misc_ctl + #define HAL_REO1_RING_BASE_LSB(ab) ab->hw_params.regs->hal_reo1_ring_base_lsb + #define HAL_REO1_RING_BASE_MSB(ab) ab->hw_params.regs->hal_reo1_ring_base_msb + #define HAL_REO1_RING_ID(ab) ab->hw_params.regs->hal_reo1_ring_id +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 09ce357f0f0d..96db85c55585 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -771,10 +771,10 @@ static void ath11k_hw_wcn6855_reo_setup(struct ath11k_base *ab) + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); + ath11k_hif_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val); + +- val = ath11k_hif_read32(ab, reo_base + HAL_REO1_MISC_CTL); ++ val = ath11k_hif_read32(ab, reo_base + HAL_REO1_MISC_CTL(ab)); + val &= ~HAL_REO1_MISC_CTL_FRAGMENT_DST_RING; + val |= FIELD_PREP(HAL_REO1_MISC_CTL_FRAGMENT_DST_RING, HAL_SRNG_RING_ID_REO2SW1); +- ath11k_hif_write32(ab, reo_base + HAL_REO1_MISC_CTL, val); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_MISC_CTL(ab), val); + + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0(ab), + HAL_DEFAULT_REO_TIMEOUT_USEC); +@@ -1983,6 +1983,9 @@ const struct ath11k_hw_regs ipq8074_regs = { + + /* Shadow register area */ + .hal_shadow_base_addr = 0x0, ++ ++ /* REO misc control register, not used in IPQ8074 */ ++ .hal_reo1_misc_ctl = 0x0, + }; + + const struct ath11k_hw_regs qca6390_regs = { +@@ -2065,6 +2068,9 @@ const struct ath11k_hw_regs qca6390_regs = { + + /* Shadow register area */ + .hal_shadow_base_addr = 0x000008fc, ++ ++ /* REO misc control register, not used in QCA6390 */ ++ .hal_reo1_misc_ctl = 0x0, + }; + + const struct ath11k_hw_regs qcn9074_regs = { +@@ -2147,6 +2153,9 @@ const struct ath11k_hw_regs qcn9074_regs = { + + /* Shadow register area */ + .hal_shadow_base_addr = 0x0, ++ ++ /* REO misc control register, not used in QCN9074 */ ++ .hal_reo1_misc_ctl = 0x0, + }; + + const struct ath11k_hw_regs wcn6855_regs = { +@@ -2229,6 +2238,11 @@ const struct ath11k_hw_regs wcn6855_regs = { + + /* Shadow register area */ + .hal_shadow_base_addr = 0x000008fc, ++ ++ /* REO misc control register, used for fragment ++ * destination ring config in WCN6855. ++ */ ++ .hal_reo1_misc_ctl = 0x00000630, + }; + + const struct ath11k_hw_regs wcn6750_regs = { +@@ -2311,6 +2325,11 @@ const struct ath11k_hw_regs wcn6750_regs = { + + /* Shadow register area */ + .hal_shadow_base_addr = 0x00000504, ++ ++ /* REO misc control register, used for fragment ++ * destination ring config in WCN6750. ++ */ ++ .hal_reo1_misc_ctl = 0x000005d8, + }; + + const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = { +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 6d588cd80093..3a2abde63489 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -379,6 +379,7 @@ struct ath11k_hw_regs { + u32 pcie_pcs_osc_dtct_config_base; + + u32 hal_shadow_base_addr; ++ u32 hal_reo1_misc_ctl; + }; + + extern const struct ath11k_hw_regs ipq8074_regs; +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0250-ath11k-reset-11d-state-in-process-of-recovery.patch b/package/kernel/mac80211/patches/ath11k/0250-ath11k-reset-11d-state-in-process-of-recovery.patch new file mode 100644 index 00000000000000..9b24b4f9cd9506 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0250-ath11k-reset-11d-state-in-process-of-recovery.patch @@ -0,0 +1,88 @@ +From 9497b7880ffd71fcbd469970f6ef45bb55877bfd Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 4 May 2022 23:46:36 -0400 +Subject: [PATCH] ath11k: reset 11d state in process of recovery + +When doing simulate_fw_crash operation periodically with a short interval +time such as 10 seconds, it is easy happened WMI command timed out for +WMI_SCAN_CHAN_LIST_CMDID in ath11k_reg_update_chan_list(). + +log: +[42287.610053] ath11k_pci 0000:01:00.0: wmi command 12291 timeout +[42287.610064] ath11k_pci 0000:01:00.0: failed to send WMI_SCAN_CHAN_LIST cmd +[42287.610073] ath11k_pci 0000:01:00.0: failed to perform regd update : -11 + +Note that this issue does not occur with a longer interval such as 20 seconds. + +The reason the issue occurs with a shorter interval is the following steps: +1) Upon initial boot, or after device recovery, the initial hw scan plus +the 11d scan will run, and when 6 GHz support is present, these scans +can take up to 12 seconds to complete, so ath11k_reg_update_chan_list() +is still waiting the completion of ar->completed_11d_scan. +2) If a simulate_fw_crash operation is received during this time, those +scans do not complete, and ath11k_core_pre_reconfigure_recovery() +complete the ar->completed_11d_scan, then ath11k_reg_update_chan_list() +wakeup and start to send WMI_SCAN_CHAN_LIST_CMDID, but firmware is crashed +at this moment, so wmi timed out occur. + +To address this issue, reset the 11d state during device recovery so that +WMI_SCAN_CHAN_LIST_CMDID does not timed out for short interval time such +as 10 seconds. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Fixes: 1f682dc9fb37 ("ath11k: reduce the wait time of 11d scan and hw scan while add interface") +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220505034636.29582-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 5 +++++ + drivers/net/wireless/ath/ath11k/reg.c | 3 +++ + 3 files changed, 9 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 01e1d494b527..236215aa3867 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1620,6 +1620,7 @@ static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) + + ieee80211_stop_queues(ar->hw); + ath11k_mac_drain_tx(ar); ++ ar->state_11d = ATH11K_11D_IDLE; + complete(&ar->completed_11d_scan); + complete(&ar->scan.started); + complete(&ar->scan.completed); +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index df15fa42d8ad..9b28fdac3c94 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6155,6 +6155,11 @@ void ath11k_mac_11d_scan_stop(struct ath11k *ar) + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac stop 11d vdev id %d\n", + ar->vdev_id_11d_scan); + ++ if (ar->state_11d == ATH11K_11D_PREPARING) { ++ ar->state_11d = ATH11K_11D_IDLE; ++ complete(&ar->completed_11d_scan); ++ } ++ + if (ar->vdev_id_11d_scan != ATH11K_11D_INVALID_VDEV_ID) { + vdev_id = ar->vdev_id_11d_scan; + +diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c +index 79ac2142317a..7ee3ff69dfc8 100644 +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -139,6 +139,9 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) + "reg hw scan wait left time %d\n", left); + } + ++ if (ar->state == ATH11K_STATE_RESTARTING) ++ return 0; ++ + bands = hw->wiphy->bands; + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!bands[band]) +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0251-ath11k-Handle-keepalive-during-WoWLAN-suspend-and-re.patch b/package/kernel/mac80211/patches/ath11k/0251-ath11k-Handle-keepalive-during-WoWLAN-suspend-and-re.patch new file mode 100644 index 00000000000000..377fe62159ba69 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0251-ath11k-Handle-keepalive-during-WoWLAN-suspend-and-re.patch @@ -0,0 +1,261 @@ +From 0f84a156aa3b9c9889c64a31d36b533508fabcb7 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 9 May 2022 14:57:31 +0300 +Subject: [PATCH] ath11k: Handle keepalive during WoWLAN suspend and resume + +With WoWLAN enabled and after sleeping for a rather long time, +we are seeing that with some APs, it is not able to wake up +the STA though the correct wake up pattern has been configured. +This is because the host doesn't send keepalive command to +firmware, thus firmware will not send any packet to the AP and +after a specific time the AP kicks out the STA. + +Fix this issue by enabling keepalive before going to suspend +and disabling it after resume back. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220506012540.1579604-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 31 ++++++++++++++++++ + drivers/net/wireless/ath/ath11k/mac.h | 4 +++ + drivers/net/wireless/ath/ath11k/wmi.c | 41 ++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.h | 46 +++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wow.c | 34 ++++++++++++++++++++ + 5 files changed, 156 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 9b28fdac3c94..7fdc39b7205e 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -9035,3 +9035,34 @@ void ath11k_mac_destroy(struct ath11k_base *ab) + pdev->ar = NULL; + } + } ++ ++int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif, ++ enum wmi_sta_keepalive_method method, ++ u32 interval) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct wmi_sta_keepalive_arg arg = {}; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ if (arvif->vdev_type != WMI_VDEV_TYPE_STA) ++ return 0; ++ ++ if (!test_bit(WMI_TLV_SERVICE_STA_KEEP_ALIVE, ar->ab->wmi_ab.svc_map)) ++ return 0; ++ ++ arg.vdev_id = arvif->vdev_id; ++ arg.enabled = 1; ++ arg.method = method; ++ arg.interval = interval; ++ ++ ret = ath11k_wmi_sta_keepalive(ar, &arg); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to set keepalive on vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ ++ return 0; ++} +diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h +index 7f93e3a9ca23..57ebfc592b00 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -8,6 +8,7 @@ + + #include + #include ++#include "wmi.h" + + struct ath11k; + struct ath11k_base; +@@ -173,4 +174,7 @@ void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb); + void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id); + void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif); + int ath11k_mac_wait_tx_complete(struct ath11k *ar); ++int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif, ++ enum wmi_sta_keepalive_method method, ++ u32 interval); + #endif +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 1410114d1d5c..63463b6c63be 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -8959,3 +8959,44 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar) + + return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID); + } ++ ++int ath11k_wmi_sta_keepalive(struct ath11k *ar, ++ const struct wmi_sta_keepalive_arg *arg) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_sta_keepalive_cmd *cmd; ++ struct wmi_sta_keepalive_arp_resp *arp; ++ struct sk_buff *skb; ++ size_t len; ++ ++ len = sizeof(*cmd) + sizeof(*arp); ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_sta_keepalive_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_STA_KEEPALIVE_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ cmd->vdev_id = arg->vdev_id; ++ cmd->enabled = arg->enabled; ++ cmd->interval = arg->interval; ++ cmd->method = arg->method; ++ ++ if (arg->method == WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE || ++ arg->method == WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST) { ++ arp = (struct wmi_sta_keepalive_arp_resp *)(cmd + 1); ++ arp->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_STA_KEEPALVE_ARP_RESPONSE) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*arp) - TLV_HDR_SIZE); ++ arp->src_ip4_addr = arg->src_ip4_addr; ++ arp->dest_ip4_addr = arg->dest_ip4_addr; ++ ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr); ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi sta keepalive vdev %d enabled %d method %d interval %d\n", ++ arg->vdev_id, arg->enabled, arg->method, arg->interval); ++ ++ return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); ++} +diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h +index 7600e9a52da8..b1fad4707dc6 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5907,6 +5907,50 @@ struct wmi_pdev_set_geo_table_cmd { + u32 rsvd_len; + } __packed; + ++struct wmi_sta_keepalive_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 enabled; ++ ++ /* WMI_STA_KEEPALIVE_METHOD_ */ ++ u32 method; ++ ++ /* in seconds */ ++ u32 interval; ++ ++ /* following this structure is the TLV for struct ++ * wmi_sta_keepalive_arp_resp ++ */ ++} __packed; ++ ++struct wmi_sta_keepalive_arp_resp { ++ u32 tlv_header; ++ u32 src_ip4_addr; ++ u32 dest_ip4_addr; ++ struct wmi_mac_addr dest_mac_addr; ++} __packed; ++ ++struct wmi_sta_keepalive_arg { ++ u32 vdev_id; ++ u32 enabled; ++ u32 method; ++ u32 interval; ++ u32 src_ip4_addr; ++ u32 dest_ip4_addr; ++ const u8 dest_mac_addr[ETH_ALEN]; ++}; ++ ++enum wmi_sta_keepalive_method { ++ WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1, ++ WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE = 2, ++ WMI_STA_KEEPALIVE_METHOD_ETHERNET_LOOPBACK = 3, ++ WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST = 4, ++ WMI_STA_KEEPALIVE_METHOD_MGMT_VENDOR_ACTION = 5, ++}; ++ ++#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 ++#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 ++ + int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); + struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); +@@ -6087,5 +6131,7 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar, + struct ath11k_vif *arvif); + int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val); + int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar); ++int ath11k_wmi_sta_keepalive(struct ath11k *ar, ++ const struct wmi_sta_keepalive_arg *arg); + + #endif +diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c +index 9d088cebef03..b3e65cd13d83 100644 +--- a/drivers/net/wireless/ath/ath11k/wow.c ++++ b/drivers/net/wireless/ath/ath11k/wow.c +@@ -640,6 +640,24 @@ static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable) + return 0; + } + ++static int ath11k_wow_set_keepalive(struct ath11k *ar, ++ enum wmi_sta_keepalive_method method, ++ u32 interval) ++{ ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ ret = ath11k_mac_vif_set_keepalive(arvif, method, interval); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + int ath11k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) + { +@@ -691,6 +709,14 @@ int ath11k_wow_op_suspend(struct ieee80211_hw *hw, + goto cleanup; + } + ++ ret = ath11k_wow_set_keepalive(ar, ++ WMI_STA_KEEPALIVE_METHOD_NULL_FRAME, ++ WMI_STA_KEEPALIVE_INTERVAL_DEFAULT); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret); ++ goto cleanup; ++ } ++ + ret = ath11k_wow_enable(ar->ab); + if (ret) { + ath11k_warn(ar->ab, "failed to start wow: %d\n", ret); +@@ -786,6 +812,14 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw) + goto exit; + } + ++ ret = ath11k_wow_set_keepalive(ar, ++ WMI_STA_KEEPALIVE_METHOD_NULL_FRAME, ++ WMI_STA_KEEPALIVE_INTERVAL_DISABLE); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret); ++ goto exit; ++ } ++ + exit: + if (ret) { + switch (ar->state) { +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0252-ath11k-Implement-remain-on-channel-support.patch b/package/kernel/mac80211/patches/ath11k/0252-ath11k-Implement-remain-on-channel-support.patch new file mode 100644 index 00000000000000..78513f33f022fc --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0252-ath11k-Implement-remain-on-channel-support.patch @@ -0,0 +1,199 @@ +From 3a5627b94222c3abc7e65486e2d2c0cc0a35c140 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 9 May 2022 14:57:31 +0300 +Subject: [PATCH] ath11k: Implement remain-on-channel support + +Add remain on channel support, it is needed in several +scenarios such as Passpoint etc. + +Currently this is supported by QCA6390, WCN6855, IPQ8074, +IPQ6018 and QCN9074. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220506013614.1580274-2-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 115 +++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 4 + + 3 files changed, 120 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 236215aa3867..6b6535437485 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1624,6 +1624,7 @@ static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) + complete(&ar->completed_11d_scan); + complete(&ar->scan.started); + complete(&ar->scan.completed); ++ complete(&ar->scan.on_channel); + complete(&ar->peer_assoc_done); + complete(&ar->peer_delete_done); + complete(&ar->install_key_done); +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 7fdc39b7205e..a8ddbdcfac16 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8354,6 +8354,118 @@ static int ath11k_mac_op_set_bios_sar_specs(struct ieee80211_hw *hw, + return ret; + } + ++static int ath11k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct ath11k *ar = hw->priv; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ spin_lock_bh(&ar->data_lock); ++ ar->scan.roc_notify = false; ++ spin_unlock_bh(&ar->data_lock); ++ ++ ath11k_scan_abort(ar); ++ ++ mutex_unlock(&ar->conf_mutex); ++ ++ cancel_delayed_work_sync(&ar->scan.timeout); ++ ++ return 0; ++} ++ ++static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_channel *chan, ++ int duration, ++ enum ieee80211_roc_type type) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_vif *arvif = (void *)vif->drv_priv; ++ struct scan_req_params arg; ++ int ret; ++ u32 scan_time_msec; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ spin_lock_bh(&ar->data_lock); ++ switch (ar->scan.state) { ++ case ATH11K_SCAN_IDLE: ++ reinit_completion(&ar->scan.started); ++ reinit_completion(&ar->scan.completed); ++ reinit_completion(&ar->scan.on_channel); ++ ar->scan.state = ATH11K_SCAN_STARTING; ++ ar->scan.is_roc = true; ++ ar->scan.vdev_id = arvif->vdev_id; ++ ar->scan.roc_freq = chan->center_freq; ++ ar->scan.roc_notify = true; ++ ret = 0; ++ break; ++ case ATH11K_SCAN_STARTING: ++ case ATH11K_SCAN_RUNNING: ++ case ATH11K_SCAN_ABORTING: ++ ret = -EBUSY; ++ break; ++ } ++ spin_unlock_bh(&ar->data_lock); ++ ++ if (ret) ++ goto exit; ++ ++ scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2; ++ ++ memset(&arg, 0, sizeof(arg)); ++ ath11k_wmi_start_scan_init(ar, &arg); ++ arg.num_chan = 1; ++ arg.chan_list = kcalloc(arg.num_chan, sizeof(*arg.chan_list), ++ GFP_KERNEL); ++ if (!arg.chan_list) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ arg.vdev_id = arvif->vdev_id; ++ arg.scan_id = ATH11K_SCAN_ID; ++ arg.chan_list[0] = chan->center_freq; ++ arg.dwell_time_active = scan_time_msec; ++ arg.dwell_time_passive = scan_time_msec; ++ arg.max_scan_time = scan_time_msec; ++ arg.scan_flags |= WMI_SCAN_FLAG_PASSIVE; ++ arg.scan_flags |= WMI_SCAN_FILTER_PROBE_REQ; ++ arg.burst_duration = duration; ++ ++ ret = ath11k_start_scan(ar, &arg); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to start roc scan: %d\n", ret); ++ ++ spin_lock_bh(&ar->data_lock); ++ ar->scan.state = ATH11K_SCAN_IDLE; ++ spin_unlock_bh(&ar->data_lock); ++ goto free_chan_list; ++ } ++ ++ ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ); ++ if (ret == 0) { ++ ath11k_warn(ar->ab, "failed to switch to channel for roc scan\n"); ++ ret = ath11k_scan_stop(ar); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret); ++ ret = -ETIMEDOUT; ++ goto free_chan_list; ++ } ++ ++ ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, ++ msecs_to_jiffies(duration)); ++ ++ ret = 0; ++ ++free_chan_list: ++ kfree(arg.chan_list); ++exit: ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ + static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .start = ath11k_mac_op_start, +@@ -8406,6 +8518,8 @@ static const struct ieee80211_ops ath11k_ops = { + #endif + + .set_sar_specs = ath11k_mac_op_set_bios_sar_specs, ++ .remain_on_channel = ath11k_mac_op_remain_on_channel, ++ .cancel_remain_on_channel = ath11k_mac_op_cancel_remain_on_channel, + }; + + static void ath11k_mac_update_ch_list(struct ath11k *ar, +@@ -8995,6 +9109,7 @@ int ath11k_mac_allocate(struct ath11k_base *ab) + init_completion(&ar->bss_survey_done); + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); ++ init_completion(&ar->scan.on_channel); + init_completion(&ar->thermal.wmi_sync); + + INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 63463b6c63be..0eb2e5ef45d2 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -5264,6 +5264,8 @@ static void ath11k_wmi_event_scan_started(struct ath11k *ar) + break; + case ATH11K_SCAN_STARTING: + ar->scan.state = ATH11K_SCAN_RUNNING; ++ if (ar->scan.is_roc) ++ ieee80211_ready_on_channel(ar->hw); + complete(&ar->scan.started); + break; + } +@@ -5346,6 +5348,8 @@ static void ath11k_wmi_event_scan_foreign_chan(struct ath11k *ar, u32 freq) + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); ++ if (ar->scan.is_roc && ar->scan.roc_freq == freq) ++ complete(&ar->scan.on_channel); + break; + } + } +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0253-ath11k-Don-t-check-arvif-is_started-before-sending-m.patch b/package/kernel/mac80211/patches/ath11k/0253-ath11k-Don-t-check-arvif-is_started-before-sending-m.patch new file mode 100644 index 00000000000000..a5f68f5dd6eb76 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0253-ath11k-Don-t-check-arvif-is_started-before-sending-m.patch @@ -0,0 +1,59 @@ +From 355333a217541916576351446b5832fec7930566 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 9 May 2022 14:57:31 +0300 +Subject: [PATCH] ath11k: Don't check arvif->is_started before sending + management frames + +Commit 66307ca04057 ("ath11k: fix mgmt_tx_wmi cmd sent to FW for +deleted vdev") wants both of below two conditions are true before +sending management frames: + +1: ar->allocated_vdev_map & (1LL << arvif->vdev_id) +2: arvif->is_started + +Actually the second one is not necessary because with the first one +we can make sure the vdev is present. + +Also use ar->conf_mutex to synchronize vdev delete and mgmt. TX. + +This issue is found in case of Passpoint scenario where ath11k +needs to send action frames before vdev is started. + +Fix it by removing the second condition. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Fixes: 66307ca04057 ("ath11k: fix mgmt_tx_wmi cmd sent to FW for deleted vdev") +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220506013614.1580274-3-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index a8ddbdcfac16..73de3619101f 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5551,8 +5551,8 @@ static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work) + } + + arvif = ath11k_vif_to_arvif(skb_cb->vif); +- if (ar->allocated_vdev_map & (1LL << arvif->vdev_id) && +- arvif->is_started) { ++ mutex_lock(&ar->conf_mutex); ++ if (ar->allocated_vdev_map & (1LL << arvif->vdev_id)) { + ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n", +@@ -5570,6 +5570,7 @@ static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work) + arvif->is_started); + ath11k_mgmt_over_wmi_tx_drop(ar, skb); + } ++ mutex_unlock(&ar->conf_mutex); + } + } + +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0254-ath11k-Designating-channel-frequency-when-sending-ma.patch b/package/kernel/mac80211/patches/ath11k/0254-ath11k-Designating-channel-frequency-when-sending-ma.patch new file mode 100644 index 00000000000000..9feded5c4136e0 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0254-ath11k-Designating-channel-frequency-when-sending-ma.patch @@ -0,0 +1,140 @@ +From 1d7f514577f0ccf3e5f5736247138868fb62896a Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 9 May 2022 14:57:32 +0300 +Subject: [PATCH] ath11k: Designating channel frequency when sending management + frames + +In case of Passpoint, the WLAN interface may be requested to +remain on a specific channel and then to send some management +frames on that channel. Now chanfreq of wmi_mgmt_send_cmd is set +as 0, as a result firmware may choose a default but wrong channel. +Fix it by assigning chanfreq field with the designated channel. + +This change only applies to WCN6855 and QCA6390, other chips are +not affected. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220506013614.1580274-4-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 7 +++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/wmi.c | 17 ++++++++++++++++- + 3 files changed, 24 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 6b6535437485..26f7bdd1241a 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -110,6 +110,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dp_window_idx = 0, + .ce_window_idx = 0, + .fixed_fw_mem = false, ++ .support_off_channel_tx = false, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -185,6 +186,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dp_window_idx = 0, + .ce_window_idx = 0, + .fixed_fw_mem = false, ++ .support_off_channel_tx = false, + }, + { + .name = "qca6390 hw2.0", +@@ -259,6 +261,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dp_window_idx = 0, + .ce_window_idx = 0, + .fixed_fw_mem = false, ++ .support_off_channel_tx = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -333,6 +336,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dp_window_idx = 3, + .ce_window_idx = 2, + .fixed_fw_mem = false, ++ .support_off_channel_tx = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -407,6 +411,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dp_window_idx = 0, + .ce_window_idx = 0, + .fixed_fw_mem = false, ++ .support_off_channel_tx = true, + }, + { + .name = "wcn6855 hw2.1", +@@ -480,6 +485,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dp_window_idx = 0, + .ce_window_idx = 0, + .fixed_fw_mem = false, ++ .support_off_channel_tx = true, + }, + { + .name = "wcn6750 hw1.0", +@@ -553,6 +559,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .dp_window_idx = 1, + .ce_window_idx = 2, + .fixed_fw_mem = true, ++ .support_off_channel_tx = false, + }, + }; + +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 3a2abde63489..77dc5c851c9b 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -204,6 +204,7 @@ struct ath11k_hw_params { + u8 dp_window_idx; + u8 ce_window_idx; + bool fixed_fw_mem; ++ bool support_off_channel_tx; + }; + + struct ath11k_hw_ops { +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 0eb2e5ef45d2..84d1c7054013 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -625,10 +625,25 @@ struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len) + return skb; + } + ++static u32 ath11k_wmi_mgmt_get_freq(struct ath11k *ar, ++ struct ieee80211_tx_info *info) ++{ ++ struct ath11k_base *ab = ar->ab; ++ u32 freq = 0; ++ ++ if (ab->hw_params.support_off_channel_tx && ++ ar->scan.is_roc && ++ (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) ++ freq = ar->scan.roc_freq; ++ ++ return freq; ++} ++ + int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, + struct sk_buff *frame) + { + struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(frame); + struct wmi_mgmt_send_cmd *cmd; + struct wmi_tlv *frame_tlv; + struct sk_buff *skb; +@@ -649,7 +664,7 @@ int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->desc_id = buf_id; +- cmd->chanfreq = 0; ++ cmd->chanfreq = ath11k_wmi_mgmt_get_freq(ar, info); + cmd->paddr_lo = lower_32_bits(ATH11K_SKB_CB(frame)->paddr); + cmd->paddr_hi = upper_32_bits(ATH11K_SKB_CB(frame)->paddr); + cmd->frame_len = frame->len; +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0256-ath11k-Reuse-the-available-memory-after-firmware-rel.patch b/package/kernel/mac80211/patches/ath11k/0256-ath11k-Reuse-the-available-memory-after-firmware-rel.patch new file mode 100644 index 00000000000000..13e3ecc400a7cd --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0256-ath11k-Reuse-the-available-memory-after-firmware-rel.patch @@ -0,0 +1,116 @@ +From 5962f370ce416371b432325a8f98680f73a1bfdc Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Mon, 9 May 2022 14:57:32 +0300 +Subject: [PATCH] ath11k: Reuse the available memory after firmware reload + +Ath11k allocates memory when firmware requests memory in QMI. +Coldboot calibration and firmware recovery uses firmware reload. +On firmware reload, firmware sends memory request again. If Ath11k +allocates memory on first firmware boot, reuse the available +memory. Also check if the segment type and size is same +on the next firmware boot. Reuse if segment type/size is +same as previous firmware boot else free the segment and +allocate the segment with size/type. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.6.0.1-00752-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220506141448.10340-1-quic_akolli@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 1 - + drivers/net/wireless/ath/ath11k/qmi.c | 24 +++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/qmi.h | 2 ++ + 3 files changed, 23 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 26f7bdd1241a..1e98ff9ff288 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1777,7 +1777,6 @@ static void ath11k_core_reset(struct work_struct *work) + ATH11K_RECOVER_START_TIMEOUT_HZ); + + ath11k_hif_power_down(ab); +- ath11k_qmi_free_resource(ab); + ath11k_hif_power_up(ab); + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n"); +diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c +index d1e945074bc1..61ead37a944a 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1970,6 +1970,21 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + chunk = &ab->qmi.target_mem[i]; ++ ++ /* Firmware reloads in coldboot/firmware recovery. ++ * in such case, no need to allocate memory for FW again. ++ */ ++ if (chunk->vaddr) { ++ if (chunk->prev_type == chunk->type || ++ chunk->prev_size == chunk->size) ++ continue; ++ ++ /* cannot reuse the existing chunk */ ++ dma_free_coherent(ab->dev, chunk->size, ++ chunk->vaddr, chunk->paddr); ++ chunk->vaddr = NULL; ++ } ++ + chunk->vaddr = dma_alloc_coherent(ab->dev, + chunk->size, + &chunk->paddr, +@@ -1990,6 +2005,8 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) + chunk->type); + return -EINVAL; + } ++ chunk->prev_type = chunk->type; ++ chunk->prev_size = chunk->size; + } + + return 0; +@@ -2466,9 +2483,6 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab) + char path[100]; + int ret; + +- if (m3_mem->vaddr || m3_mem->size) +- return 0; +- + fw = ath11k_core_firmware_request(ab, ATH11K_M3_FILE); + if (IS_ERR(fw)) { + ret = PTR_ERR(fw); +@@ -2478,6 +2492,9 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab) + return ret; + } + ++ if (m3_mem->vaddr || m3_mem->size) ++ goto skip_m3_alloc; ++ + m3_mem->vaddr = dma_alloc_coherent(ab->dev, + fw->size, &m3_mem->paddr, + GFP_KERNEL); +@@ -2488,6 +2505,7 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab) + return -ENOMEM; + } + ++skip_m3_alloc: + memcpy(m3_mem->vaddr, fw->data, fw->size); + m3_mem->size = fw->size; + release_firmware(fw); +diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h +index c24e6995cca3..c83cf822be81 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -97,6 +97,8 @@ struct ath11k_qmi_event_msg { + struct target_mem_chunk { + u32 size; + u32 type; ++ u32 prev_size; ++ u32 prev_type; + dma_addr_t paddr; + u32 *vaddr; + void __iomem *iaddr; +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0257-ath11k-remove-redundant-assignment-to-variables-vht_.patch b/package/kernel/mac80211/patches/ath11k/0257-ath11k-remove-redundant-assignment-to-variables-vht_.patch new file mode 100644 index 00000000000000..b0688ce9f7c4fc --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0257-ath11k-remove-redundant-assignment-to-variables-vht_.patch @@ -0,0 +1,53 @@ +From 25c321e8534e9efe1869b548e7912faffed1f5be Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Mon, 9 May 2022 14:57:32 +0300 +Subject: [PATCH] ath11k: remove redundant assignment to variables vht_mcs and + he_mcs + +The variables vht_mcs and he_mcs are being initialized in the +start of for-loops however they are re-assigned new values in +the loop and not used outside the loop. The initializations +are redundant and can be removed. + +Cleans up clang scan warnings: + +warning: Although the value stored to 'vht_mcs' is used in the +enclosing expression, the value is never actually read from +'vht_mcs' [deadcode.DeadStores] + +warning: Although the value stored to 'he_mcs' is used in the +enclosing expression, the value is never actually read from +'he_mcs' [deadcode.DeadStores] + +Signed-off-by: Colin Ian King +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220507184155.26939-1-colin.i.king@gmail.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 73de3619101f..ee1590b16eff 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1951,7 +1951,7 @@ static void ath11k_peer_assoc_h_vht(struct ath11k *ar, + /* Calculate peer NSS capability from VHT capabilities if STA + * supports VHT. + */ +- for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) { ++ for (i = 0, max_nss = 0; i < NL80211_VHT_NSS_MAX; i++) { + vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >> + (2 * i) & 3; + +@@ -2272,7 +2272,7 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, + /* Calculate peer NSS capability from HE capabilities if STA + * supports HE. + */ +- for (i = 0, max_nss = 0, he_mcs = 0; i < NL80211_HE_NSS_MAX; i++) { ++ for (i = 0, max_nss = 0; i < NL80211_HE_NSS_MAX; i++) { + he_mcs = he_tx_mcs >> (2 * i) & 3; + + /* In case of fixed rates, MCS Range in he_tx_mcs might have +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0258-ath11k-update-missing-MU-MIMO-and-OFDMA-stats.patch b/package/kernel/mac80211/patches/ath11k/0258-ath11k-update-missing-MU-MIMO-and-OFDMA-stats.patch new file mode 100644 index 00000000000000..22f5e0a92f5254 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0258-ath11k-update-missing-MU-MIMO-and-OFDMA-stats.patch @@ -0,0 +1,257 @@ +From fdb8fc34a6fbf8c7614f222cbe1176a0598f1941 Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Mon, 16 May 2022 13:25:59 +0300 +Subject: [PATCH] ath11k: update missing MU-MIMO and OFDMA stats + +Update the missing MU-MIMO/OFDMA info in PDEV RX, +trigger and schedule stats. i.e HTT stats type 10, 12 and 17 +respectively. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4-01209-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1652152236-12038-1-git-send-email-quic_srirrama@quicinc.com +--- + .../wireless/ath/ath11k/debugfs_htt_stats.c | 88 ++++++++++++++++++- + .../wireless/ath/ath11k/debugfs_htt_stats.h | 39 ++++++++ + 2 files changed, 126 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +index 4484235bcda4..b3efca6bd7dd 100644 +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-3-Clause-Clear + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #include +@@ -1403,6 +1404,8 @@ htt_print_tx_selfgen_ax_stats_tlv(const void *tag_buf, + htt_stats_buf->ax_mu_mimo_brpoll_7); + len += scnprintf(buf + len, buf_len - len, "ax_basic_trigger = %u\n", + htt_stats_buf->ax_basic_trigger); ++ len += scnprintf(buf + len, buf_len - len, "ax_ulmumimo_trigger = %u\n", ++ htt_stats_buf->ax_ulmumimo_trigger); + len += scnprintf(buf + len, buf_len - len, "ax_bsr_trigger = %u\n", + htt_stats_buf->ax_bsr_trigger); + len += scnprintf(buf + len, buf_len - len, "ax_mu_bar_trigger = %u\n", +@@ -1485,6 +1488,8 @@ htt_print_tx_selfgen_ax_err_stats_tlv(const void *tag_buf, + htt_stats_buf->ax_mu_mimo_brp7_err); + len += scnprintf(buf + len, buf_len - len, "ax_basic_trigger_err = %u\n", + htt_stats_buf->ax_basic_trigger_err); ++ len += scnprintf(buf + len, buf_len - len, "ax_ulmumimo_trigger_err = %u\n", ++ htt_stats_buf->ax_ulmumimo_trigger_err); + len += scnprintf(buf + len, buf_len - len, "ax_bsr_trigger_err = %u\n", + htt_stats_buf->ax_bsr_trigger_err); + len += scnprintf(buf + len, buf_len - len, "ax_mu_bar_trigger_err = %u\n", +@@ -1519,6 +1524,16 @@ htt_print_tx_pdev_mu_mimo_sch_stats_tlv(const void *tag_buf, + len += scnprintf(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n\n", + htt_stats_buf->mu_mimo_ppdu_posted); + ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "ac_mu_mimo_sch_posted_per_group_index %u = %u\n", ++ i, htt_stats_buf->ac_mu_mimo_sch_posted_per_grp_sz[i]); ++ ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_mu_mimo_sch_posted_per_group_index %u = %u\n", ++ i, htt_stats_buf->ax_mu_mimo_sch_posted_per_grp_sz[i]); ++ + len += scnprintf(buf + len, buf_len - len, "11ac MU_MIMO SCH STATS:\n"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) +@@ -1535,10 +1550,34 @@ htt_print_tx_pdev_mu_mimo_sch_stats_tlv(const void *tag_buf, + + len += scnprintf(buf + len, buf_len - len, "\n11ax OFDMA SCH STATS:\n"); + +- for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) { + len += scnprintf(buf + len, buf_len - len, + "ax_ofdma_sch_nusers_%u = %u\n", + i, htt_stats_buf->ax_ofdma_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ul_ofdma_basic_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_ul_ofdma_basic_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ul_ofdma_bsr_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_ul_ofdma_bsr_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ul_ofdma_sch_bar_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_ul_ofdma_bar_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ul_ofdma_brp_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_ul_ofdma_brp_sch_nusers[i]); ++ } ++ ++ len += scnprintf(buf + len, buf_len - len, "\n11ax UL MUMIO SCH STATS:\n"); ++ ++ for (i = 0; i < HTT_TX_PDEV_STATS_NUM_UL_MUMIMO_USER_STATS; i++) { ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ul_mumimo_basic_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_ul_mumimo_basic_sch_nusers[i]); ++ len += scnprintf(buf + len, buf_len - len, ++ "ax_ul_mumimo_brp_sch_nusers_%u = %u\n", ++ i, htt_stats_buf->ax_ul_mumimo_brp_sch_nusers[i]); ++ } + + if (len >= buf_len) + buf[buf_len - 1] = 0; +@@ -2933,6 +2972,21 @@ static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, + len += scnprintf(buf + len, buf_len - len, "txbf = %u\n", + htt_stats_buf->txbf); + ++ len += scnprintf(buf + len, buf_len - len, "\nrx_su_ndpa = %u", ++ htt_stats_buf->rx_su_ndpa); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_11ax_su_txbf_mcs, ++ "rx_11ax_su_txbf_mcs", HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, ++ "\n"); ++ ++ len += scnprintf(buf + len, buf_len - len, "\nrx_mu_ndpa = %u", ++ htt_stats_buf->rx_mu_ndpa); ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_11ax_mu_txbf_mcs, ++ "rx_11ax_mu_txbf_mcs", HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, ++ "\n"); ++ ++ len += scnprintf(buf + len, buf_len - len, "\nrx_br_poll = %u", ++ htt_stats_buf->rx_br_poll); ++ + PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_legacy_cck_rate, + "rx_legacy_cck_rate", + HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS, "\n"); +@@ -2995,6 +3049,38 @@ static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, + len += scnprintf(buf + len, buf_len - len, "\n"); + } + ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulofdma_non_data_nusers, ++ "rx_ulofdma_non_data_nusers", HTT_RX_PDEV_MAX_OFDMA_NUM_USER, ++ "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulofdma_data_nusers, ++ "rx_ulofdma_data_nusers", HTT_RX_PDEV_MAX_OFDMA_NUM_USER, ++ "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_11ax_dl_ofdma_mcs, ++ "rx_11ax_dl_ofdma_mcs", HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, ++ "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_11ax_dl_ofdma_ru, ++ "rx_11ax_dl_ofdma_ru", HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS, ++ "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulmumimo_non_data_ppdu, ++ "rx_ulmumimo_non_data_ppdu", HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, ++ "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulmumimo_data_ppdu, ++ "rx_ulmumimo_data_ppdu", HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, ++ "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulmumimo_mpdu_ok, ++ "rx_ulmumimo_mpdu_ok", HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, ++ "\n"); ++ ++ PRINT_ARRAY_TO_BUF(buf, len, htt_stats_buf->rx_ulmumimo_mpdu_fail, ++ "rx_ulmumimo_mpdu_fail", HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, ++ "\n"); ++ + len += scnprintf(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x\n", + htt_stats_buf->per_chain_rssi_pkt_type); + +diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h +index dc210c54d131..5d722b51b125 100644 +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. ++ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #ifndef DEBUG_HTT_STATS_H +@@ -682,6 +683,7 @@ struct htt_tx_selfgen_ax_stats_tlv { + u32 ax_bsr_trigger; + u32 ax_mu_bar_trigger; + u32 ax_mu_rts_trigger; ++ u32 ax_ulmumimo_trigger; + }; + + struct htt_tx_selfgen_ac_err_stats_tlv { +@@ -712,12 +714,14 @@ struct htt_tx_selfgen_ax_err_stats_tlv { + u32 ax_bsr_trigger_err; + u32 ax_mu_bar_trigger_err; + u32 ax_mu_rts_trigger_err; ++ u32 ax_ulmumimo_trigger_err; + }; + + /* == TX MU STATS == */ + #define HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS 4 + #define HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS 8 + #define HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS 74 ++#define HTT_TX_PDEV_STATS_NUM_UL_MUMIMO_USER_STATS 8 + + struct htt_tx_pdev_mu_mimo_sch_stats_tlv { + /* mu-mimo sw sched cmd stats */ +@@ -734,6 +738,24 @@ struct htt_tx_pdev_mu_mimo_sch_stats_tlv { + u32 ac_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS]; + u32 ax_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS]; + u32 ax_ofdma_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ u32 ax_ul_ofdma_basic_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ u32 ax_ul_ofdma_bsr_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ u32 ax_ul_ofdma_bar_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ u32 ax_ul_ofdma_brp_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; ++ ++ /* UL MU-MIMO */ ++ /* ax_ul_mumimo_basic_sch_nusers[i] is the number of basic triggers sent ++ * for (i+1) users ++ */ ++ u32 ax_ul_mumimo_basic_sch_nusers[HTT_TX_PDEV_STATS_NUM_UL_MUMIMO_USER_STATS]; ++ ++ /* ax_ul_mumimo_brp_sch_nusers[i] is the number of brp triggers sent ++ * for (i+1) users ++ */ ++ u32 ax_ul_mumimo_brp_sch_nusers[HTT_TX_PDEV_STATS_NUM_UL_MUMIMO_USER_STATS]; ++ ++ u32 ac_mu_mimo_sch_posted_per_grp_sz[HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS]; ++ u32 ax_mu_mimo_sch_posted_per_grp_sz[HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS]; + }; + + struct htt_tx_pdev_mu_mimo_mpdu_stats_tlv { +@@ -1297,6 +1319,8 @@ struct htt_tx_pdev_rate_stats_tlv { + #define HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + #define HTT_RX_PDEV_MAX_OFDMA_NUM_USER 8 + #define HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS 16 ++#define HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS 6 ++#define HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER 8 + + struct htt_rx_pdev_rate_stats_tlv { + u32 mac_id__word; +@@ -1375,6 +1399,21 @@ struct htt_rx_pdev_rate_stats_tlv { + u32 per_chain_rssi_pkt_type; + s8 rx_per_chain_rssi_in_dbm[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; ++ ++ u32 rx_su_ndpa; ++ u32 rx_11ax_su_txbf_mcs[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; ++ u32 rx_mu_ndpa; ++ u32 rx_11ax_mu_txbf_mcs[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; ++ u32 rx_br_poll; ++ u32 rx_11ax_dl_ofdma_mcs[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; ++ u32 rx_11ax_dl_ofdma_ru[HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS]; ++ ++ u32 rx_ulmumimo_non_data_ppdu[HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; ++ u32 rx_ulmumimo_data_ppdu[HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; ++ u32 rx_ulmumimo_mpdu_ok[HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; ++ u32 rx_ulmumimo_mpdu_fail[HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; ++ u32 rx_ulofdma_non_data_nusers[HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; ++ u32 rx_ulofdma_data_nusers[HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + }; + + /* == RX PDEV/SOC STATS == */ +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0259-ath11k-Fix-warning-on-variable-sar-dereference-befor.patch b/package/kernel/mac80211/patches/ath11k/0259-ath11k-Fix-warning-on-variable-sar-dereference-befor.patch new file mode 100644 index 00000000000000..ffa5afd48f3148 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0259-ath11k-Fix-warning-on-variable-sar-dereference-befor.patch @@ -0,0 +1,68 @@ +From 77bbbd5e0ed3b5998a353b0948584faa4f565f0e Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Tue, 17 May 2022 08:48:44 +0800 +Subject: [PATCH] ath11k: Fix warning on variable 'sar' dereference before + check + +We are seeing below warning: +warn: variable dereferenced before check 'sar' + +Fix it by moving ahead pointer check on 'sar'. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Fixes: 652f69ed9c1b ("ath11k: Add support for SAR") +Reported-by: kernel test robot +Reported-by: Dan Carpenter +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220517004844.2412660-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index ee1590b16eff..7d574ad67e59 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8297,11 +8297,15 @@ static int ath11k_mac_op_set_bios_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) + { + struct ath11k *ar = hw->priv; +- const struct cfg80211_sar_sub_specs *sspec = sar->sub_specs; ++ const struct cfg80211_sar_sub_specs *sspec; + int ret, index; + u8 *sar_tbl; + u32 i; + ++ if (!sar || sar->type != NL80211_SAR_TYPE_POWER || ++ sar->num_sub_specs == 0) ++ return -EINVAL; ++ + mutex_lock(&ar->conf_mutex); + + if (!test_bit(WMI_TLV_SERVICE_BIOS_SAR_SUPPORT, ar->ab->wmi_ab.svc_map) || +@@ -8310,12 +8314,6 @@ static int ath11k_mac_op_set_bios_sar_specs(struct ieee80211_hw *hw, + goto exit; + } + +- if (!sar || sar->type != NL80211_SAR_TYPE_POWER || +- sar->num_sub_specs == 0) { +- ret = -EINVAL; +- goto exit; +- } +- + ret = ath11k_wmi_pdev_set_bios_geo_table_param(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to set geo table: %d\n", ret); +@@ -8328,6 +8326,7 @@ static int ath11k_mac_op_set_bios_sar_specs(struct ieee80211_hw *hw, + goto exit; + } + ++ sspec = sar->sub_specs; + for (i = 0; i < sar->num_sub_specs; i++) { + if (sspec->freq_range_index >= (BIOS_SAR_TABLE_LEN >> 1)) { + ath11k_warn(ar->ab, "Ignore bad frequency index %u, max allowed %u\n", +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0260-ath11k-Init-hw_params-before-setting-up-AHB-resource.patch b/package/kernel/mac80211/patches/ath11k/0260-ath11k-Init-hw_params-before-setting-up-AHB-resource.patch new file mode 100644 index 00000000000000..247d566f6cdc1a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0260-ath11k-Init-hw_params-before-setting-up-AHB-resource.patch @@ -0,0 +1,46 @@ +From bebcfd2534a63ab7e7325f5337662bc84ca038b6 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Tue, 17 May 2022 11:22:41 +0530 +Subject: [PATCH] ath11k: Init hw_params before setting up AHB resources + +As part of adding the support of WCN6750 to ath11k, bus_params +were moved to hw_params and this regressed the initialization +of WCN6750. By the time AHB resources are setup for WCN6750, +hw_params will not be initialized and therefore initialization +for WCN6750 will fail. This is applicable only for WCN6750, +no other device is impacted. + +Fix this by moving the initialization of hw_params before +setting up AHB resources. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Fixes: 00402f49d26f ("ath11k: Add support for WCN6750 device") +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220517055241.15885-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/ahb.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c +index fa11807f48a9..d7d33d5cdfc5 100644 +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -976,11 +976,11 @@ static int ath11k_ahb_probe(struct platform_device *pdev) + ab->hw_rev = hw_rev; + platform_set_drvdata(pdev, ab); + +- ret = ath11k_ahb_setup_resources(ab); ++ ret = ath11k_core_pre_init(ab); + if (ret) + goto err_core_free; + +- ret = ath11k_core_pre_init(ab); ++ ret = ath11k_ahb_setup_resources(ab); + if (ret) + goto err_core_free; + +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0261-mac80211-introduce-BSS-color-collision-detection.patch b/package/kernel/mac80211/patches/ath11k/0261-mac80211-introduce-BSS-color-collision-detection.patch new file mode 100644 index 00000000000000..e3c3f7bd0f8bb3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0261-mac80211-introduce-BSS-color-collision-detection.patch @@ -0,0 +1,44 @@ +From 6d945a33f2b0aa24fc210dadaa0af3e8218e7002 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 25 Mar 2022 11:42:41 +0100 +Subject: [PATCH] mac80211: introduce BSS color collision detection + +Add ieee80211_rx_check_bss_color_collision routine in order to introduce +BSS color collision detection in mac80211 if it is not supported in HW/FW +(e.g. for mt7915 chipset). +Add IEEE80211_HW_DETECTS_COLOR_COLLISION flag to let the driver notify +BSS color collision detection is supported in HW/FW. Set this for ath11k +which apparently didn't need this code. + +Tested-by: Peter Chiu +Co-developed-by: Ryder Lee +Signed-off-by: Ryder Lee +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/a05eeeb1841a84560dc5aaec77894fcb69a54f27.1648204871.git.lorenzo@kernel.org +[clarify commit message a bit, move flag to mac80211] +Signed-off-by: Johannes Berg +--- + drivers/net/wireless/ath/ath11k/mac.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 175a4ae752f3..49421d91d39c 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8720,9 +8720,12 @@ static int __ath11k_mac_register(struct ath11k *ar) + + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); +- if (test_bit(WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD, ar->ab->wmi_ab.svc_map)) ++ if (test_bit(WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD, ++ ar->ab->wmi_ab.svc_map)) { + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_BSS_COLOR); ++ ieee80211_hw_set(ar->hw, DETECTS_COLOR_COLLISION); ++ } + + ar->hw->wiphy->cipher_suites = cipher_suites; + ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0262-ath11k-fix-netdev-open-race.patch b/package/kernel/mac80211/patches/ath11k/0262-ath11k-fix-netdev-open-race.patch new file mode 100644 index 00000000000000..9e2b6f10628aaa --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0262-ath11k-fix-netdev-open-race.patch @@ -0,0 +1,104 @@ +From d4ba1ff87b17e81686ada8f429300876f55f95ad Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Sun, 22 May 2022 15:33:16 +0300 +Subject: [PATCH] ath11k: fix netdev open race + +Make sure to allocate resources needed before registering the device. + +This specifically avoids having a racing open() trigger a BUG_ON() in +mod_timer() when ath11k_mac_op_start() is called before the +mon_reap_timer as been set up. + +I did not see this issue with next-20220310, but I hit it on every probe +with next-20220511. Perhaps some timing changed in between. + +Here's the backtrace: + +[ 51.346947] kernel BUG at kernel/time/timer.c:990! +[ 51.346958] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP +... +[ 51.578225] Call trace: +[ 51.583293] __mod_timer+0x298/0x390 +[ 51.589518] mod_timer+0x14/0x20 +[ 51.595368] ath11k_mac_op_start+0x41c/0x4a0 [ath11k] +[ 51.603165] drv_start+0x38/0x60 [mac80211] +[ 51.610110] ieee80211_do_open+0x29c/0x7d0 [mac80211] +[ 51.617945] ieee80211_open+0x60/0xb0 [mac80211] +[ 51.625311] __dev_open+0x100/0x1c0 +[ 51.631420] __dev_change_flags+0x194/0x210 +[ 51.638214] dev_change_flags+0x24/0x70 +[ 51.644646] do_setlink+0x228/0xdb0 +[ 51.650723] __rtnl_newlink+0x460/0x830 +[ 51.657162] rtnl_newlink+0x4c/0x80 +[ 51.663229] rtnetlink_rcv_msg+0x124/0x390 +[ 51.669917] netlink_rcv_skb+0x58/0x130 +[ 51.676314] rtnetlink_rcv+0x18/0x30 +[ 51.682460] netlink_unicast+0x250/0x310 +[ 51.688960] netlink_sendmsg+0x19c/0x3e0 +[ 51.695458] ____sys_sendmsg+0x220/0x290 +[ 51.701938] ___sys_sendmsg+0x7c/0xc0 +[ 51.708148] __sys_sendmsg+0x68/0xd0 +[ 51.714254] __arm64_sys_sendmsg+0x28/0x40 +[ 51.720900] invoke_syscall+0x48/0x120 + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") +Fixes: 840c36fa727a ("ath11k: dp: stop rx pktlog before suspend") +Signed-off-by: Johan Hovold +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220517103436.15867-1-johan+linaro@kernel.org +--- + drivers/net/wireless/ath/ath11k/core.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 1e98ff9ff288..c8e0bc935838 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1225,23 +1225,23 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab) + return ret; + } + +- ret = ath11k_mac_register(ab); ++ ret = ath11k_dp_pdev_alloc(ab); + if (ret) { +- ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); ++ ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); + goto err_pdev_debug; + } + +- ret = ath11k_dp_pdev_alloc(ab); ++ ret = ath11k_mac_register(ab); + if (ret) { +- ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); +- goto err_mac_unregister; ++ ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); ++ goto err_dp_pdev_free; + } + + ret = ath11k_thermal_register(ab); + if (ret) { + ath11k_err(ab, "could not register thermal device: %d\n", + ret); +- goto err_dp_pdev_free; ++ goto err_mac_unregister; + } + + ret = ath11k_spectral_init(ab); +@@ -1254,10 +1254,10 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab) + + err_thermal_unregister: + ath11k_thermal_unregister(ab); +-err_dp_pdev_free: +- ath11k_dp_pdev_free(ab); + err_mac_unregister: + ath11k_mac_unregister(ab); ++err_dp_pdev_free: ++ ath11k_dp_pdev_free(ab); + err_pdev_debug: + ath11k_debugfs_pdev_destroy(ab); + +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0263-ath11k-fix-IRQ-affinity-warning-on-shutdown.patch b/package/kernel/mac80211/patches/ath11k/0263-ath11k-fix-IRQ-affinity-warning-on-shutdown.patch new file mode 100644 index 00000000000000..b15eb5822cc22f --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0263-ath11k-fix-IRQ-affinity-warning-on-shutdown.patch @@ -0,0 +1,36 @@ +From 3bd0c69653ac636eae8872aacdcd4156f772f928 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Mon, 23 May 2022 16:32:58 +0200 +Subject: [PATCH] ath11k: fix IRQ affinity warning on shutdown + +Make sure to clear the IRQ affinity hint also on shutdown to avoid +triggering a WARN_ON_ONCE() in __free_irq() when stopping MHI while +using a single MSI vector. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Fixes: e94b07493da3 ("ath11k: Set IRQ affinity to CPU0 in case of one MSI vector") +Signed-off-by: Johan Hovold +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220523143258.24818-1-johan+linaro@kernel.org +--- + drivers/net/wireless/ath/ath11k/pci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c +index dedf1b88ddf6..487a303b3077 100644 +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -920,7 +920,9 @@ static void ath11k_pci_remove(struct pci_dev *pdev) + static void ath11k_pci_shutdown(struct pci_dev *pdev) + { + struct ath11k_base *ab = pci_get_drvdata(pdev); ++ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + ++ ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); + ath11k_pci_power_down(ab); + } + +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0264-ath11k-support-avg-signal-in-station-dump.patch b/package/kernel/mac80211/patches/ath11k/0264-ath11k-support-avg-signal-in-station-dump.patch new file mode 100644 index 00000000000000..74ea8ec803781b --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0264-ath11k-support-avg-signal-in-station-dump.patch @@ -0,0 +1,90 @@ +From b205ce4c266cd66e6f954733ef6c94f06722e150 Mon Sep 17 00:00:00 2001 +From: Thiraviyam Mariyappan +Date: Thu, 19 May 2022 18:27:41 +0530 +Subject: [PATCH] ath11k: support avg signal in station dump + +Currently mac80211 doesn't calculate average signal when RSS(Receive side +scaling) is enabled from the driver, so average signal isn't printed in +the station dump. To address this issue, calculate the average signal +from RSSI within driver and display in the station dump. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Thiraviyam Mariyappan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1652965061-30457-1-git-send-email-quic_tmariyap@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 4 ++++ + drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +++ + drivers/net/wireless/ath/ath11k/mac.c | 5 +++++ + 3 files changed, 12 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index 95bca0b078b1..ea31016437b0 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include "qmi.h" + #include "htc.h" + #include "wmi.h" +@@ -464,6 +465,8 @@ struct ath11k_per_ppdu_tx_stats { + u32 retry_bytes; + }; + ++DECLARE_EWMA(avg_rssi, 10, 8) ++ + struct ath11k_sta { + struct ath11k_vif *arvif; + +@@ -482,6 +485,7 @@ struct ath11k_sta { + u64 rx_duration; + u64 tx_duration; + u8 rssi_comb; ++ struct ewma_avg_rssi avg_rssi; + s8 rssi_beacon; + s8 chain_signal[IEEE80211_MAX_CHAINS]; + struct ath11k_htt_tx_stats *tx_stats; +diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c +index 049774cc158c..e16beb96c37c 100644 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2765,6 +2765,9 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, + if (!rx_stats) + return; + ++ arsta->rssi_comb = ppdu_info->rssi_comb; ++ ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); ++ + num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count + + ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count; + +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 7d574ad67e59..f11956163822 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4479,6 +4479,7 @@ static int ath11k_mac_station_add(struct ath11k *ar, + } + } + ++ ewma_avg_rssi_init(&arsta->avg_rssi); + return 0; + + free_tx_stats: +@@ -8161,6 +8162,10 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, + sinfo->signal = db2dbm ? signal : signal + ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + } ++ ++ sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi) + ++ ATH11K_DEFAULT_NOISE_FLOOR; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); + } + + #if IS_ENABLED(CONFIG_IPV6) +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0265-ath11k-fix-missing-skb-drop-on-htc_tx_completion-err.patch b/package/kernel/mac80211/patches/ath11k/0265-ath11k-fix-missing-skb-drop-on-htc_tx_completion-err.patch new file mode 100644 index 00000000000000..7eca8f527e3aa5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0265-ath11k-fix-missing-skb-drop-on-htc_tx_completion-err.patch @@ -0,0 +1,41 @@ +From e5646fe3b7ef739c392e59da7db6adf5e1fdef42 Mon Sep 17 00:00:00 2001 +From: Christian 'Ansuel' Marangi +Date: Sat, 28 May 2022 16:25:16 +0200 +Subject: [PATCH] ath11k: fix missing skb drop on htc_tx_completion error + +On htc_tx_completion error the skb is not dropped. This is wrong since +the completion_handler logic expect the skb to be consumed anyway even +when an error is triggered. Not freeing the skb on error is a memory +leak since the skb won't be freed anywere else. Correctly free the +packet on eid >= ATH11K_HTC_EP_COUNT before returning. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1 + +Fixes: f951380a6022 ("ath11k: Disabling credit flow for WMI path") +Signed-off-by: Christian 'Ansuel' Marangi +Reviewed-by: Jeff Johnson +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220528142516.20819-2-ansuelsmth@gmail.com +--- + drivers/net/wireless/ath/ath11k/htc.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c +index 069c29a4fac7..ca3aedc0252d 100644 +--- a/drivers/net/wireless/ath/ath11k/htc.c ++++ b/drivers/net/wireless/ath/ath11k/htc.c +@@ -258,8 +258,10 @@ void ath11k_htc_tx_completion_handler(struct ath11k_base *ab, + u8 eid; + + eid = ATH11K_SKB_CB(skb)->eid; +- if (eid >= ATH11K_HTC_EP_COUNT) ++ if (eid >= ATH11K_HTC_EP_COUNT) { ++ dev_kfree_skb_any(skb); + return; ++ } + + ep = &htc->endpoint[eid]; + spin_lock_bh(&htc->tx_lock); +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0266-ath11k-Fix-incorrect-debug_mask-mappings.patch b/package/kernel/mac80211/patches/ath11k/0266-ath11k-Fix-incorrect-debug_mask-mappings.patch new file mode 100644 index 00000000000000..50ffab08fcc236 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0266-ath11k-Fix-incorrect-debug_mask-mappings.patch @@ -0,0 +1,40 @@ +From 9331f7d3c54a263bede5055e106e40b28d0bd937 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Thu, 2 Jun 2022 17:26:21 +0530 +Subject: [PATCH] ath11k: Fix incorrect debug_mask mappings + +Currently a couple of debug_mask entries are mapped to the same value, +this could enable unintended driver logging. If enabling DP_TX logs was +the intention, then this could also enable PCI logs flooding the dmesg +buffer or vice versa. Fix this by correctly assigning the debug masks. + +Found during code review. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Fixes: aa2092a9bab3f ("ath11k: add raw mode and software crypto support") +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220602115621.15339-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/debug.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h +index fbbd5fe02aa8..91545640c47b 100644 +--- a/drivers/net/wireless/ath/ath11k/debug.h ++++ b/drivers/net/wireless/ath/ath11k/debug.h +@@ -23,8 +23,8 @@ enum ath11k_debug_mask { + ATH11K_DBG_TESTMODE = 0x00000400, + ATH11k_DBG_HAL = 0x00000800, + ATH11K_DBG_PCI = 0x00001000, +- ATH11K_DBG_DP_TX = 0x00001000, +- ATH11K_DBG_DP_RX = 0x00002000, ++ ATH11K_DBG_DP_TX = 0x00002000, ++ ATH11K_DBG_DP_RX = 0x00004000, + ATH11K_DBG_ANY = 0xffffffff, + }; + +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0267-ath11k-Avoid-REO-CMD-failed-prints-during-firmware-r.patch b/package/kernel/mac80211/patches/ath11k/0267-ath11k-Avoid-REO-CMD-failed-prints-during-firmware-r.patch new file mode 100644 index 00000000000000..42024192671ec1 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0267-ath11k-Avoid-REO-CMD-failed-prints-during-firmware-r.patch @@ -0,0 +1,44 @@ +From 0ab52b2bd7be8fd49c8ade7703c1faa15359c6c5 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Thu, 2 Jun 2022 17:59:29 +0530 +Subject: [PATCH] ath11k: Avoid REO CMD failed prints during firmware recovery + +Currently when firmware recovery is in progress, we do not queue REO +commands to the firmware, instead -ESHUTDOWN will be returned to the +caller leading to a failure print on the console. The REO command in +the problem scenario is sent for all tids of a peer in which case we +will have 16 failure prints on the console for a single peer. For an +AP usecase, this count would be even higher in a worst case scenario. +Since these commands are bound to fail during firmware recovery, it +is better to avoid printing these failures and thereby avoid message +flooding on the console. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Fixes: 8ee8d38ca472 ("ath11k: Fix crash during firmware recovery on reo cmd ring access") +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220602122929.18896-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c +index e16beb96c37c..2148acf37071 100644 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -835,8 +835,9 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, + ath11k_dp_rx_tid_del_func); + if (ret) { +- ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", +- tid, ret); ++ if (ret != -ESHUTDOWN) ++ ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", ++ tid, ret); + dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0268-ath11k-Fix-LDPC-config-in-set_bitrate_mask-hook.patch b/package/kernel/mac80211/patches/ath11k/0268-ath11k-Fix-LDPC-config-in-set_bitrate_mask-hook.patch new file mode 100644 index 00000000000000..a5ac0a6e369fd9 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0268-ath11k-Fix-LDPC-config-in-set_bitrate_mask-hook.patch @@ -0,0 +1,72 @@ +From 4c1fc4f60a534728a34ed224281d17a33f2bf2bd Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Thu, 2 Jun 2022 18:11:42 +0530 +Subject: [PATCH] ath11k: Fix LDPC config in set_bitrate_mask hook + +Currently ar->ht_cap_info is not set anywhere in the code, this could +result in LDPC config computed wrongly in the set_bitrate_mask() hook +and eventually sending wrong config to the target. Fix this by checking +the LDPC support from the band HT capabilities of the radio instead. + +Furthermore, it looks like firmwares running on the ath11k hardware +do not set WMI_HT_CAP_LDPC for advertising the TX LDPC capability, +WMI_HT_CAP_TX_LDPC is set instead and this should be checked for +getting hardware's TX LDPC support. This is true for QCA6390, WCN6855 +& WCN6750 hardware. + +Also, remove unused variables ht_cap_info & vht_cap_info from +struct ath11k{}. + +Found this during code review. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220602124142.29909-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 2 -- + drivers/net/wireless/ath/ath11k/mac.c | 4 +++- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index ea31016437b0..2bd5eb9df4d4 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -582,8 +582,6 @@ struct ath11k { + struct ath11k_pdev_wmi *wmi; + struct ath11k_pdev_dp dp; + u8 mac_addr[ETH_ALEN]; +- u32 ht_cap_info; +- u32 vht_cap_info; + struct ath11k_he ar_he; + enum ath11k_state state; + bool supports_6ghz; +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index f11956163822..6b3d7a014847 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7800,6 +7800,7 @@ ath11k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, + { + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; ++ struct ath11k_pdev_cap *cap; + struct ath11k *ar = arvif->ar; + enum nl80211_band band; + const u8 *ht_mcs_mask; +@@ -7820,10 +7821,11 @@ ath11k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, + return -EPERM; + + band = def.chan->band; ++ cap = &ar->pdev->cap; + ht_mcs_mask = mask->control[band].ht_mcs; + vht_mcs_mask = mask->control[band].vht_mcs; + he_mcs_mask = mask->control[band].he_mcs; +- ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); ++ ldpc = !!(cap->band[band].ht_cap_info & WMI_HT_CAP_TX_LDPC); + + sgi = mask->control[band].gi; + if (sgi == NL80211_TXRATE_FORCE_LGI) +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/0269-ath11k-Fix-warnings-reported-by-checkpatch.patch b/package/kernel/mac80211/patches/ath11k/0269-ath11k-Fix-warnings-reported-by-checkpatch.patch new file mode 100644 index 00000000000000..4c59f1478e7e85 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0269-ath11k-Fix-warnings-reported-by-checkpatch.patch @@ -0,0 +1,97 @@ +From 3926e0c12238a8053ed5a598b5b84c6cf2adacab Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Thu, 2 Jun 2022 18:43:50 +0530 +Subject: [PATCH] ath11k: Fix warnings reported by checkpatch + +Fix warnings reported by checkpatch tool. Below are the errors fixed, + +drivers/net/wireless/ath/ath11k/hal_rx.c:760: 'recevied' may be misspelled - perhaps 'received? +drivers/net/wireless/ath/ath11k/qmi.c:2232: Prefer strscpy over strlcpy +drivers/net/wireless/ath/ath11k/qmi.c:2238: Prefer strscpy over strlcpy +drivers/net/wireless/ath/ath11k/qmi.c:2662: Prefer strscpy over strlcpy +drivers/net/wireless/ath/ath11k/mac.c:7836: 'atleast' may be misspelled - perhaps 'at least'? +drivers/net/wireless/ath/ath11k/wmi.c:6566: 'succeded' may be misspelled - perhaps 'succeeded'? + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220602131350.29486-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/hal_rx.c | 2 +- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + drivers/net/wireless/ath/ath11k/qmi.c | 6 +++--- + drivers/net/wireless/ath/ath11k/wmi.c | 2 +- + 4 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c +index 4bb1fbaed0c9..7f39c6fb7408 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -757,7 +757,7 @@ void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, + + /* TODO: HW queue descriptors are currently allocated for max BA + * window size for all QOS TIDs so that same descriptor can be used +- * later when ADDBA request is recevied. This should be changed to ++ * later when ADDBA request is received. This should be changed to + * allocate HW queue descriptors based on BA window size being + * negotiated (0 for non BA cases), and reallocate when BA window + * size changes and also send WMI message to FW to change the REO +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 6b3d7a014847..42d2e8cf8125 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7835,7 +7835,7 @@ ath11k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, + he_ltf = mask->control[band].he_ltf; + + /* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it +- * requires passing atleast one of used basic rates along with them. ++ * requires passing at least one of used basic rates along with them. + * Fixed rate setting across different preambles(legacy, HT, VHT) is + * not supported by the FW. Hence use of FIXED_RATE vdev param is not + * suitable for setting single HT/VHT rates. +diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c +index 61ead37a944a..00136601cb7d 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2229,13 +2229,13 @@ static int ath11k_qmi_request_target_cap(struct ath11k_base *ab) + + if (resp.fw_version_info_valid) { + ab->qmi.target.fw_version = resp.fw_version_info.fw_version; +- strlcpy(ab->qmi.target.fw_build_timestamp, ++ strscpy(ab->qmi.target.fw_build_timestamp, + resp.fw_version_info.fw_build_timestamp, + sizeof(ab->qmi.target.fw_build_timestamp)); + } + + if (resp.fw_build_id_valid) +- strlcpy(ab->qmi.target.fw_build_id, resp.fw_build_id, ++ strscpy(ab->qmi.target.fw_build_id, resp.fw_build_id, + sizeof(ab->qmi.target.fw_build_id)); + + if (resp.eeprom_read_timeout_valid) { +@@ -2659,7 +2659,7 @@ static int ath11k_qmi_wlanfw_wlan_cfg_send(struct ath11k_base *ab) + memset(&resp, 0, sizeof(resp)); + + req->host_version_valid = 1; +- strlcpy(req->host_version, ATH11K_HOST_VERSION_STRING, ++ strscpy(req->host_version, ATH11K_HOST_VERSION_STRING, + sizeof(req->host_version)); + + req->tgt_cfg_valid = 1; +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 84d1c7054013..f69918b9452a 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -6563,7 +6563,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk + + fallback: + /* Fallback to older reg (by sending previous country setting +- * again if fw has succeded and we failed to process here. ++ * again if fw has succeeded and we failed to process here. + * The Regdomain should be uniform across driver and fw. Since the + * FW has processed the command and sent a success status, we expect + * this function to succeed as well. If it doesn't, CTRY needs to be +-- +2.36.1 + diff --git a/package/kernel/mac80211/patches/ath11k/100-ath11k-load-appropriate-board-data-from-board-id.patch b/package/kernel/mac80211/patches/ath11k/100-ath11k-load-appropriate-board-data-from-board-id.patch new file mode 100644 index 00000000000000..01f67ca9771da2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/100-ath11k-load-appropriate-board-data-from-board-id.patch @@ -0,0 +1,47 @@ +From 89aec0a67ee30cd11762aede86b3edfdb2433663 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Thu, 2 Jul 2020 12:04:34 +0530 +Subject: [PATCH] ath11k: load appropriate board data based on board id + +This patch adds support to read board id from dts and load +appropriate board data. + +Adding the patch which was removed as a part of commit id - +Ib950b3271fede9ccf7d53fe9629c38ee729a0ef5 + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Lavanya Suresh +--- + drivers/net/wireless/ath/ath11k/qmi.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2009,9 +2009,11 @@ static int ath11k_qmi_assign_target_mem_ + + static int ath11k_qmi_request_target_cap(struct ath11k_base *ab) + { ++ struct device *dev = ab->dev; + struct qmi_wlanfw_cap_req_msg_v01 req; + struct qmi_wlanfw_cap_resp_msg_v01 resp; + struct qmi_txn txn; ++ unsigned int board_id; + int ret = 0; + int r; + char *fw_build_id; +@@ -2056,10 +2058,13 @@ static int ath11k_qmi_request_target_cap + ab->qmi.target.chip_family = resp.chip_info.chip_family; + } + +- if (resp.board_info_valid) ++ if (!of_property_read_u32(dev->of_node, "qcom,board_id", &board_id) && board_id != 0xFF) { ++ ab->qmi.target.board_id = board_id; ++ } else if (resp.board_info_valid) { + ab->qmi.target.board_id = resp.board_info.board_id; +- else ++ } else { + ab->qmi.target.board_id = 0xFF; ++ } + + if (resp.soc_info_valid) + ab->qmi.target.soc_id = resp.soc_info.soc_id; diff --git a/package/kernel/mac80211/patches/ath11k/101-ath11k-update-debugfs-support-for-mupltiple-radios-i.patch b/package/kernel/mac80211/patches/ath11k/101-ath11k-update-debugfs-support-for-mupltiple-radios-i.patch new file mode 100644 index 00000000000000..83c597ca7d2864 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/101-ath11k-update-debugfs-support-for-mupltiple-radios-i.patch @@ -0,0 +1,151 @@ +From a1e16842a097171cc544b45e343effe97eadb493 Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Wed, 30 Mar 2022 11:28:03 +0300 +Subject: ath11k: update debugfs support for mupltiple radios in PCI bus + +debugfs_ath11k struct is moved to ath11k_core, since its common +for both pci and ahb. + +Current ath11k_pci insmod fails if there are multiple PCI rdaios, + + ath11k_pci 0000:01:00.0: Hardware name qcn9074 hw1.0 + debugfs: Directory 'ath11k' with parent '/' already present! + ath11k_pci 0000:01:00.0: failed to create ath11k debugfs + ath11k_pci 0000:01:00.0: failed to create soc core: -17 + ath11k_pci 0000:01:00.0: failed to init core: -17 + ath11k_pci: probe of 0000:01:00.0 failed with error -17 + +To avoid the failure, debugfs directory is created with soc_name +and bus_id to allow creating debugfs directory for second PCI radio. + +with this Debugfs entries looks like, + # ls -l /sys/kernel/debug/ath11k/ + ipq8074 hw2.0 qcn9000 hw1.0_0000:01:00.0 qcn9000 hw1.0_0001:01:00.0 + + # ls -l /sys/kernel/debug/ath11k/ipq8074 hw2.0/ + mac0 mac1 simulate_fw_crash soc_dp_stats + + # ls -l /sys/kernel/debug/ath11k/qcn9000 hw1.0_0000:01:00.0 + mac0 simulate_fw_crash soc_dp_stats + + # /sys/kernel/debug/ath11k/qcn9000 hw1.0_0001:01:00.0: + mac0 simulate_fw_crash soc_dp_stats + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.4.0.1.r2-00012-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Anilkumar Kolli +Patchwork-Id: 12101611 +Signed-off-by: Kalle Valo +--- + drivers/net/wireless/ath/ath11k/debugfs.c | 58 +++++++++++++++++++++++++------ + 1 file changed, 48 insertions(+), 10 deletions(-) + +(limited to 'drivers/net/wireless/ath/ath11k/debugfs.c') + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -15,6 +15,8 @@ + #include "debugfs_htt_stats.h" + #include "peer.h" + ++struct dentry *debugfs_ath11k; ++ + static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = { + "REO2SW1_RING", + "REO2SW2_RING", +@@ -987,10 +989,6 @@ int ath11k_debugfs_pdev_create(struct at + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) + return 0; + +- ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k); +- if (IS_ERR(ab->debugfs_soc)) +- return PTR_ERR(ab->debugfs_soc); +- + debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, + &fops_simulate_fw_crash); + +@@ -1002,24 +1000,58 @@ int ath11k_debugfs_pdev_create(struct at + + void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab) + { +- debugfs_remove_recursive(ab->debugfs_soc); +- ab->debugfs_soc = NULL; + } + + int ath11k_debugfs_soc_create(struct ath11k_base *ab) + { +- ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL); ++ struct device *dev = ab->dev; ++ char soc_name[64] = {0}; ++ ++ if (!(IS_ERR_OR_NULL(ab->debugfs_soc))) ++ return 0; ++ ++ if (ab->hif.bus == ATH11K_BUS_AHB) { ++ snprintf(soc_name, sizeof(soc_name), "%s", ab->hw_params.name); ++ } else { ++ snprintf(soc_name, sizeof(soc_name), "%s_%s", ++ ab->hw_params.name, dev_name(dev)); ++ } + +- return PTR_ERR_OR_ZERO(ab->debugfs_ath11k); ++ ab->debugfs_soc = debugfs_create_dir(soc_name, debugfs_ath11k); ++ if (IS_ERR_OR_NULL(ab->debugfs_soc)) { ++ if (IS_ERR(ab->debugfs_soc)) ++ return PTR_ERR(ab->debugfs_soc); ++ return -ENOMEM; ++ } ++ ++ return 0; + } + + void ath11k_debugfs_soc_destroy(struct ath11k_base *ab) + { +- debugfs_remove_recursive(ab->debugfs_ath11k); +- ab->debugfs_ath11k = NULL; ++ debugfs_remove_recursive(ab->debugfs_soc); ++ ab->debugfs_soc = NULL; + } + EXPORT_SYMBOL(ath11k_debugfs_soc_destroy); + ++int ath11k_debugfs_create(void) ++{ ++ debugfs_ath11k = debugfs_create_dir("ath11k", NULL); ++ if (IS_ERR_OR_NULL(debugfs_ath11k)) { ++ if (IS_ERR(debugfs_ath11k)) ++ return PTR_ERR(debugfs_ath11k); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++void ath11k_debugfs_destroy(void) ++{ ++ debugfs_remove_recursive(debugfs_ath11k); ++ debugfs_ath11k = NULL; ++} ++ + void ath11k_debugfs_fw_stats_init(struct ath11k *ar) + { + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", +@@ -1388,6 +1420,9 @@ int ath11k_debugfs_register(struct ath11 + char pdev_name[5]; + char buf[100] = {0}; + ++ if (!(IS_ERR_OR_NULL(ar->debug.debugfs_pdev))) ++ return 0; ++ + snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); + + ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); +@@ -1448,6 +1483,9 @@ void ath11k_debugfs_unregister(struct at + kfree(dbr_debug); + ar->debug.dbr_debug[i] = NULL; + } ++ ++ debugfs_remove_recursive(ar->debug.debugfs_pdev); ++ ar->debug.debugfs_pdev = NULL; + } + + static ssize_t ath11k_write_twt_add_dialog(struct file *file, diff --git a/package/kernel/mac80211/patches/ath11k/102-ath11k-Enable-threaded-NAPI-on-some-radios.patch b/package/kernel/mac80211/patches/ath11k/102-ath11k-Enable-threaded-NAPI-on-some-radios.patch new file mode 100644 index 00000000000000..f1ca3dcd4ed7df --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/102-ath11k-Enable-threaded-NAPI-on-some-radios.patch @@ -0,0 +1,117 @@ +From 9a7f302b187dd845611ef4ee35b305bd078e59d6 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 15 Apr 2022 11:29:25 +0200 +Subject: [PATCH] ath11k: Enable threaded NAPI on some radios + +Enable threaded NAPI on IPQ8074 and QCN9074. + +Unlike traditional NAPI poll which runs in softirq context and on the +core which scheduled the NAPI, threaded NAPI makes use of kernel threads +which are under direct control of the scheduler and helps in balancing the +NAPI processing load across multiple CPUs, this helps in improving +throughput. + +In the case of IPQ8074 it increased the throughput at 80MHz by about +200Mbps. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01201-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Manikanta Pubbisetty +Signed-off-by: Robert Marko +--- + drivers/net/wireless/ath/ath11k/ahb.c | 2 ++ + drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/pcic.c | 2 ++ + 4 files changed, 11 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -304,6 +304,8 @@ static void ath11k_ahb_ext_irq_enable(st + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + if (!irq_grp->napi_enabled) { ++ if (ab->hw_params.threaded_napi) ++ dev_set_threaded(&irq_grp->napi_ndev, true); + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -111,6 +111,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .ce_window_idx = 0, + .fixed_fw_mem = false, + .support_off_channel_tx = false, ++ .threaded_napi = true, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -187,6 +188,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .ce_window_idx = 0, + .fixed_fw_mem = false, + .support_off_channel_tx = false, ++ .threaded_napi = false, + }, + { + .name = "qca6390 hw2.0", +@@ -262,6 +264,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .ce_window_idx = 0, + .fixed_fw_mem = false, + .support_off_channel_tx = true, ++ .threaded_napi = false, + }, + { + .name = "qcn9074 hw1.0", +@@ -337,6 +340,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .ce_window_idx = 2, + .fixed_fw_mem = false, + .support_off_channel_tx = false, ++ .threaded_napi = true, + }, + { + .name = "wcn6855 hw2.0", +@@ -412,6 +416,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .ce_window_idx = 0, + .fixed_fw_mem = false, + .support_off_channel_tx = true, ++ .threaded_napi = false, + }, + { + .name = "wcn6855 hw2.1", +@@ -486,6 +491,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .ce_window_idx = 0, + .fixed_fw_mem = false, + .support_off_channel_tx = true, ++ .threaded_napi = false, + }, + { + .name = "wcn6750 hw1.0", +@@ -560,6 +566,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .ce_window_idx = 2, + .fixed_fw_mem = true, + .support_off_channel_tx = false, ++ .threaded_napi = false, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -205,6 +205,7 @@ struct ath11k_hw_params { + u8 ce_window_idx; + bool fixed_fw_mem; + bool support_off_channel_tx; ++ bool threaded_napi; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -441,6 +441,8 @@ void ath11k_pcic_ext_irq_enable(struct a + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + if (!irq_grp->napi_enabled) { ++ if (ab->hw_params.threaded_napi) ++ dev_set_threaded(&irq_grp->napi_ndev, true); + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } diff --git a/package/kernel/mac80211/patches/ath11k/103-RFC-ath11k-fix-peer-addition-deletion-error-on-sta-band-migration.patch b/package/kernel/mac80211/patches/ath11k/103-RFC-ath11k-fix-peer-addition-deletion-error-on-sta-band-migration.patch new file mode 100644 index 00000000000000..a99dfab1a2cc49 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/103-RFC-ath11k-fix-peer-addition-deletion-error-on-sta-band-migration.patch @@ -0,0 +1,210 @@ +From patchwork Fri Jun 3 16:45:59 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Ansuel Smith +X-Patchwork-Id: 12869226 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id 84B21C433EF + for ; + Fri, 3 Jun 2022 16:46:27 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S1344074AbiFCQqZ (ORCPT + ); + Fri, 3 Jun 2022 12:46:25 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44126 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S244871AbiFCQqY (ORCPT + ); + Fri, 3 Jun 2022 12:46:24 -0400 +Received: from mail-ed1-x536.google.com (mail-ed1-x536.google.com + [IPv6:2a00:1450:4864:20::536]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9B551517F7 + for ; + Fri, 3 Jun 2022 09:46:22 -0700 (PDT) +Received: by mail-ed1-x536.google.com with SMTP id w27so10885628edl.7 + for ; + Fri, 03 Jun 2022 09:46:22 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; + h=from:to:cc:subject:date:message-id:mime-version + :content-transfer-encoding; + bh=Woceb0Rke0kHZAKQOxFNrONpwZyqKA30q2Uef9j1sWg=; + b=O+nr7Y4GlgXVxyiPhIS/JwPUvloVGcydyPndUaBGvyEf2pxef/YWw5Qqe6eac/4XzD + voUH4cvEFs8nGlotS6fFiDzMX+owWrmB6mgMEf8rr/JF6tEI3ZSO2Uuj5U8456CcjlQf + TYB0HwcKWQtGKWIa9ZOj28tDeWjuKEyFgs1jcaiIM9ZGaXLsQ/uXiVEq/0xl725fnCFj + XRb7p/OD3ZgjurbGyrbq7DvqmJWo08nfqupNBp8WGebWXEi8sg9fvBsDLxVpOOu2M4cS + QuFbo6lm+xP35t2tq4H6MEQUqLwYIn3x1YRRyw90Is6nTn4Z/Xx6O7bwNEtDnhqMl2Vs + lQDw== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; + h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version + :content-transfer-encoding; + bh=Woceb0Rke0kHZAKQOxFNrONpwZyqKA30q2Uef9j1sWg=; + b=NBKf3VB8uRQIiI/T/4Hk3VX7EtagxBI2SspjCiQ802whCCiWd0bSKC8jdb8dYw/Wkj + kATAOOM5eGfgNDAdOAyqVifJYCUiNIOQQZ2vOxELDijlIgcFUOuqNovxHO6Sr7lMk45I + Dgaf0Pil1rz+zjWSQ1u86rvCcqSnbaIM9jGntbtrPdgl20j8uvuviFiz8LtUlPkZ9NbF + f0G1B7tDNpam620FCWyzUN3Bn4LjN1ljTXXltwL3Y0MC70pAmdDKE0ePca9p3IynDajp + m6VxmzpRiAgpxYxuwLuy6l/YrwoU5Lx4lo3J2sXwgB3EX2zsidTFWHZ0QmhUBzlUAGOr + Vxjw== +X-Gm-Message-State: AOAM531rVg/yOuT7Qeebj85paatNB4Ra/sFrhcEOXnkPg7W1gcoB077r + JU5kxggE7Nam1E7i1ca7bDI= +X-Google-Smtp-Source: + ABdhPJwox2o3lpWBvFvHM5Z3hm3Ssg58lh8U+R8DMB58iiRmtt5SYLeE+KthNUIacPOMey6H2eiBrA== +X-Received: by 2002:a05:6402:20c:b0:42b:d302:a80f with SMTP id + t12-20020a056402020c00b0042bd302a80fmr11687092edv.330.1654274780902; + Fri, 03 Jun 2022 09:46:20 -0700 (PDT) +Received: from localhost.localdomain (93-42-70-190.ip85.fastwebnet.it. + [93.42.70.190]) + by smtp.googlemail.com with ESMTPSA id + f22-20020a1709067f9600b006f3ef214db6sm3010819ejr.28.2022.06.03.09.46.20 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Fri, 03 Jun 2022 09:46:20 -0700 (PDT) +From: Christian 'Ansuel' Marangi +To: ath11k@lists.infradead.org +Cc: linux-wireless@vger.kernel.org, + Christian 'Ansuel' Marangi +Subject: [RFC PATCH] ath11k: fix peer addition/deletion error on sta band + migration +Date: Fri, 3 Jun 2022 18:45:59 +0200 +Message-Id: <20220603164559.27769-1-ansuelsmth@gmail.com> +X-Mailer: git-send-email 2.36.1 +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +This patch try to fix the following error. + +Wed Jun 1 22:19:30 2022 kern.warn kernel: [ 119.561227] ath11k c000000.wifi: peer already added vdev id 0 req, vdev id 1 present +Wed Jun 1 22:19:30 2022 kern.warn kernel: [ 119.561282] ath11k c000000.wifi: Failed to add peer: 28:c2:1f:xx:xx:xx for VDEV: 0 +Wed Jun 1 22:19:30 2022 kern.warn kernel: [ 119.568053] ath11k c000000.wifi: Failed to add station: 28:c2:1f:xx:xx:xx for VDEV: 0 +Wed Jun 1 22:19:31 2022 daemon.notice hostapd: wlan2: STA 28:c2:1f:xx:xx:xx IEEE 802.11: Could not add STA to kernel driver +Wed Jun 1 22:19:31 2022 daemon.notice hostapd: wlan2: STA 28:c2:1f:xx:xx:xx IEEE 802.11: did not acknowledge authentication response +Wed Jun 1 22:19:31 2022 daemon.notice hostapd: wlan1: AP-STA-DISCONNECTED 28:c2:1f:xx:xx:xx +Wed Jun 1 22:19:31 2022 daemon.info hostapd: wlan1: STA 28:c2:1f:xx:xx:xx IEEE 802.11: disassociated due to inactivity +Wed Jun 1 22:19:32 2022 daemon.info hostapd: wlan1: STA 28:c2:1f:xx:xx:xx IEEE 802.11: deauthenticated due to inactivity (timer DEAUTH/REMOVE) + +To repro this: +- Have 2 Wifi with the same bssid and pass on different band (2.4 and +5GHz) +- Enable 802.11r Fast Transaction with same mobility domain +- FT Protocol: FT over the Air +From a openwrt system issue the command (with the correct mac) +ubus call hostapd.wlan1 wnm_disassoc_imminent '{"addr":"28:C2:1F:xx:xx:xx"}' +Notice the log printing the errors. + +The cause of this error has been investigated and we found that this is +related to the WiFi Fast Transaction feature. We observed that this is +triggered when the router tells the device to change band. In this case +the device first auth to the other band and then the disconnect path +from the prev band is triggered. +This is problematic with the current rhash implementation since the +addrs is used as key and the logic of "adding first, delete later" +conflicts with the rhash logic. +In fact peer addition will fail since the peer is already added and with +that fixed a peer deletion will cause unitended effect by removing the +peer just added. + +Current solution to this is to add additional logic to the peer delete, +make sure we are deleting the correct peer taken from the rhash +table (and fallback to the peer list) and for the peer add logic delete +the peer entry for the rhash list before adding the new one (counting as +an error only when a peer with the same vlan_id is asked to be added). + +With this change, a sta can correctly transition from 2.4GHz and 5GHZ +with no drop and no error are printed. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1 + +Fixes: 7b0c70d92a43 ("ath11k: Add peer rhash table support") +Signed-off-by: Christian 'Ansuel' Marangi +--- + +Some additional comments external to this patch. +I tried to find different way to fix this... +One of them would be mod the logic of the rhash and using as a key both +the vlan_id and the addr but this is problematic for the function +where ath11k_peer_find_by_addr is used as vlan_id is not always available. + +I honestly think a correct solution would be have a rhash list per vdev_id +or per mac_id but again this is problematic for some function that just handles +data and have only the addr as a way to identify the peer. + +So unless some change are done to the firmware to provide the vlan_id in the +msdu data this to me seems to be the only solution to correctly handle this case. + +Another solution I tried was to add to the peer some additional info and put +the rhash addition in the peer delete logic by passing the "to-be-added peer" to +the peer to delete but I notice that it's unreliable since it can happent that +the new peer hasn't been mapped at the time the peer delete is called. + +So this is really how to handle the rhash table in this corner case. +Considering how peer are handled in theory it should never happen to have +dangling peer that are not deleted. + +Hoping this is not too much of an hack and we find a good solution for this +problem. + + drivers/net/wireless/ath/ath11k/peer.c | 30 ++++++++++++++++++++++---- + 1 file changed, 26 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c +index 9e22aaf34b88..1ae7af02c364 100644 +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -302,6 +302,21 @@ static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr) + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_addr(ab, addr); ++ /* Check if the found peer is what we want to remove. ++ * While the sta is transitioning to another band we may ++ * have 2 peer with the same addr assigned to different ++ * vdev_id. Make sure we are deleting the correct peer. ++ */ ++ if (peer && peer->vdev_id == vdev_id) ++ ath11k_peer_rhash_delete(ab, peer); ++ ++ /* Fallback to peer list search if the correct peer can't be found. ++ * Skip the deletion of the peer from the rhash since it has already ++ * been deleted in peer add. ++ */ ++ if (!peer) ++ peer = ath11k_peer_find(ab, vdev_id, addr); ++ + if (!peer) { + spin_unlock_bh(&ab->base_lock); + mutex_unlock(&ab->tbl_mtx_lock); +@@ -312,8 +327,6 @@ static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr) + return -EINVAL; + } + +- ath11k_peer_rhash_delete(ab, peer); +- + spin_unlock_bh(&ab->base_lock); + mutex_unlock(&ab->tbl_mtx_lock); + +@@ -372,8 +385,17 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr); + if (peer) { +- spin_unlock_bh(&ar->ab->base_lock); +- return -EINVAL; ++ if (peer->vdev_id == param->vdev_id) { ++ spin_unlock_bh(&ar->ab->base_lock); ++ return -EINVAL; ++ } ++ ++ /* Assume sta is transitioning to another band. ++ * Remove here the peer from rhash. ++ */ ++ mutex_lock(&ar->ab->tbl_mtx_lock); ++ ath11k_peer_rhash_delete(ar->ab, peer); ++ mutex_unlock(&ar->ab->tbl_mtx_lock); + } + spin_unlock_bh(&ar->ab->base_lock); + diff --git a/package/kernel/mac80211/patches/ath11k/900-ath11k-Disable-coldboot-calibration-for-IPQ8074.patch b/package/kernel/mac80211/patches/ath11k/900-ath11k-Disable-coldboot-calibration-for-IPQ8074.patch new file mode 100644 index 00000000000000..5b4d4965525b20 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/900-ath11k-Disable-coldboot-calibration-for-IPQ8074.patch @@ -0,0 +1,24 @@ +From fee0e35d4e652243bfd8cf4c52e1706e0cc9b88d Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 16 Oct 2021 19:34:10 +0200 +Subject: [PATCH] ath11k: Disable coldboot calibration for IPQ8074 + +There is a bug with the remoteproc reset after coldboot calibration, +so until that is resolved disabled it to allow using the radio. + +Signed-off-by: Robert Marko +--- + drivers/net/wireless/ath/ath11k/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -83,7 +83,7 @@ static const struct ath11k_hw_params ath + .supports_shadow_regs = false, + .idle_ps = false, + .supports_sta_ps = false, +- .cold_boot_calib = true, ++ .cold_boot_calib = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, diff --git a/package/kernel/mac80211/patches/ath11k/901-ath11k-ipq8074-support-512MB-memory-profile.patch b/package/kernel/mac80211/patches/ath11k/901-ath11k-ipq8074-support-512MB-memory-profile.patch new file mode 100644 index 00000000000000..589d5acce548f9 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/901-ath11k-ipq8074-support-512MB-memory-profile.patch @@ -0,0 +1,63 @@ +From 20ad5a47dd5093a8eb934a64f398d16d4952de91 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 15 Dec 2021 19:44:52 +0100 +Subject: [PATCH] ath11k: ipq8074: support 512MB memory profile + +ath11k is really memory intensive for devices with less that 1GB of ram, +so lets port the QSDK patch that adds a profile for devices with 512MB +of RAM. + +Signed-off-by: Csaba Sipos +Signed-off-by: Robert Marko +--- + drivers/net/wireless/ath/ath11k/Kconfig | 12 ++ + drivers/net/wireless/ath/ath11k/core.c | 6 + + local-symbols | 2 + + 4 files changed, 176 insertions(+) +--- a/drivers/net/wireless/ath/ath11k/Kconfig ++++ b/drivers/net/wireless/ath/ath11k/Kconfig +@@ -61,3 +61,15 @@ config ATH11K_SPECTRAL + Enable ath11k spectral scan support + + Say Y to enable access to the FFT/spectral data via debugfs. ++ ++config ATH11K_MEM_PROFILE_512MB ++ bool "Atheros ath11k 512MB memory profile" ++ depends on ATH11K ++ help ++ Use limits for the 512MB memory size instead of 1GB. ++ ++config ATH11K_MEM_PROFILE_1GB ++ bool "Atheros ath11k 1GB memory profile" ++ depends on ATH11K ++ help ++ Use limits for the 1GB memory size. +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -84,9 +84,15 @@ static const struct ath11k_hw_params ath + .idle_ps = false, + .supports_sta_ps = false, + .cold_boot_calib = false, ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_1GB + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, ++#elif CPTCFG_ATH11K_MEM_PROFILE_512MB ++ .fw_mem_mode = 1, ++ .num_vdevs = 8, ++ .num_peers = 128, ++#endif + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .supports_regdb = false, +--- a/local-symbols ++++ b/local-symbols +@@ -155,6 +155,8 @@ ATH11K_DEBUG= + ATH11K_DEBUGFS= + ATH11K_TRACING= + ATH11K_SPECTRAL= ++ATH11K_MEM_PROFILE_512MB= ++ATH11K_MEM_PROFILE_1GB= + WLAN_VENDOR_ATMEL= + ATMEL= + PCI_ATMEL= diff --git a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch index afe8cea2057bf8..ae329b9327ffa8 100644 --- a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch +++ b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch @@ -1,6 +1,6 @@ --- a/local-symbols +++ b/local-symbols -@@ -322,6 +322,7 @@ RT2X00_LIB_FIRMWARE= +@@ -324,6 +324,7 @@ RT2X00_LIB_FIRMWARE= RT2X00_LIB_CRYPTO= RT2X00_LIB_LEDS= RT2X00_LIB_DEBUGFS= diff --git a/package/kernel/mac80211/patches/subsys/0001-v5.16-ieee80211-Add-new-A-MPDU-factor-macro-for-HE-6-GHz-p.patch b/package/kernel/mac80211/patches/subsys/0001-v5.16-ieee80211-Add-new-A-MPDU-factor-macro-for-HE-6-GHz-p.patch new file mode 100644 index 00000000000000..3092d9c47863a8 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/0001-v5.16-ieee80211-Add-new-A-MPDU-factor-macro-for-HE-6-GHz-p.patch @@ -0,0 +1,27 @@ +From 62b8963cd84df1fc04986cd9b27586acce758f36 Mon Sep 17 00:00:00 2001 +From: Pradeep Kumar Chitrapu +Date: Tue, 28 Sep 2021 14:00:45 +0300 +Subject: [PATCH] ieee80211: Add new A-MPDU factor macro for HE 6 GHz peer caps + +Add IEEE80211_HE_6GHZ_MAX_AMPDU_FACTOR as per IEEE Std 802.11ax-2021, +9.4.2.263 to use for peer max A-MPDU factor in 6 GHz band. + +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Jouni Malinen +Acked-by: Johannes Berg +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20210913175510.193005-1-jouni@codeaurora.org +--- + include/linux/ieee80211.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/linux/ieee80211.h ++++ b/include/linux/ieee80211.h +@@ -2084,6 +2084,7 @@ int ieee80211_get_vht_max_nss(struct iee + + #define IEEE80211_HE_VHT_MAX_AMPDU_FACTOR 20 + #define IEEE80211_HE_HT_MAX_AMPDU_FACTOR 16 ++#define IEEE80211_HE_6GHZ_MAX_AMPDU_FACTOR 13 + + /* 802.11ax HE PHY capabilities */ + #define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G 0x02 diff --git a/package/kernel/mac80211/patches/subsys/0002-v5.18-ieee80211-add-EHT-1K-aggregation-definitions.patch b/package/kernel/mac80211/patches/subsys/0002-v5.18-ieee80211-add-EHT-1K-aggregation-definitions.patch new file mode 100644 index 00000000000000..f7c62b102c410a --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/0002-v5.18-ieee80211-add-EHT-1K-aggregation-definitions.patch @@ -0,0 +1,99 @@ +From 2a2c86f15e17c5013b9897b67d895e64a25ae3cb Mon Sep 17 00:00:00 2001 +From: Mordechay Goodstein +Date: Mon, 14 Feb 2022 17:29:52 +0100 +Subject: [PATCH] ieee80211: add EHT 1K aggregation definitions + +We add the fields for parsing extended ADDBA request/respond, +and new max 1K aggregation for limit ADDBA request/respond. + +Adjust drivers to use the proper macro, IEEE80211_MAX_AMPDU_BUF -> +IEEE80211_MAX_AMPDU_BUF_HE. + +Signed-off-by: Mordechay Goodstein +Link: https://lore.kernel.org/r/20220214173004.b8b447ce95b7.I0ee2554c94e89abc7a752b0f7cc7fd79c273efea@changeid +Signed-off-by: Johannes Berg +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 +- + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 ++-- + drivers/net/wireless/mediatek/mt76/mt7915/init.c | 4 ++-- + include/linux/ieee80211.h | 6 +++++- + net/mac80211/agg-rx.c | 2 +- + 5 files changed, 11 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6555,7 +6555,7 @@ static int __ath11k_mac_register(struct + ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; + ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; +- ar->hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; ++ ar->hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + + ar->hw->vif_data_size = sizeof(struct ath11k_vif); + ar->hw->sta_data_size = sizeof(struct ath11k_sta); +--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c ++++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +@@ -761,12 +761,12 @@ iwl_op_mode_mvm_start(struct iwl_trans * + if (!hw) + return NULL; + +- hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; ++ hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + + if (cfg->max_tx_agg_size) + hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; + else +- hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; ++ hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + + op_mode = hw->priv; + +--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c ++++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c +@@ -217,8 +217,8 @@ mt7915_init_wiphy(struct ieee80211_hw *h + struct wiphy *wiphy = hw->wiphy; + + hw->queues = 4; +- hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; +- hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; ++ hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; ++ hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->netdev_features = NETIF_F_RXCSUM; + + hw->radiotap_timestamp.units_pos = +--- a/include/linux/ieee80211.h ++++ b/include/linux/ieee80211.h +@@ -1023,6 +1023,8 @@ struct ieee80211_tpc_report_ie { + #define IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK GENMASK(2, 1) + #define IEEE80211_ADDBA_EXT_FRAG_LEVEL_SHIFT 1 + #define IEEE80211_ADDBA_EXT_NO_FRAG BIT(0) ++#define IEEE80211_ADDBA_EXT_BUF_SIZE_MASK GENMASK(7, 5) ++#define IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT 10 + + struct ieee80211_addba_ext_ie { + u8 data; +@@ -1697,10 +1699,12 @@ struct ieee80211_ht_operation { + * A-MPDU buffer sizes + * According to HT size varies from 8 to 64 frames + * HE adds the ability to have up to 256 frames. ++ * EHT adds the ability to have up to 1K frames. + */ + #define IEEE80211_MIN_AMPDU_BUF 0x8 + #define IEEE80211_MAX_AMPDU_BUF_HT 0x40 +-#define IEEE80211_MAX_AMPDU_BUF 0x100 ++#define IEEE80211_MAX_AMPDU_BUF_HE 0x100 ++#define IEEE80211_MAX_AMPDU_BUF_EHT 0x400 + + + /* Spatial Multiplexing Power Save Modes (for capability) */ +--- a/net/mac80211/agg-rx.c ++++ b/net/mac80211/agg-rx.c +@@ -310,7 +310,7 @@ void ___ieee80211_start_rx_ba_session(st + } + + if (sta->sta.he_cap.has_he) +- max_buf_size = IEEE80211_MAX_AMPDU_BUF; ++ max_buf_size = IEEE80211_MAX_AMPDU_BUF_HE; + else + max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT; + diff --git a/target/linux/ipq807x/Makefile b/target/linux/ipq807x/Makefile new file mode 100644 index 00000000000000..cbece5c67f41e2 --- /dev/null +++ b/target/linux/ipq807x/Makefile @@ -0,0 +1,20 @@ +include $(TOPDIR)/rules.mk + +ARCH:=aarch64 +BOARD:=ipq807x +BOARDNAME:=Qualcomm Atheros IPQ807x +FEATURES:=squashfs ramdisk fpu nand rtc emmc +KERNELNAME:=Image dtbs +CPU_TYPE:=cortex-a53 +SUBTARGETS:=generic + +KERNEL_PATCHVER:=5.15 + +include $(INCLUDE_DIR)/target.mk +DEFAULT_PACKAGES += \ + kmod-usb3 kmod-usb-dwc3 kmod-usb-dwc3-qcom \ + kmod-leds-gpio kmod-gpio-button-hotplug \ + ath11k-firmware-ipq8074 kmod-ath11k-ahb \ + wpad-basic-wolfssl uboot-envtools + +$(eval $(call BuildTarget)) diff --git a/target/linux/ipq807x/base-files/etc/board.d/01_leds b/target/linux/ipq807x/base-files/etc/board.d/01_leds new file mode 100644 index 00000000000000..d48dcfa385fd18 --- /dev/null +++ b/target/linux/ipq807x/base-files/etc/board.d/01_leds @@ -0,0 +1,28 @@ + +. /lib/functions/uci-defaults.sh + +board_config_update + +board=$(board_name) + +case "$board" in +edgecore,eap102) + ucidef_set_led_netdev "wan" "WAN" "green:wanpoe" "eth0" + ;; +redmi,ax6|\ +xiaomi,ax3600) + ucidef_set_led_netdev "wan" "WAN" "blue:network" "eth0" + ;; +qnap,301w) + ucidef_set_led_netdev "lan1" "LAN1" "green:lan1" "eth0" + ucidef_set_led_netdev "lan2" "LAN2" "green:lan2" "eth1" + ucidef_set_led_netdev "lan3" "LAN3" "green:lan3" "eth2" + ucidef_set_led_netdev "lan4" "LAN4" "green:lan4" "eth3" + ucidef_set_led_netdev "10G_1" "10G_1" "green:10g_1" "eth4" + ucidef_set_led_netdev "10G_2" "10G_2" "green:10g_2" "eth5" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/ipq807x/base-files/etc/board.d/02_network b/target/linux/ipq807x/base-files/etc/board.d/02_network new file mode 100644 index 00000000000000..aa0e341b61ea45 --- /dev/null +++ b/target/linux/ipq807x/base-files/etc/board.d/02_network @@ -0,0 +1,41 @@ +# +# Copyright (c) 2015 The Linux Foundation. All rights reserved. +# Copyright (c) 2011-2015 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +ipq807x_setup_interfaces() +{ + local board="$1" + + case "$board" in + edgecore,eap102) + ucidef_set_interfaces_lan_wan "eth1" "eth0" + ;; + edimax,cax1800) + ucidef_set_interfaces_lan_wan "eth0" + ;; + qnap,301w) + ucidef_set_interfaces_lan_wan "eth0 eth1 eth2 eth3 eth4" "eth5" + ;; + redmi,ax6|\ + xiaomi,ax3600) + ucidef_set_interfaces_lan_wan "eth1 eth2 eth3" "eth0" + ;; + xiaomi,ax9000) + ucidef_set_interfaces_lan_wan "eth0 eth1 eth2 eth3" "eth4" + ;; + *) + echo "Unsupported hardware. Network interfaces not initialized" + ;; + esac +} + +board_config_update +board=$(board_name) +ipq807x_setup_interfaces $board +board_config_flush + +exit 0 diff --git a/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata new file mode 100644 index 00000000000000..e5daf2dbab44ae --- /dev/null +++ b/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata @@ -0,0 +1,20 @@ +#!/bin/sh + +[ -e /lib/firmware/$FIRMWARE ] && exit 0 + +. /lib/functions/caldata.sh + +board=$(board_name) + +case "$FIRMWARE" in +"ath10k/cal-pci-0000:01:00.0.bin") + case "$board" in + xiaomi,ax3600) + caldata_extract "0:art" 0x33000 0x844 + ;; + esac + ;; +*) + exit 1 + ;; +esac diff --git a/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata b/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata new file mode 100644 index 00000000000000..a2290ac124aa08 --- /dev/null +++ b/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata @@ -0,0 +1,32 @@ +#!/bin/sh + +[ -e /lib/firmware/$FIRMWARE ] && exit 0 + +. /lib/functions/caldata.sh + +board=$(board_name) + +case "$FIRMWARE" in +"ath11k/IPQ8074/hw2.0/cal-ahb-c000000.wifi.bin") + case "$board" in + edgecore,eap102|\ + edimax,cax1800|\ + qnap,301w|\ + redmi,ax6|\ + xiaomi,ax3600|\ + xiaomi,ax9000) + caldata_extract "0:art" 0x1000 0x20000 + ;; + esac + ;; +"ath11k/QCN9074/hw1.0/cal-pci-0000:01:00.0.bin") + case "$board" in + xiaomi,ax9000) + caldata_extract "0:art" 0x26800 0x20000 + ;; + esac + ;; +*) + exit 1 + ;; +esac diff --git a/target/linux/ipq807x/base-files/etc/init.d/bootcount b/target/linux/ipq807x/base-files/etc/init.d/bootcount new file mode 100755 index 00000000000000..6d5f3abf914720 --- /dev/null +++ b/target/linux/ipq807x/base-files/etc/init.d/bootcount @@ -0,0 +1,21 @@ +#!/bin/sh /etc/rc.common + +START=99 + +boot() { + case $(board_name) in + edgecore,eap102) + fw_setenv upgrade_available 0 + # Unset changed flag after sysupgrade complete + fw_setenv changed + ;; + redmi,ax6|\ + xiaomi,ax3600|\ + xiaomi,ax9000) + # OTA handling should not be used. Reset it just in case. + fw_setenv flag_ota_reboot 0 + # Not strictly needed but useful to handle partition crash condition + fw_setenv flag_boot_success 1 + ;; + esac +} diff --git a/target/linux/ipq807x/base-files/lib/upgrade/mmc.sh b/target/linux/ipq807x/base-files/lib/upgrade/mmc.sh new file mode 100644 index 00000000000000..dac9ddd5686544 --- /dev/null +++ b/target/linux/ipq807x/base-files/lib/upgrade/mmc.sh @@ -0,0 +1,83 @@ +# +# Copyright (C) 2016 lede-project.org +# + +# this can be used as a generic mmc upgrade script +# just add a device entry in platform.sh, +# define "kernelname" and "rootfsname" and call mmc_do_upgrade +# after the kernel and rootfs flash a loopdev (as overlay) is +# setup on top of the rootfs partition +# for the proper function a padded rootfs image is needed, basically +# append "pad-to 64k" to the image definition +# this is based on the ipq806x zyxel.sh mmc upgrade + +. /lib/functions.sh + +mmc_do_upgrade() { + local tar_file="$1" + local rootfs= + local kernel= + + [ -z "$kernel" ] && kernel=$(find_mmc_part ${kernelname}) + [ -z "$rootfs" ] && rootfs=$(find_mmc_part ${rootfsname}) + + [ -z "$kernel" ] && echo "Upgrade failed: kernel partition not found! Rebooting..." && reboot -f + [ -z "$rootfs" ] && echo "Upgrade failed: rootfs partition not found! Rebooting..." && reboot -f + + mmc_do_flash $tar_file $kernel $rootfs + + return 0 +} + +mmc_do_flash() { + local tar_file=$1 + local kernel=$2 + local rootfs=$3 + + # keep sure its unbound + losetup --detach-all || { + echo Failed to detach all loop devices. Skip this try. + reboot -f + } + + # use the first found directory in the tar archive + local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') + board_dir=${board_dir%/} + + echo "flashing kernel to $kernel" + tar xf $tar_file ${board_dir}/kernel -O >$kernel + + echo "flashing rootfs to ${rootfs}" + tar xf $tar_file ${board_dir}/root -O >"${rootfs}" + + # a padded rootfs is needed for overlay fs creation + local offset=$(tar xf $tar_file ${board_dir}/root -O | wc -c) + [ $offset -lt 65536 ] && { + echo Wrong size for rootfs: $offset + sleep 10 + reboot -f + } + + # Mount loop for rootfs_data + local loopdev="$(losetup -f)" + losetup -o $offset $loopdev $rootfs || { + echo "Failed to mount looped rootfs_data." + sleep 10 + reboot -f + } + + echo "Format new rootfs_data at position ${offset}." + mkfs.ext4 -F -L rootfs_data $loopdev + mkdir /tmp/new_root + mount -t ext4 $loopdev /tmp/new_root && { + echo "Saving config to rootfs_data at position ${offset}." + cp -v "$UPGRADE_BACKUP" "/tmp/new_root/$BACKUP_FILE" + umount /tmp/new_root + } + + # Cleanup + losetup -d $loopdev >/dev/null 2>&1 + sync + umount -a + reboot -f +} diff --git a/target/linux/ipq807x/base-files/lib/upgrade/platform.sh b/target/linux/ipq807x/base-files/lib/upgrade/platform.sh new file mode 100644 index 00000000000000..a2b653c8195340 --- /dev/null +++ b/target/linux/ipq807x/base-files/lib/upgrade/platform.sh @@ -0,0 +1,65 @@ +PART_NAME=firmware +REQUIRE_IMAGE_METADATA=1 + +RAMFS_COPY_BIN='fw_printenv fw_setenv' +RAMFS_COPY_DATA='/etc/fw_env.config /var/lock/fw_printenv.lock' + +platform_check_image() { + return 0; +} + +platform_do_upgrade() { + case "$(board_name)" in + edgecore,eap102) + active="$(fw_printenv -n active)" + if [ "$active" -eq "1" ]; then + CI_UBIPART="rootfs2" + else + CI_UBIPART="rootfs1" + fi + # force altbootcmd which handles partition change in u-boot + fw_setenv bootcount 3 + fw_setenv upgrade_available 1 + nand_do_upgrade "$1" + ;; + edimax,cax1800) + nand_do_upgrade "$1" + ;; + qnap,301w) + kernelname="0:HLOS" + rootfsname="rootfs" + mmc_do_upgrade "$1" + ;; + redmi,ax6|\ + xiaomi,ax3600|\ + xiaomi,ax9000) + part_num="$(fw_printenv -n flag_boot_rootfs)" + if [ "$part_num" -eq "0" ]; then + CI_UBIPART="rootfs_1" + target_num=1 + # Reset fail flag for the current partition + # With both partition set to fail, the partition 2 (bit 1) + # is loaded + fw_setenv flag_try_sys2_failed 0 + else + CI_UBIPART="rootfs" + target_num=0 + # Reset fail flag for the current partition + # or uboot will skip the loading of this partition + fw_setenv flag_try_sys1_failed 0 + fi + + # Tell uboot to switch partition + fw_setenv flag_boot_rootfs $target_num + fw_setenv flag_last_success $target_num + + # Reset success flag + fw_setenv flag_boot_success 0 + + nand_do_upgrade "$1" + ;; + *) + default_do_upgrade "$1" + ;; + esac +} diff --git a/target/linux/ipq807x/config-5.15 b/target/linux/ipq807x/config-5.15 new file mode 100644 index 00000000000000..0b7afe610c6db3 --- /dev/null +++ b/target/linux/ipq807x/config-5.15 @@ -0,0 +1,508 @@ +CONFIG_64BIT=y +# CONFIG_APQ_GCC_8084 is not set +# CONFIG_APQ_MMCC_8084 is not set +CONFIG_AQUANTIA_PHY=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_CRYPTO=y +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_PTR_AUTH_KERNEL=y +CONFIG_ARM64_SVE=y +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +# CONFIG_ARM_MHU_V2 is not set +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +CONFIG_ARM_PSCI_FW=y +# CONFIG_ARM_QCOM_CPUFREQ_HW is not set +# CONFIG_ARM_QCOM_CPUFREQ_NVMEM is not set +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_BLK_PM=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_QCOM=y +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_COREDUMP=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_THERMAL=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRC16=y +CONFIG_CRC8=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DEV_QCE_AEAD=y +# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set +CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y +# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set +# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set +CONFIG_CRYPTO_DEV_QCE_SHA=y +CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y +CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 +CONFIG_CRYPTO_DEV_QCOM_RNG=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEV_COREDUMP=y +CONFIG_DMADEVICES=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DTC=y +CONFIG_DT_IDLE_STATES=y +CONFIG_EDAC_SUPPORT=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FRAME_POINTER=y +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y +# CONFIG_I2C_QCOM_CCI is not set +CONFIG_I2C_QUP=y +CONFIG_IIO=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_IPQ_APSS_6018 is not set +CONFIG_IPQ_APSS_8074=y +# CONFIG_IPQ_APSS_PLL is not set +# CONFIG_IPQ_GCC_4019 is not set +# CONFIG_IPQ_GCC_6018 is not set +# CONFIG_IPQ_GCC_806X is not set +CONFIG_IPQ_GCC_8074=y +# CONFIG_IPQ_LCC_806X is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +# CONFIG_KPSS_XCC is not set +CONFIG_LIBFDT=y +CONFIG_LIB_MEMNEQ=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MAILBOX=y +# CONFIG_MAILBOX_TEST is not set +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MDIO_IPQ4019=y +# CONFIG_MDM_GCC_9615 is not set +# CONFIG_MDM_LCC_9615 is not set +CONFIG_MEMFD_CREATE=y +# CONFIG_MFD_HI6421_SPMI is not set +# CONFIG_MFD_QCOM_RPM is not set +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_CQHCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +CONFIG_MMC_SDHCI_MSM=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MODULES_USE_ELF_RELA=y +# CONFIG_MSM_GCC_8660 is not set +# CONFIG_MSM_GCC_8916 is not set +# CONFIG_MSM_GCC_8939 is not set +# CONFIG_MSM_GCC_8960 is not set +# CONFIG_MSM_GCC_8974 is not set +# CONFIG_MSM_GCC_8994 is not set +# CONFIG_MSM_GCC_8996 is not set +# CONFIG_MSM_GCC_8998 is not set +# CONFIG_MSM_GPUCC_8998 is not set +# CONFIG_MSM_LCC_8960 is not set +# CONFIG_MSM_MMCC_8960 is not set +# CONFIG_MSM_MMCC_8974 is not set +# CONFIG_MSM_MMCC_8996 is not set +# CONFIG_MSM_MMCC_8998 is not set +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +CONFIG_MTD_NAND_QCOM=y +CONFIG_MTD_QCOMSMEM_PARTS=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NLS=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y +CONFIG_NVMEM=y +# CONFIG_NVMEM_SPMI_SDAM is not set +CONFIG_NVMEM_SYSFS=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_PADATA=y +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_PERFORMANCE is not set +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_DW_HOST=y +CONFIG_PCIE_PME=y +CONFIG_PCIE_QCOM=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYS_ADDR_T_64BIT=y +# CONFIG_PHY_QCOM_APQ8064_SATA is not set +# CONFIG_PHY_QCOM_IPQ4019_USB is not set +# CONFIG_PHY_QCOM_IPQ806X_SATA is not set +# CONFIG_PHY_QCOM_IPQ806X_USB is not set +# CONFIG_PHY_QCOM_PCIE2 is not set +CONFIG_PHY_QCOM_QMP=y +CONFIG_PHY_QCOM_QUSB2=y +# CONFIG_PHY_QCOM_USB_HS_28NM is not set +# CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2 is not set +# CONFIG_PHY_QCOM_USB_SS is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_APQ8064 is not set +# CONFIG_PINCTRL_APQ8084 is not set +# CONFIG_PINCTRL_IPQ4019 is not set +# CONFIG_PINCTRL_IPQ6018 is not set +# CONFIG_PINCTRL_IPQ8064 is not set +CONFIG_PINCTRL_IPQ8074=y +# CONFIG_PINCTRL_MDM9615 is not set +CONFIG_PINCTRL_MSM=y +# CONFIG_PINCTRL_MSM8226 is not set +# CONFIG_PINCTRL_MSM8660 is not set +# CONFIG_PINCTRL_MSM8916 is not set +# CONFIG_PINCTRL_MSM8960 is not set +# CONFIG_PINCTRL_MSM8976 is not set +# CONFIG_PINCTRL_MSM8994 is not set +# CONFIG_PINCTRL_MSM8996 is not set +# CONFIG_PINCTRL_MSM8998 is not set +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set +# CONFIG_PINCTRL_QCS404 is not set +# CONFIG_PINCTRL_SC7180 is not set +# CONFIG_PINCTRL_SDM660 is not set +# CONFIG_PINCTRL_SDM845 is not set +# CONFIG_PINCTRL_SM8150 is not set +# CONFIG_PINCTRL_SM8250 is not set +CONFIG_PM=y +# CONFIG_PM8916_WATCHDOG is not set +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_MSM is not set +# CONFIG_POWER_RESET_QCOM_PON is not set +CONFIG_POWER_SUPPLY=y +CONFIG_PRINTK_TIME=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +# CONFIG_QCOM_A53PLL is not set +# CONFIG_QCOM_AOSS_QMP is not set +CONFIG_QCOM_APCS_IPC=y +CONFIG_QCOM_APM=y +# CONFIG_QCOM_APR is not set +CONFIG_QCOM_BAM_DMA=y +# CONFIG_QCOM_CLK_APCC_MSM8996 is not set +# CONFIG_QCOM_CLK_APCS_MSM8916 is not set +# CONFIG_QCOM_CLK_APCS_SDX55 is not set +# CONFIG_QCOM_COINCELL is not set +# CONFIG_QCOM_COMMAND_DB is not set +# CONFIG_QCOM_CPR is not set +# CONFIG_QCOM_EBI2 is not set +# CONFIG_QCOM_FASTRPC is not set +CONFIG_QCOM_GDSC=y +# CONFIG_QCOM_GENI_SE is not set +# CONFIG_QCOM_GSBI is not set +# CONFIG_QCOM_HFPLL is not set +# CONFIG_QCOM_IPCC is not set +# CONFIG_QCOM_LLCC is not set +CONFIG_QCOM_MDT_LOADER=y +# CONFIG_QCOM_OCMEM is not set +# CONFIG_QCOM_PDC is not set +CONFIG_QCOM_PIL_INFO=y +# CONFIG_QCOM_Q6V5_ADSP is not set +CONFIG_QCOM_Q6V5_COMMON=y +# CONFIG_QCOM_Q6V5_MSS is not set +# CONFIG_QCOM_Q6V5_PAS is not set +CONFIG_QCOM_Q6V5_WCSS=y +CONFIG_QCOM_QFPROM=y +# CONFIG_QCOM_RMTFS_MEM is not set +# CONFIG_QCOM_RPMH is not set +CONFIG_QCOM_RPROC_COMMON=y +CONFIG_QCOM_SCM=y +# CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT is not set +# CONFIG_QCOM_SMD_RPM is not set +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_SMEM_STATE=y +CONFIG_QCOM_SMP2P=y +# CONFIG_QCOM_SMSM is not set +CONFIG_QCOM_SOCINFO=y +CONFIG_QCOM_SPMI_ADC5=y +# CONFIG_QCOM_SYSMON is not set +CONFIG_QCOM_TSENS=y +CONFIG_QCOM_VADC_COMMON=y +# CONFIG_QCOM_WCNSS_CTRL is not set +# CONFIG_QCOM_WCNSS_PIL is not set +CONFIG_QCOM_WDT=y +# CONFIG_QCS_GCC_404 is not set +# CONFIG_QCS_Q6SSTOP_404 is not set +# CONFIG_QCS_TURING_404 is not set +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_SPMI=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_CPR3=y +# CONFIG_REGULATOR_CPR3_NPU is not set +CONFIG_REGULATOR_CPR4_APSS=y +# CONFIG_REGULATOR_QCOM_LABIBB is not set +CONFIG_REGULATOR_QCOM_SPMI=y +# CONFIG_REGULATOR_QCOM_USB_VBUS is not set +# CONFIG_REGULATOR_VQMMC_IPQ4019 is not set +CONFIG_RELOCATABLE=y +CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y +CONFIG_RESET_CONTROLLER=y +# CONFIG_RESET_QCOM_AOSS is not set +# CONFIG_RESET_QCOM_PDC is not set +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPMSG=y +CONFIG_RPMSG_CHAR=y +# CONFIG_RPMSG_NS is not set +CONFIG_RPMSG_QCOM_GLINK=y +CONFIG_RPMSG_QCOM_GLINK_RPM=y +CONFIG_RPMSG_QCOM_GLINK_SMEM=y +CONFIG_RPMSG_QCOM_SMD=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +# CONFIG_SCHED_CORE is not set +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_SCHED_THERMAL_PRESSURE=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_PROC_FS is not set +# CONFIG_SC_DISPCC_7180 is not set +# CONFIG_SC_GCC_7180 is not set +# CONFIG_SC_GPUCC_7180 is not set +# CONFIG_SC_LPASS_CORECC_7180 is not set +# CONFIG_SC_MSS_7180 is not set +# CONFIG_SC_VIDEOCC_7180 is not set +# CONFIG_SDM_CAMCC_845 is not set +# CONFIG_SDM_DISPCC_845 is not set +# CONFIG_SDM_GCC_660 is not set +# CONFIG_SDM_GCC_845 is not set +# CONFIG_SDM_GPUCC_845 is not set +# CONFIG_SDM_LPASSCC_845 is not set +# CONFIG_SDM_VIDEOCC_845 is not set +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +# CONFIG_SM_GCC_8150 is not set +# CONFIG_SM_GCC_8250 is not set +# CONFIG_SM_GPUCC_8150 is not set +# CONFIG_SM_GPUCC_8250 is not set +# CONFIG_SM_VIDEOCC_8150 is not set +# CONFIG_SM_VIDEOCC_8250 is not set +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BUS=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_QUP=y +CONFIG_SPMI=y +# CONFIG_SPMI_HISI3670 is not set +CONFIG_SPMI_MSM_PMIC_ARB=y +# CONFIG_SPMI_PMIC_CLKDIV is not set +CONFIG_SRCU=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +# CONFIG_UCLAMP_TASK is not set +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_SUPPORT=y +CONFIG_VIRTIO=y +# CONFIG_VIRTIO_BLK is not set +# CONFIG_VIRTIO_NET is not set +CONFIG_VMAP_STACK=y +CONFIG_WANT_DEV_COREDUMP=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_SYSFS=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts new file mode 100644 index 00000000000000..c4c895e3177200 --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Dirk Buchwalder */ + +/dts-v1/; + +#include "ipq8074-512m.dtsi" +#include "ipq8074-ac-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + + model = "Edimax CAX1800"; + compatible = "edimax,cax1800", "qcom,ipq8074"; + interrupt-parent = <&intc>; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_system_red; + led-failsafe = &led_system_red; + led-running = &led_system_green; + led-upgrade = &led_system_red; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet4 = &dp5; + label-mac-device = &dp5; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_system_red: system-red { + label = "red:system"; + gpios = <&tlmm 25 GPIO_ACTIVE_HIGH>; + }; + + led_system_green: system-green { + label = "green:system"; + gpios = <&tlmm 26 GPIO_ACTIVE_HIGH>; + }; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 27 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "rootfs"; + reg = <0x0000000 0x3400000>; + }; + }; + }; +}; + +&blsp1_spi1 { + pinctrl-0 = <&spi_0_pins>; + pinctrl-names = "default"; + cs-select = <0>; + status = "ok"; + + m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + use-default-sizes; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@50000 { + label = "0:mibib"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "0:bootconfig"; + reg = <0x60000 0x20000>; + read-only; + }; + + partition@80000 { + label = "0:bootconfig1"; + reg = <0x80000 0x20000>; + read-only; + }; + + partition@a0000 { + label = "0:qsee"; + reg = <0xa0000 0x180000>; + read-only; + }; + + partition@220000 { + label = "0:qsee_1"; + reg = <0x220000 0x180000>; + read-only; + }; + + partition@3a0000 { + label = "0:devcfg"; + reg = <0x3a0000 0x10000>; + read-only; + }; + + partition@3b0000 { + label = "0:devcfg_1"; + reg = <0x3b0000 0x10000>; + read-only; + }; + + partition@3c0000 { + label = "0:apdp"; + reg = <0x3c0000 0x10000>; + read-only; + }; + + partition@3d0000 { + label = "0:apdp_1"; + reg = <0x3d0000 0x10000>; + read-only; + }; + + partition@3e0000 { + label = "0:rpm"; + reg = <0x3e0000 0x40000>; + read-only; + }; + + partition@420000 { + label = "0:rpm_1"; + reg = <0x420000 0x40000>; + read-only; + }; + + partition@460000 { + label = "0:cdt"; + reg = <0x460000 0x10000>; + read-only; + }; + + partition@470000 { + label = "0:cdt_1"; + reg = <0x470000 0x10000>; + read-only; + }; + + partition@480000 { + label = "0:appsblenv"; + reg = <0x480000 0x10000>; + }; + + partition@490000 { + label = "0:appsbl"; + reg = <0x490000 0xa0000>; + read-only; + }; + + partition@530000 { + label = "0:appsbl_1"; + reg = <0x530000 0xa0000>; + read-only; + }; + + partition@5d0000 { + label = "0:art"; + reg = <0x5d0000 0x40000>; + read-only; + }; + + partition@610000 { + label = "0:ethphyfw"; + reg = <0x610000 0x80000>; + read-only; + }; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075: ethernet-phy@4 { + reg = <4>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x1e>; /* lan port bitmap */ + switch_wan_bmp = <0x20>; /* wan port bitmap */ + switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@4 { + port_id = <5>; + phy_address = <4>; + }; + }; + + port_scheduler_resource { + port@0 { + port_id = <0>; + ucast_queue = <0 143>; + mcast_queue = <256 271>; + l0sp = <0 35>; + l0cdrr = <0 47>; + l0edrr = <0 47>; + l1cdrr = <0 7>; + l1edrr = <0 7>; + }; + port@1 { + port_id = <1>; + ucast_queue = <144 159>; + mcast_queue = <272 275>; + l0sp = <36 39>; + l0cdrr = <48 63>; + l0edrr = <48 63>; + l1cdrr = <8 11>; + l1edrr = <8 11>; + }; + port@2 { + port_id = <2>; + ucast_queue = <160 175>; + mcast_queue = <276 279>; + l0sp = <40 43>; + l0cdrr = <64 79>; + l0edrr = <64 79>; + l1cdrr = <12 15>; + l1edrr = <12 15>; + }; + port@3 { + port_id = <3>; + ucast_queue = <176 191>; + mcast_queue = <280 283>; + l0sp = <44 47>; + l0cdrr = <80 95>; + l0edrr = <80 95>; + l1cdrr = <16 19>; + l1edrr = <16 19>; + }; + port@4 { + port_id = <4>; + ucast_queue = <192 207>; + mcast_queue = <284 287>; + l0sp = <48 51>; + l0cdrr = <96 111>; + l0edrr = <96 111>; + l1cdrr = <20 23>; + l1edrr = <20 23>; + }; + port@5 { + port_id = <5>; + ucast_queue = <208 223>; + mcast_queue = <288 291>; + l0sp = <52 55>; + l0cdrr = <112 127>; + l0edrr = <112 127>; + l1cdrr = <24 27>; + l1edrr = <24 27>; + }; + port@6 { + port_id = <6>; + ucast_queue = <224 239>; + mcast_queue = <292 295>; + l0sp = <56 59>; + l0cdrr = <128 143>; + l0edrr = <128 143>; + l1cdrr = <28 31>; + l1edrr = <28 31>; + }; + port@7 { + port_id = <7>; + ucast_queue = <240 255>; + mcast_queue = <296 299>; + l0sp = <60 63>; + l0cdrr = <144 159>; + l0edrr = <144 159>; + l1cdrr = <32 35>; + l1edrr = <32 35>; + }; + }; + port_scheduler_config { + port@0 { + port_id = <0>; + l1scheduler { + group@0 { + sp = <0 1>; /*L0 SPs*/ + /*cpri cdrr epri edrr*/ + cfg = <0 0 0 0>; + }; + }; + l0scheduler { + group@0 { + /*unicast queues*/ + ucast_queue = <0 4 8>; + /*multicast queues*/ + mcast_queue = <256 260>; + /*sp cpri cdrr epri edrr*/ + cfg = <0 0 0 0 0>; + }; + group@1 { + ucast_queue = <1 5 9>; + mcast_queue = <257 261>; + cfg = <0 1 1 1 1>; + }; + group@2 { + ucast_queue = <2 6 10>; + mcast_queue = <258 262>; + cfg = <0 2 2 2 2>; + }; + group@3 { + ucast_queue = <3 7 11>; + mcast_queue = <259 263>; + cfg = <0 3 3 3 3>; + }; + }; + }; + port@1 { + port_id = <1>; + l1scheduler { + group@0 { + sp = <36>; + cfg = <0 8 0 8>; + }; + group@1 { + sp = <37>; + cfg = <1 9 1 9>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <144>; + ucast_loop_pri = <16>; + mcast_queue = <272>; + mcast_loop_pri = <4>; + cfg = <36 0 48 0 48>; + }; + }; + }; + port@2 { + port_id = <2>; + l1scheduler { + group@0 { + sp = <40>; + cfg = <0 12 0 12>; + }; + group@1 { + sp = <41>; + cfg = <1 13 1 13>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <160>; + ucast_loop_pri = <16>; + mcast_queue = <276>; + mcast_loop_pri = <4>; + cfg = <40 0 64 0 64>; + }; + }; + }; + port@3 { + port_id = <3>; + l1scheduler { + group@0 { + sp = <44>; + cfg = <0 16 0 16>; + }; + group@1 { + sp = <45>; + cfg = <1 17 1 17>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <176>; + ucast_loop_pri = <16>; + mcast_queue = <280>; + mcast_loop_pri = <4>; + cfg = <44 0 80 0 80>; + }; + }; + }; + port@4 { + port_id = <4>; + l1scheduler { + group@0 { + sp = <48>; + cfg = <0 20 0 20>; + }; + group@1 { + sp = <49>; + cfg = <1 21 1 21>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <192>; + ucast_loop_pri = <16>; + mcast_queue = <284>; + mcast_loop_pri = <4>; + cfg = <48 0 96 0 96>; + }; + }; + }; + port@5 { + port_id = <5>; + l1scheduler { + group@0 { + sp = <52>; + cfg = <0 24 0 24>; + }; + group@1 { + sp = <53>; + cfg = <1 25 1 25>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <208>; + ucast_loop_pri = <16>; + mcast_queue = <288>; + mcast_loop_pri = <4>; + cfg = <52 0 112 0 112>; + }; + }; + }; + port@6 { + port_id = <6>; + l1scheduler { + group@0 { + sp = <56>; + cfg = <0 28 0 28>; + }; + group@1 { + sp = <57>; + cfg = <1 29 1 29>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <224>; + ucast_loop_pri = <16>; + mcast_queue = <292>; + mcast_loop_pri = <4>; + cfg = <56 0 128 0 128>; + }; + }; + }; + port@7 { + port_id = <7>; + l1scheduler { + group@0 { + sp = <60>; + cfg = <0 32 0 32>; + }; + group@1 { + sp = <61>; + cfg = <1 33 1 33>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <240>; + ucast_loop_pri = <16>; + mcast_queue = <296>; + cfg = <60 0 144 0 144>; + }; + }; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&soc { + + dp5: dp5 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <5>; + reg = <0x3a001800 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075>; + phy-mode = "sgmii"; + }; +}; + +&wifi { + status = "okay"; + + qcom,board_id = <657>; + qcom,ath11k-calibration-variant = "Edimax-CAX1800"; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts new file mode 100644 index 00000000000000..3af5a347134ed6 --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Robert Marko */ + +/dts-v1/; + +#include "ipq8071-ax3600.dtsi" + +/ { + model = "Xiaomi AX3600"; + compatible = "xiaomi,ax3600", "qcom,ipq8074"; + + leds { + compatible = "gpio-leds"; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + }; + + led_system_yellow: system-yellow { + label = "yellow:system"; + gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; + }; + + network-yellow { + label = "yellow:network"; + gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; + }; + + network-blue { + label = "blue:network"; + gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; + }; + + aiot { + label = "blue:aiot"; + gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0tpt"; + }; + }; +}; + +&pcie_qmp0 { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpio = <&tlmm 52 GPIO_ACTIVE_HIGH>; + + bridge@0,0 { + reg = <0x00000000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi0: wifi@1,0 { + status = "okay"; + + compatible = "qcom,ath10k"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath10k-calibration-variant = "Xiaomi-AX3600"; + }; + }; +}; + +&wifi { + qcom,ath11k-calibration-variant = "Xiaomi-AX3600"; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi new file mode 100644 index 00000000000000..0670e3c9143810 --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Robert Marko */ + +#include "ipq8074-512m.dtsi" +#include "ipq8074-ac-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + + interrupt-parent = <&intc>; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_system_yellow; + led-failsafe = &led_system_yellow; + led-running = &led_system_blue; + led-upgrade = &led_system_yellow; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet1 = &dp2; + ethernet2 = &dp3; + ethernet3 = &dp4; + ethernet4 = &dp5; + label-mac-device = &dp2; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 34 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "qcom,smem-part"; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_1: ethernet-phy@1 { + reg = <1>; + }; + + qca8075_2: ethernet-phy@2 { + reg = <2>; + }; + + qca8075_3: ethernet-phy@3 { + reg = <3>; + }; + + qca8075_4: ethernet-phy@4 { + reg = <4>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x1e>; /* lan port bitmap */ + switch_wan_bmp = <0x20>; /* wan port bitmap */ + switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@4 { + port_id = <5>; + phy_address = <4>; + }; + }; + + port_scheduler_resource { + port@0 { + port_id = <0>; + ucast_queue = <0 143>; + mcast_queue = <256 271>; + l0sp = <0 35>; + l0cdrr = <0 47>; + l0edrr = <0 47>; + l1cdrr = <0 7>; + l1edrr = <0 7>; + }; + port@1 { + port_id = <1>; + ucast_queue = <144 159>; + mcast_queue = <272 275>; + l0sp = <36 39>; + l0cdrr = <48 63>; + l0edrr = <48 63>; + l1cdrr = <8 11>; + l1edrr = <8 11>; + }; + port@2 { + port_id = <2>; + ucast_queue = <160 175>; + mcast_queue = <276 279>; + l0sp = <40 43>; + l0cdrr = <64 79>; + l0edrr = <64 79>; + l1cdrr = <12 15>; + l1edrr = <12 15>; + }; + port@3 { + port_id = <3>; + ucast_queue = <176 191>; + mcast_queue = <280 283>; + l0sp = <44 47>; + l0cdrr = <80 95>; + l0edrr = <80 95>; + l1cdrr = <16 19>; + l1edrr = <16 19>; + }; + port@4 { + port_id = <4>; + ucast_queue = <192 207>; + mcast_queue = <284 287>; + l0sp = <48 51>; + l0cdrr = <96 111>; + l0edrr = <96 111>; + l1cdrr = <20 23>; + l1edrr = <20 23>; + }; + port@5 { + port_id = <5>; + ucast_queue = <208 223>; + mcast_queue = <288 291>; + l0sp = <52 55>; + l0cdrr = <112 127>; + l0edrr = <112 127>; + l1cdrr = <24 27>; + l1edrr = <24 27>; + }; + port@6 { + port_id = <6>; + ucast_queue = <224 239>; + mcast_queue = <292 295>; + l0sp = <56 59>; + l0cdrr = <128 143>; + l0edrr = <128 143>; + l1cdrr = <28 31>; + l1edrr = <28 31>; + }; + port@7 { + port_id = <7>; + ucast_queue = <240 255>; + mcast_queue = <296 299>; + l0sp = <60 63>; + l0cdrr = <144 159>; + l0edrr = <144 159>; + l1cdrr = <32 35>; + l1edrr = <32 35>; + }; + }; + port_scheduler_config { + port@0 { + port_id = <0>; + l1scheduler { + group@0 { + sp = <0 1>; /*L0 SPs*/ + /*cpri cdrr epri edrr*/ + cfg = <0 0 0 0>; + }; + }; + l0scheduler { + group@0 { + /*unicast queues*/ + ucast_queue = <0 4 8>; + /*multicast queues*/ + mcast_queue = <256 260>; + /*sp cpri cdrr epri edrr*/ + cfg = <0 0 0 0 0>; + }; + group@1 { + ucast_queue = <1 5 9>; + mcast_queue = <257 261>; + cfg = <0 1 1 1 1>; + }; + group@2 { + ucast_queue = <2 6 10>; + mcast_queue = <258 262>; + cfg = <0 2 2 2 2>; + }; + group@3 { + ucast_queue = <3 7 11>; + mcast_queue = <259 263>; + cfg = <0 3 3 3 3>; + }; + }; + }; + port@1 { + port_id = <1>; + l1scheduler { + group@0 { + sp = <36>; + cfg = <0 8 0 8>; + }; + group@1 { + sp = <37>; + cfg = <1 9 1 9>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <144>; + ucast_loop_pri = <16>; + mcast_queue = <272>; + mcast_loop_pri = <4>; + cfg = <36 0 48 0 48>; + }; + }; + }; + port@2 { + port_id = <2>; + l1scheduler { + group@0 { + sp = <40>; + cfg = <0 12 0 12>; + }; + group@1 { + sp = <41>; + cfg = <1 13 1 13>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <160>; + ucast_loop_pri = <16>; + mcast_queue = <276>; + mcast_loop_pri = <4>; + cfg = <40 0 64 0 64>; + }; + }; + }; + port@3 { + port_id = <3>; + l1scheduler { + group@0 { + sp = <44>; + cfg = <0 16 0 16>; + }; + group@1 { + sp = <45>; + cfg = <1 17 1 17>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <176>; + ucast_loop_pri = <16>; + mcast_queue = <280>; + mcast_loop_pri = <4>; + cfg = <44 0 80 0 80>; + }; + }; + }; + port@4 { + port_id = <4>; + l1scheduler { + group@0 { + sp = <48>; + cfg = <0 20 0 20>; + }; + group@1 { + sp = <49>; + cfg = <1 21 1 21>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <192>; + ucast_loop_pri = <16>; + mcast_queue = <284>; + mcast_loop_pri = <4>; + cfg = <48 0 96 0 96>; + }; + }; + }; + port@5 { + port_id = <5>; + l1scheduler { + group@0 { + sp = <52>; + cfg = <0 24 0 24>; + }; + group@1 { + sp = <53>; + cfg = <1 25 1 25>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <208>; + ucast_loop_pri = <16>; + mcast_queue = <288>; + mcast_loop_pri = <4>; + cfg = <52 0 112 0 112>; + }; + }; + }; + port@6 { + port_id = <6>; + l1scheduler { + group@0 { + sp = <56>; + cfg = <0 28 0 28>; + }; + group@1 { + sp = <57>; + cfg = <1 29 1 29>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <224>; + ucast_loop_pri = <16>; + mcast_queue = <292>; + mcast_loop_pri = <4>; + cfg = <56 0 128 0 128>; + }; + }; + }; + port@7 { + port_id = <7>; + l1scheduler { + group@0 { + sp = <60>; + cfg = <0 32 0 32>; + }; + group@1 { + sp = <61>; + cfg = <1 33 1 33>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <240>; + ucast_loop_pri = <16>; + mcast_queue = <296>; + cfg = <60 0 144 0 144>; + }; + }; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&soc { + dp2: dp2 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <2>; + reg = <0x3a001200 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_1>; + phy-mode = "sgmii"; + }; + + dp3: dp3 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <3>; + reg = <0x3a001400 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_2>; + phy-mode = "sgmii"; + }; + + dp4: dp4 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <4>; + reg = <0x3a001600 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_3>; + phy-mode = "sgmii"; + }; + + dp5: dp5 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <5>; + reg = <0x3a001800 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_4>; + phy-mode = "sgmii"; + }; +}; + +&wifi { + status = "okay"; + + qcom,board_id = <658>; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts new file mode 100644 index 00000000000000..c66fb95bc7f312 --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Zhijun You */ + +/dts-v1/; + +#include "ipq8071-ax3600.dtsi" + +/ { + model = "Redmi AX6"; + compatible = "redmi,ax6", "qcom,ipq8074"; + + leds { + compatible = "gpio-leds"; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; + }; + + led_system_yellow: system-yellow { + label = "yellow:system"; + gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; + }; + + network-blue { + label = "blue:network"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + }; + + network-yellow { + label = "yellow:network"; + gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&wifi { + qcom,ath11k-calibration-variant = "Redmi-AX6"; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts new file mode 100644 index 00000000000000..493d69062a3e77 --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts @@ -0,0 +1,679 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2022, Matthew Hagan */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-ac-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "Edgecore EAP102"; + compatible = "edgecore,eap102", "qcom,ipq8074"; + + interrupt-parent = <&intc>; + + aliases { + serial0 = &blsp1_uart5; + serial1 = &blsp1_uart3; + led-boot = &led_system_green; + led-failsafe = &led_system_green; + led-running = &led_system_green; + led-upgrade = &led_system_green; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet1 = &dp5; + ethernet0 = &dp6; + label-mac-device = &dp6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + reset { + label = "reset"; + gpios = <&tlmm 66 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_wanpoe { + label = "green:wanpoe"; + gpios = <&tlmm 46 GPIO_ACTIVE_HIGH>; + }; + + led_wlan2g { + label = "green:wlan2g"; + gpio = <&tlmm 47 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy1radio"; + }; + + led_wlan5g { + label = "green:wlan5g"; + gpio = <&tlmm 48 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0radio"; + }; + + led_system_green: led_system { + label = "green:power"; + gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + button_pins: button_pins { + reset_button { + pins = "gpio66"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_spi1 { + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@50000 { + label = "0:mibib"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "0:bootconfig"; + reg = <0x60000 0x20000>; + read-only; + }; + + partition@80000 { + label = "0:bootconfig1"; + reg = <0x80000 0x20000>; + read-only; + }; + + partition@a0000 { + label = "0:qsee"; + reg = <0xa0000 0x180000>; + read-only; + }; + + partition@220000 { + label = "0:qsee_1"; + reg = <0x220000 0x180000>; + read-only; + }; + + partition@3a0000 { + label = "0:devcfg"; + reg = <0x3a0000 0x10000>; + read-only; + }; + + partition@3b0000 { + label = "0:devcfg_1"; + reg = <0x3b0000 0x10000>; + read-only; + }; + + partition@3c0000 { + label = "0:apdp"; + reg = <0x3c0000 0x10000>; + read-only; + }; + + partition@3d0000 { + label = "0:apdp_1"; + reg = <0x3d0000 0x10000>; + read-only; + }; + + partition@3e0000 { + label = "0:rpm"; + reg = <0x3e0000 0x40000>; + read-only; + }; + + partition@420000 { + label = "0:rpm_1"; + reg = <0x420000 0x40000>; + read-only; + }; + + partition@460000 { + label = "0:cdt"; + reg = <0x460000 0x10000>; + read-only; + }; + + partition@470000 { + label = "0:cdt_1"; + reg = <0x470000 0x10000>; + read-only; + }; + + partition@480000 { + label = "0:appsblenv"; + reg = <0x480000 0x10000>; + }; + + partition@490000 { + label = "0:appsbl"; + reg = <0x490000 0xc0000>; + read-only; + }; + + partition@550000 { + label = "0:appsbl_1"; + reg = <0x530000 0xc0000>; + read-only; + }; + + partition@610000 { + label = "0:art"; + reg = <0x610000 0x40000>; + read-only; + }; + + partition@650000 { + label = "0:ethphyfw"; + reg = <0x650000 0x80000>; + read-only; + }; + + partition@6d0000 { + label = "0:product_info"; + reg = <0x6d0000 0x80000>; + read-only; + }; + + partition@750000 { + label = "priv_data1"; + reg = <0x750000 0x10000>; + read-only; + }; + + partition@760000 { + label = "priv_data2"; + reg = <0x760000 0x10000>; + read-only; + }; + }; + }; +}; + +&blsp1_uart3 { + status = "okay"; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "rootfs1"; + reg = <0x0000000 0x3400000>; + }; + + partition@3400000 { + label = "0:wififw"; + reg = <0x3400000 0x800000>; + read-only; + }; + + partition@3c00000 { + label = "rootfs2"; + reg = <0x3c00000 0x3400000>; + }; + + partition@7000000 { + label = "0:wififw_1"; + reg = <0x7000000 0x800000>; + read-only; + }; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + + qca8081_24: ethernet-phy@24 { + compatible = "ethernet-phy-id004d.d101"; + reg = <24>; + reset-gpios = <&tlmm 33 GPIO_ACTIVE_LOW>; + }; + + qca8081_28: ethernet-phy@28 { + compatible = "ethernet-phy-id004d.d101"; + reg = <28>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x3e>; /* lan port bitmap */ + switch_wan_bmp = <0x40>; /* wan port bitmap */ + switch_mac_mode = <0xff>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xf>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xf>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@4 { + port_id = <5>; + phy_address = <24>; + port_mac_sel = "QGMAC_PORT"; + }; + port@5 { + port_id = <6>; + phy_address = <28>; + port_mac_sel = "QGMAC_PORT"; + }; + }; + + port_scheduler_resource { + port@0 { + port_id = <0>; + ucast_queue = <0 143>; + mcast_queue = <256 271>; + l0sp = <0 35>; + l0cdrr = <0 47>; + l0edrr = <0 47>; + l1cdrr = <0 7>; + l1edrr = <0 7>; + }; + port@1 { + port_id = <1>; + ucast_queue = <144 159>; + mcast_queue = <272 275>; + l0sp = <36 39>; + l0cdrr = <48 63>; + l0edrr = <48 63>; + l1cdrr = <8 11>; + l1edrr = <8 11>; + }; + port@2 { + port_id = <2>; + ucast_queue = <160 175>; + mcast_queue = <276 279>; + l0sp = <40 43>; + l0cdrr = <64 79>; + l0edrr = <64 79>; + l1cdrr = <12 15>; + l1edrr = <12 15>; + }; + port@3 { + port_id = <3>; + ucast_queue = <176 191>; + mcast_queue = <280 283>; + l0sp = <44 47>; + l0cdrr = <80 95>; + l0edrr = <80 95>; + l1cdrr = <16 19>; + l1edrr = <16 19>; + }; + port@4 { + port_id = <4>; + ucast_queue = <192 207>; + mcast_queue = <284 287>; + l0sp = <48 51>; + l0cdrr = <96 111>; + l0edrr = <96 111>; + l1cdrr = <20 23>; + l1edrr = <20 23>; + }; + port@5 { + port_id = <5>; + ucast_queue = <208 223>; + mcast_queue = <288 291>; + l0sp = <52 55>; + l0cdrr = <112 127>; + l0edrr = <112 127>; + l1cdrr = <24 27>; + l1edrr = <24 27>; + }; + port@6 { + port_id = <6>; + ucast_queue = <224 239>; + mcast_queue = <292 295>; + l0sp = <56 59>; + l0cdrr = <128 143>; + l0edrr = <128 143>; + l1cdrr = <28 31>; + l1edrr = <28 31>; + }; + port@7 { + port_id = <7>; + ucast_queue = <240 255>; + mcast_queue = <296 299>; + l0sp = <60 63>; + l0cdrr = <144 159>; + l0edrr = <144 159>; + l1cdrr = <32 35>; + l1edrr = <32 35>; + }; + }; + port_scheduler_config { + port@0 { + port_id = <0>; + l1scheduler { + group@0 { + sp = <0 1>; /*L0 SPs*/ + /*cpri cdrr epri edrr*/ + cfg = <0 0 0 0>; + }; + }; + l0scheduler { + group@0 { + /*unicast queues*/ + ucast_queue = <0 4 8>; + /*multicast queues*/ + mcast_queue = <256 260>; + /*sp cpri cdrr epri edrr*/ + cfg = <0 0 0 0 0>; + }; + group@1 { + ucast_queue = <1 5 9>; + mcast_queue = <257 261>; + cfg = <0 1 1 1 1>; + }; + group@2 { + ucast_queue = <2 6 10>; + mcast_queue = <258 262>; + cfg = <0 2 2 2 2>; + }; + group@3 { + ucast_queue = <3 7 11>; + mcast_queue = <259 263>; + cfg = <0 3 3 3 3>; + }; + }; + }; + port@1 { + port_id = <1>; + l1scheduler { + group@0 { + sp = <36>; + cfg = <0 8 0 8>; + }; + group@1 { + sp = <37>; + cfg = <1 9 1 9>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <144>; + ucast_loop_pri = <16>; + mcast_queue = <272>; + mcast_loop_pri = <4>; + cfg = <36 0 48 0 48>; + }; + }; + }; + port@2 { + port_id = <2>; + l1scheduler { + group@0 { + sp = <40>; + cfg = <0 12 0 12>; + }; + group@1 { + sp = <41>; + cfg = <1 13 1 13>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <160>; + ucast_loop_pri = <16>; + mcast_queue = <276>; + mcast_loop_pri = <4>; + cfg = <40 0 64 0 64>; + }; + }; + }; + port@3 { + port_id = <3>; + l1scheduler { + group@0 { + sp = <44>; + cfg = <0 16 0 16>; + }; + group@1 { + sp = <45>; + cfg = <1 17 1 17>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <176>; + ucast_loop_pri = <16>; + mcast_queue = <280>; + mcast_loop_pri = <4>; + cfg = <44 0 80 0 80>; + }; + }; + }; + port@4 { + port_id = <4>; + l1scheduler { + group@0 { + sp = <48>; + cfg = <0 20 0 20>; + }; + group@1 { + sp = <49>; + cfg = <1 21 1 21>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <192>; + ucast_loop_pri = <16>; + mcast_queue = <284>; + mcast_loop_pri = <4>; + cfg = <48 0 96 0 96>; + }; + }; + }; + port@5 { + port_id = <5>; + l1scheduler { + group@0 { + sp = <52>; + cfg = <0 24 0 24>; + }; + group@1 { + sp = <53>; + cfg = <1 25 1 25>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <208>; + ucast_loop_pri = <16>; + mcast_queue = <288>; + mcast_loop_pri = <4>; + cfg = <52 0 112 0 112>; + }; + }; + }; + port@6 { + port_id = <6>; + l1scheduler { + group@0 { + sp = <56>; + cfg = <0 28 0 28>; + }; + group@1 { + sp = <57>; + cfg = <1 29 1 29>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <224>; + ucast_loop_pri = <16>; + mcast_queue = <292>; + mcast_loop_pri = <4>; + cfg = <56 0 128 0 128>; + }; + }; + }; + port@7 { + port_id = <7>; + l1scheduler { + group@0 { + sp = <60>; + cfg = <0 32 0 32>; + }; + group@1 { + sp = <61>; + cfg = <1 33 1 33>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <240>; + ucast_loop_pri = <16>; + mcast_queue = <296>; + cfg = <60 0 144 0 144>; + }; + }; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&soc { + dp6: dp6 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <6>; + reg = <0x3a001800 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8081_28>; + phy-mode = "sgmii"; + }; + + dp5: dp5 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <5>; + reg = <0x3a001a00 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8081_24>; + phy-mode = "sgmii"; + }; +}; + +&wifi { + status = "okay"; + + qcom,board_id = <658>; + qcom,ath11k-calibration-variant = "Edgecore-EAP102"; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts new file mode 100644 index 00000000000000..0a3010cba1a57a --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts @@ -0,0 +1,808 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Dirk Buchwalder */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + + model = "QNAP 301w"; + compatible = "qnap,301w", "qcom,ipq8074"; + interrupt-parent = <&intc>; + + aliases { + serial0 = &blsp1_uart5; + /* + * Aliases as required by u-boot + * to patch MAC addresses + */ + led-boot = &led_system_red; + led-failsafe = &led_system_red; + led-running = &led_pwr_green; + led-upgrade = &led_system_red; + ethernet0 = &dp1; + ethernet1 = &dp2; + ethernet2 = &dp3; + ethernet3 = &dp4; + ethernet4 = &dp5; + ethernet5 = &dp6; + label-mac-device = &dp1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + keys { + compatible = "gpio-keys"; + + wps { + label = "wps"; + gpios = <&tlmm 57 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&tlmm 67 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_system_green: system-green { + label = "green:system"; + gpios = <&tlmm 1 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_red: system-red { + label = "red:system"; + gpios = <&tlmm 3 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_pwr_green: pwr-green { + label = "green:pwr"; + gpios = <&tlmm 4 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_wifi_green: wifi-green { + label = "green:wifi"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan1_green: lan1-green { + label = "green:lan1"; + gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan1_amber: lan1-amber { + label = "amber:lan1"; + gpios = <&tlmm 7 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan2_green: lan2-green { + label = "green:lan2"; + gpios = <&tlmm 8 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan2_amber: lan2-amber { + label = "amber:lan2"; + gpios = <&tlmm 11 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan3_green: lan3-green { + label = "green:lan3"; + gpios = <&tlmm 12 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan3_amber: lan3-amber { + label = "amber:lan3"; + gpios = <&tlmm 13 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan4_green: lan4-green { + label = "green:lan4"; + gpios = <&tlmm 14 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_lan4_amber: lan4-amber { + label = "amber:lan4"; + gpios = <&tlmm 15 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_10g_1_green: 10g_1-green { + label = "green:10g_1"; + gpios = <&tlmm 54 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_10g_1_amber: 10g_1-amber { + label = "amber:10g_1"; + gpios = <&tlmm 56 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_10g_2_green: 10g_2-green { + label = "green:10g_2"; + gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_10g_2_amber: 10g_2-amber { + label = "amber:10g_2"; + gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>; + color = ; + }; + }; +}; + +&tlmm { + + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + button_pins: button_pins { + wps_button { + pins = "gpio57"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + rst_button { + pins = "gpio67"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + qpic_pins: qpic-pins { + status = "disabled"; + /*disabling qpic-pins due to the leds are using all of the gpios*/ + }; + + leds_pins: leds_pinmux { + led_pwr_green { + pins = "gpio4"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_system_green { + pins = "gpio1"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_system_red { + pins = "gpio3"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan1_green { + pins = "gpio6"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan1_amber { + pins = "gpio7"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan2_green { + pins = "gpio8"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan2_amber { + pins = "gpio11"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan3_green { + pins = "gpio12"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan3_amber { + pins = "gpio13"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan4_green { + pins = "gpio14"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_lan4_amber { + pins = "gpio15"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_10g_1_green { + pins = "gpio54"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_10g_1_amber { + pins = "gpio56"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_10g_2_green { + pins = "gpio51"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_10g_2_amber { + pins = "gpio52"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + led_wifi_green { + pins = "gpio42"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_1 { + status = "okay"; +}; + +&qusb_phy_1 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&blsp1_spi1 { /* BLSP1 QUP1 */ + pinctrl-0 = <&spi_0_pins>; + pinctrl-names = "default"; + cs-gpios = <0>; + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "qcom,smem-part"; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + aqr113c_0: ethernet-phy@0 { + compatible ="ethernet-phy-ieee802.3-c45"; + reg = <0>; + reset-gpios = <&tlmm 59 GPIO_ACTIVE_LOW>; + }; + + aqr113c_8: ethernet-phy@8 { + compatible ="ethernet-phy-ieee802.3-c45"; + reg = <8>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; + + qca8075_16: ethernet-phy@16 { + reg = <16>; + }; + + qca8075_17: ethernet-phy@17 { + reg = <17>; + }; + + qca8075_18: ethernet-phy@18 { + reg = <18>; + }; + + qca8075_19: ethernet-phy@19 { + reg = <19>; + }; +}; + +&sdhc_1 { + status = "okay"; + + /* According to the stock dts from the QNAP gpl drop + * the emmc has a problem with the hs400 > hs200 speed switch. + * Therefore remove the mmc-hs400-1_8v property + */ + /delete-property/ mmc-hs400-1_8v; + mmc-hs200-1_8v; + mmc-ddr-1_8v; + vqmmc-supply = <&l11>; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x3e>; /* lan port bitmap */ + switch_wan_bmp = <0xc0>; /* wan port bitmap */ + switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xd>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xd>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <16>; + }; + port@1 { + port_id = <2>; + phy_address = <17>; + }; + port@2 { + port_id = <3>; + phy_address = <18>; + }; + port@3 { + port_id = <4>; + phy_address = <19>; + }; + port@4 { + port_id = <5>; + phy_address = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + ethernet-phy-ieee802.3-c45; + }; + port@5 { + port_id = <6>; + phy_address = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + ethernet-phy-ieee802.3-c45; + }; + }; + + port_scheduler_resource { + port@0 { + port_id = <0>; + ucast_queue = <0 143>; + mcast_queue = <256 271>; + l0sp = <0 35>; + l0cdrr = <0 47>; + l0edrr = <0 47>; + l1cdrr = <0 7>; + l1edrr = <0 7>; + }; + port@1 { + port_id = <1>; + ucast_queue = <144 159>; + mcast_queue = <272 275>; + l0sp = <36 39>; + l0cdrr = <48 63>; + l0edrr = <48 63>; + l1cdrr = <8 11>; + l1edrr = <8 11>; + }; + port@2 { + port_id = <2>; + ucast_queue = <160 175>; + mcast_queue = <276 279>; + l0sp = <40 43>; + l0cdrr = <64 79>; + l0edrr = <64 79>; + l1cdrr = <12 15>; + l1edrr = <12 15>; + }; + port@3 { + port_id = <3>; + ucast_queue = <176 191>; + mcast_queue = <280 283>; + l0sp = <44 47>; + l0cdrr = <80 95>; + l0edrr = <80 95>; + l1cdrr = <16 19>; + l1edrr = <16 19>; + }; + port@4 { + port_id = <4>; + ucast_queue = <192 207>; + mcast_queue = <284 287>; + l0sp = <48 51>; + l0cdrr = <96 111>; + l0edrr = <96 111>; + l1cdrr = <20 23>; + l1edrr = <20 23>; + }; + port@5 { + port_id = <5>; + ucast_queue = <208 223>; + mcast_queue = <288 291>; + l0sp = <52 55>; + l0cdrr = <112 127>; + l0edrr = <112 127>; + l1cdrr = <24 27>; + l1edrr = <24 27>; + }; + port@6 { + port_id = <6>; + ucast_queue = <224 239>; + mcast_queue = <292 295>; + l0sp = <56 59>; + l0cdrr = <128 143>; + l0edrr = <128 143>; + l1cdrr = <28 31>; + l1edrr = <28 31>; + }; + port@7 { + port_id = <7>; + ucast_queue = <240 255>; + mcast_queue = <296 299>; + l0sp = <60 63>; + l0cdrr = <144 159>; + l0edrr = <144 159>; + l1cdrr = <32 35>; + l1edrr = <32 35>; + }; + }; + port_scheduler_config { + port@0 { + port_id = <0>; + l1scheduler { + group@0 { + sp = <0 1>; /*L0 SPs*/ + /*cpri cdrr epri edrr*/ + cfg = <0 0 0 0>; + }; + }; + l0scheduler { + group@0 { + /*unicast queues*/ + ucast_queue = <0 4 8>; + /*multicast queues*/ + mcast_queue = <256 260>; + /*sp cpri cdrr epri edrr*/ + cfg = <0 0 0 0 0>; + }; + group@1 { + ucast_queue = <1 5 9>; + mcast_queue = <257 261>; + cfg = <0 1 1 1 1>; + }; + group@2 { + ucast_queue = <2 6 10>; + mcast_queue = <258 262>; + cfg = <0 2 2 2 2>; + }; + group@3 { + ucast_queue = <3 7 11>; + mcast_queue = <259 263>; + cfg = <0 3 3 3 3>; + }; + }; + }; + port@1 { + port_id = <1>; + l1scheduler { + group@0 { + sp = <36>; + cfg = <0 8 0 8>; + }; + group@1 { + sp = <37>; + cfg = <1 9 1 9>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <144>; + ucast_loop_pri = <16>; + mcast_queue = <272>; + mcast_loop_pri = <4>; + cfg = <36 0 48 0 48>; + }; + }; + }; + port@2 { + port_id = <2>; + l1scheduler { + group@0 { + sp = <40>; + cfg = <0 12 0 12>; + }; + group@1 { + sp = <41>; + cfg = <1 13 1 13>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <160>; + ucast_loop_pri = <16>; + mcast_queue = <276>; + mcast_loop_pri = <4>; + cfg = <40 0 64 0 64>; + }; + }; + }; + port@3 { + port_id = <3>; + l1scheduler { + group@0 { + sp = <44>; + cfg = <0 16 0 16>; + }; + group@1 { + sp = <45>; + cfg = <1 17 1 17>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <176>; + ucast_loop_pri = <16>; + mcast_queue = <280>; + mcast_loop_pri = <4>; + cfg = <44 0 80 0 80>; + }; + }; + }; + port@4 { + port_id = <4>; + l1scheduler { + group@0 { + sp = <48>; + cfg = <0 20 0 20>; + }; + group@1 { + sp = <49>; + cfg = <1 21 1 21>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <192>; + ucast_loop_pri = <16>; + mcast_queue = <284>; + mcast_loop_pri = <4>; + cfg = <48 0 96 0 96>; + }; + }; + }; + port@5 { + port_id = <5>; + l1scheduler { + group@0 { + sp = <52>; + cfg = <0 24 0 24>; + }; + group@1 { + sp = <53>; + cfg = <1 25 1 25>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <208>; + ucast_loop_pri = <16>; + mcast_queue = <288>; + mcast_loop_pri = <4>; + cfg = <52 0 112 0 112>; + }; + }; + }; + port@6 { + port_id = <6>; + l1scheduler { + group@0 { + sp = <56>; + cfg = <0 28 0 28>; + }; + group@1 { + sp = <57>; + cfg = <1 29 1 29>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <224>; + ucast_loop_pri = <16>; + mcast_queue = <292>; + mcast_loop_pri = <4>; + cfg = <56 0 128 0 128>; + }; + }; + }; + port@7 { + port_id = <7>; + l1scheduler { + group@0 { + sp = <60>; + cfg = <0 32 0 32>; + }; + group@1 { + sp = <61>; + cfg = <1 33 1 33>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <240>; + ucast_loop_pri = <16>; + mcast_queue = <296>; + cfg = <60 0 144 0 144>; + }; + }; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&soc { + dp1: dp1 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <1>; + reg = <0x3a001000 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_16>; + phy-mode = "sgmii"; + }; + + dp2: dp2 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <2>; + reg = <0x3a001200 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_17>; + phy-mode = "sgmii"; + }; + + dp3: dp3 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <3>; + reg = <0x3a001400 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_18>; + phy-mode = "sgmii"; + }; + + dp4: dp4 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <4>; + reg = <0x3a001600 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_19>; + phy-mode = "sgmii"; + }; + + dp5: dp5 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <5>; + reg = <0x3a001800 0x200>; + qcom,mactype = <1>; + local-mac-address = [000000000000]; + phy-handle = <&aqr113c_8>; + phy-mode = "sgmii"; + }; + + dp6: dp6 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <6>; + reg = <0x3a007000 0x3fff>; + qcom,mactype = <1>; + local-mac-address = [000000000000]; + phy-handle = <&aqr113c_0>; + phy-mode = "sgmii"; + }; +}; + +&wifi { + status = "okay"; + /* using board_id 0xff is intentionally + * as the stock firmware is also using this default board_id + */ + qcom,board_id = <0xff>; + qcom,ath11k-calibration-variant = "QNAP-301w"; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts new file mode 100644 index 00000000000000..90d228a2444d0d --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Robert Marko */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + + model = "Xiaomi AX9000"; + compatible = "xiaomi,ax9000", "qcom,ipq8074"; + interrupt-parent = <&intc>; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_system_yellow; + led-failsafe = &led_system_yellow; + led-running = &led_system_blue; + led-upgrade = &led_system_yellow; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet0 = &dp1; + ethernet1 = &dp2; + ethernet2 = &dp3; + ethernet3 = &dp4; + ethernet4 = &dp5; + label-mac-device = &dp5; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 47 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; /* Labeled Mesh on the device */ + gpios = <&tlmm 46 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 48 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_yellow: system-yellow { + label = "yellow:system"; + gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>; + color = ; + }; + + network-yellow { + label = "yellow:network"; + gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; + color = ; + }; + + network-blue { + label = "blue:network"; + gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + color = ; + }; + + top-red { + label = "red:top"; + gpios = <&tlmm 63 GPIO_ACTIVE_HIGH>; + color = ; + default-state = "keep"; + }; + + top-green { + label = "green:top"; + gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>; + color = ; + default-state = "keep"; + }; + + top-blue { + label = "blue:top"; + gpios = <&tlmm 66 GPIO_ACTIVE_HIGH>; + color = ; + default-state = "keep"; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + i2c_pins: i2c-pins { + pins = "gpio0", "gpio2"; + function = "blsp5_i2c"; + drive-strength = <8>; + bias-disable; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&blsp1_i2c6 { + status = "okay"; + + pinctrl-0 = <&i2c_pins>; + pinctrl-names = "default"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + /* + * Bootloader will find the NAND DT node by the compatible and + * then "fixup" it by adding the partitions from the SMEM table + * using the legacy bindings thus making it impossible for us + * to change the partition table or utilize NVMEM for calibration. + * So add a dummy partitions node that bootloader will populate + * and set it as disabled so the kernel ignores it instead of + * printing warnings due to the broken way bootloader adds the + * partitions. + */ + partitions { + status = "disabled"; + }; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x100000>; + read-only; + }; + + partition@100000 { + label = "0:mibib"; + reg = <0x100000 0x100000>; + read-only; + }; + + partition@200000 { + label = "0:bootconfig"; + reg = <0x200000 0x80000>; + read-only; + }; + + partition@280000 { + label = "0:bootconfig1"; + reg = <0x280000 0x80000>; + read-only; + }; + + partition@300000 { + label = "0:qsee"; + reg = <0x300000 0x300000>; + read-only; + }; + + partition@600000 { + label = "0:qsee_1"; + reg = <0x600000 0x300000>; + read-only; + }; + + partition@900000 { + label = "0:devcfg"; + reg = <0x900000 0x80000>; + read-only; + }; + + partition@980000 { + label = "0:devcfg_1"; + reg = <0x980000 0x80000>; + read-only; + }; + + partition@a00000 { + label = "0:apdp"; + reg = <0xa00000 0x80000>; + read-only; + }; + + partition@a80000 { + label = "0:apdp_1"; + reg = <0xa80000 0x80000>; + read-only; + }; + + partition@b00000 { + label = "0:rpm"; + reg = <0xb00000 0x80000>; + read-only; + }; + + partition@b80000 { + label = "0:rpm_1"; + reg = <0xb80000 0x80000>; + read-only; + }; + + partition@c00000 { + label = "0:cdt"; + reg = <0xc00000 0x80000>; + read-only; + }; + + partition@c80000 { + label = "0:cdt_1"; + reg = <0xc80000 0x80000>; + read-only; + }; + + partition@d00000 { + label = "0:appsblenv"; + reg = <0xd00000 0x80000>; + }; + + partition@d80000 { + label = "0:appsbl"; + reg = <0xd80000 0x100000>; + read-only; + }; + + partition@e80000 { + label = "0:appsbl_1"; + reg = <0xe80000 0x100000>; + read-only; + }; + + partition@f80000 { + label = "0:art"; + reg = <0xf80000 0x80000>; + read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + caldata_qca9889: caldata@4d000 { + reg = <0x4d000 0x844>; + }; + }; + + partition@1000000 { + label = "bdata"; + reg = <0x1000000 0x80000>; + read-only; + }; + + partition@1080000 { + /* This is crash + crash_syslog parts combined */ + label = "pstore"; + reg = <0x1080000 0x100000>; + }; + + partition@1180000 { + label = "rootfs"; + reg = <0x1180000 0x3800000>; + }; + + partition@4980000 { + label = "rootfs_1"; + reg = <0x4980000 0x3800000>; + }; + + partition@8180000 { + label = "cfg_bak"; + reg = <0x8180000 0x100000>; + read-only; + }; + + partition@8280000 { + label = "overlay"; + reg = <0x8280000 0x7d80000>; + }; + }; + }; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + + qca8075_1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + qca8081: ethernet-phy@24 { + compatible = "ethernet-phy-id004d.d101"; + reg = <24>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x1e>; /* lan port bitmap */ + switch_wan_bmp = <0x20>; /* wan port bitmap */ + switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xc>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@4 { + port_id = <5>; + phy_address = <24>; + port_mac_sel = "QGMAC_PORT"; + }; + }; + + port_scheduler_resource { + port@0 { + port_id = <0>; + ucast_queue = <0 143>; + mcast_queue = <256 271>; + l0sp = <0 35>; + l0cdrr = <0 47>; + l0edrr = <0 47>; + l1cdrr = <0 7>; + l1edrr = <0 7>; + }; + port@1 { + port_id = <1>; + ucast_queue = <144 159>; + mcast_queue = <272 275>; + l0sp = <36 39>; + l0cdrr = <48 63>; + l0edrr = <48 63>; + l1cdrr = <8 11>; + l1edrr = <8 11>; + }; + port@2 { + port_id = <2>; + ucast_queue = <160 175>; + mcast_queue = <276 279>; + l0sp = <40 43>; + l0cdrr = <64 79>; + l0edrr = <64 79>; + l1cdrr = <12 15>; + l1edrr = <12 15>; + }; + port@3 { + port_id = <3>; + ucast_queue = <176 191>; + mcast_queue = <280 283>; + l0sp = <44 47>; + l0cdrr = <80 95>; + l0edrr = <80 95>; + l1cdrr = <16 19>; + l1edrr = <16 19>; + }; + port@4 { + port_id = <4>; + ucast_queue = <192 207>; + mcast_queue = <284 287>; + l0sp = <48 51>; + l0cdrr = <96 111>; + l0edrr = <96 111>; + l1cdrr = <20 23>; + l1edrr = <20 23>; + }; + port@5 { + port_id = <5>; + ucast_queue = <208 223>; + mcast_queue = <288 291>; + l0sp = <52 55>; + l0cdrr = <112 127>; + l0edrr = <112 127>; + l1cdrr = <24 27>; + l1edrr = <24 27>; + }; + port@6 { + port_id = <6>; + ucast_queue = <224 239>; + mcast_queue = <292 295>; + l0sp = <56 59>; + l0cdrr = <128 143>; + l0edrr = <128 143>; + l1cdrr = <28 31>; + l1edrr = <28 31>; + }; + port@7 { + port_id = <7>; + ucast_queue = <240 255>; + mcast_queue = <296 299>; + l0sp = <60 63>; + l0cdrr = <144 159>; + l0edrr = <144 159>; + l1cdrr = <32 35>; + l1edrr = <32 35>; + }; + }; + port_scheduler_config { + port@0 { + port_id = <0>; + l1scheduler { + group@0 { + sp = <0 1>; /*L0 SPs*/ + /*cpri cdrr epri edrr*/ + cfg = <0 0 0 0>; + }; + }; + l0scheduler { + group@0 { + /*unicast queues*/ + ucast_queue = <0 4 8>; + /*multicast queues*/ + mcast_queue = <256 260>; + /*sp cpri cdrr epri edrr*/ + cfg = <0 0 0 0 0>; + }; + group@1 { + ucast_queue = <1 5 9>; + mcast_queue = <257 261>; + cfg = <0 1 1 1 1>; + }; + group@2 { + ucast_queue = <2 6 10>; + mcast_queue = <258 262>; + cfg = <0 2 2 2 2>; + }; + group@3 { + ucast_queue = <3 7 11>; + mcast_queue = <259 263>; + cfg = <0 3 3 3 3>; + }; + }; + }; + port@1 { + port_id = <1>; + l1scheduler { + group@0 { + sp = <36>; + cfg = <0 8 0 8>; + }; + group@1 { + sp = <37>; + cfg = <1 9 1 9>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <144>; + ucast_loop_pri = <16>; + mcast_queue = <272>; + mcast_loop_pri = <4>; + cfg = <36 0 48 0 48>; + }; + }; + }; + port@2 { + port_id = <2>; + l1scheduler { + group@0 { + sp = <40>; + cfg = <0 12 0 12>; + }; + group@1 { + sp = <41>; + cfg = <1 13 1 13>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <160>; + ucast_loop_pri = <16>; + mcast_queue = <276>; + mcast_loop_pri = <4>; + cfg = <40 0 64 0 64>; + }; + }; + }; + port@3 { + port_id = <3>; + l1scheduler { + group@0 { + sp = <44>; + cfg = <0 16 0 16>; + }; + group@1 { + sp = <45>; + cfg = <1 17 1 17>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <176>; + ucast_loop_pri = <16>; + mcast_queue = <280>; + mcast_loop_pri = <4>; + cfg = <44 0 80 0 80>; + }; + }; + }; + port@4 { + port_id = <4>; + l1scheduler { + group@0 { + sp = <48>; + cfg = <0 20 0 20>; + }; + group@1 { + sp = <49>; + cfg = <1 21 1 21>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <192>; + ucast_loop_pri = <16>; + mcast_queue = <284>; + mcast_loop_pri = <4>; + cfg = <48 0 96 0 96>; + }; + }; + }; + port@5 { + port_id = <5>; + l1scheduler { + group@0 { + sp = <52>; + cfg = <0 24 0 24>; + }; + group@1 { + sp = <53>; + cfg = <1 25 1 25>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <208>; + ucast_loop_pri = <16>; + mcast_queue = <288>; + mcast_loop_pri = <4>; + cfg = <52 0 112 0 112>; + }; + }; + }; + port@6 { + port_id = <6>; + l1scheduler { + group@0 { + sp = <56>; + cfg = <0 28 0 28>; + }; + group@1 { + sp = <57>; + cfg = <1 29 1 29>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <224>; + ucast_loop_pri = <16>; + mcast_queue = <292>; + mcast_loop_pri = <4>; + cfg = <56 0 128 0 128>; + }; + }; + }; + port@7 { + port_id = <7>; + l1scheduler { + group@0 { + sp = <60>; + cfg = <0 32 0 32>; + }; + group@1 { + sp = <61>; + cfg = <1 33 1 33>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <240>; + ucast_loop_pri = <16>; + mcast_queue = <296>; + cfg = <60 0 144 0 144>; + }; + }; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&soc { + dp1: dp1 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <1>; + reg = <0x3a001000 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_0>; + phy-mode = "sgmii"; + }; + + dp2: dp2 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <2>; + reg = <0x3a001200 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_1>; + phy-mode = "sgmii"; + }; + + dp3: dp3 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <3>; + reg = <0x3a001400 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_2>; + phy-mode = "sgmii"; + }; + + dp4: dp4 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <4>; + reg = <0x3a001600 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8075_3>; + phy-mode = "sgmii"; + }; + + dp5: dp5 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <5>; + reg = <0x3a001800 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-handle = <&qca8081>; + phy-mode = "sgmii"; + }; +}; + +&pcie_qmp0 { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; + + bridge@0,0 { + reg = <0x00000000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@1,0 { + status = "okay"; + + /* ath11k has no DT compatible for PCI cards */ + compatible = "pci17cb,1104"; + reg = <0x00010000 0 0 0 0>; + + qcom,board_id = <660>; + qcom,ath11k-calibration-variant = "Xiaomi-AX9000"; + }; + }; +}; + +&pcie_qmp1 { + status = "okay"; +}; + +&pcie1 { + status = "okay"; + + perst-gpio = <&tlmm 62 GPIO_ACTIVE_HIGH>; + + bridge@1,0 { + reg = <0x00010000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@1,0 { + status = "okay"; + + compatible = "qcom,ath10k"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath10k-calibration-variant = "Xiaomi-AX9000"; + nvmem-cell-names = "calibration"; + nvmem-cells = <&caldata_qca9889>; + }; + }; +}; + +&wifi { + status = "okay"; + + qcom,board_id = <660>; + qcom,ath11k-calibration-variant = "Xiaomi-AX9000"; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi new file mode 100644 index 00000000000000..dace4008b38bdf --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "ipq8074.dtsi" + +&tzapp_region { + reg = <0x0 0x4a400000 0x0 0x100000>; +}; + +&q6_region { + reg = <0x0 0x4b000000 0x0 0x3700000>; +}; + +&q6_etr_region { + reg = <0x0 0x4e700000 0x0 0x100000>; +}; + +&m3_dump_region { + reg = <0x0 0x4e800000 0x0 0x100000>; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi new file mode 100644 index 00000000000000..4207ece81de79a --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "ipq8074-cpr-regulator.dtsi" + +&CPU0 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&CPU1 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&CPU2 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&CPU3 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&cpus { + cpu_opp_table: cpu_opp_table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1017600000 { + opp-hz = /bits/ 64 <1017600000>; + opp-microvolt = <1>; + clock-latency-ns = <200000>; + }; + opp-1382400000 { + opp-hz = /bits/ 64 <1382400000>; + opp-microvolt = <2>; + clock-latency-ns = <200000>; + }; + }; +}; + +&cpu0_thermal { + trips { + cpu0_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu0_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu0_passive>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu1_thermal { + trips { + cpu1_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu1_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu1_passive>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu2_thermal { + trips { + cpu2_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu2_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu2_passive>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu3_thermal { + trips { + cpu3_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu3_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu3_passive>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cluster_thermal { + trips { + cluster_passive: cluster-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cluster_crit: cluster_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cluster_passive>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi new file mode 100644 index 00000000000000..e351a2e759a54d --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "pmp8074.dtsi" + +&soc { + apc_apm: apm@b111000 { + compatible = "qcom,ipq807x-apm"; + reg = <0xb111000 0x1000>; + reg-names = "pm-apcc-glb"; + qcom,apm-post-halt-delay = <0x2>; + qcom,apm-halt-clk-delay = <0x11>; + qcom,apm-resume-clk-delay = <0x10>; + qcom,apm-sel-switch-delay = <0x01>; + }; + + apc_cpr: cpr4-ctrl@b018000 { + compatible = "qcom,cpr4-ipq807x-apss-regulator"; + reg = <0xb018000 0x4000>, <0xa4000 0x1000>, <0x0193d008 0x4>; + reg-names = "cpr_ctrl", "fuse_base", "cpr_tcsr_reg"; + interrupts = ; + interrupt-names = "cpr"; + qcom,cpr-ctrl-name = "apc"; + qcom,cpr-sensor-time = <1000>; + qcom,cpr-loop-time = <5000000>; + qcom,cpr-idle-cycles = <15>; + qcom,cpr-step-quot-init-min = <12>; + qcom,cpr-step-quot-init-max = <14>; + qcom,cpr-count-mode = <0>; /* All-at-once */ + qcom,cpr-count-repeat = <14>; + qcom,cpr-down-error-step-limit = <1>; + qcom,cpr-up-error-step-limit = <1>; + qcom,apm-ctrl = <&apc_apm>; + qcom,apm-threshold-voltage = <848000>; + vdd-supply = <&s3>; + qcom,voltage-step = <8000>; + + thread@0 { + qcom,cpr-thread-id = <0>; + qcom,cpr-consecutive-up = <0>; + qcom,cpr-consecutive-down = <0>; + qcom,cpr-up-threshold = <4>; + qcom,cpr-down-threshold = <1>; + + apc_vreg: regulator { + regulator-name = "apc_corner"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <6>; + qcom,cpr-part-types = <2>; + qcom,cpr-parts-voltage = <1048000>; + qcom,cpr-parts-voltage-v2 = <992000>; + qcom,cpr-fuse-corners = <4>; + qcom,cpr-fuse-combos = <8>; + qcom,cpr-corners = <6>; + qcom,cpr-speed-bins = <1>; + qcom,cpr-speed-bin-corners = <6>; + qcom,cpr-corner-fmax-map = <1 3 5 6>; + qcom,allow-voltage-interpolation; + qcom,allow-quotient-interpolation; + qcom,cpr-scaled-open-loop-voltage-as-ceiling; + qcom,cpr-voltage-ceiling = + <840000 904000 944000 + 984000 992000 1064000>; + qcom,cpr-voltage-floor = + <592000 648000 712000 + 744000 784000 848000>; + qcom,corner-frequencies = + <1017600000 1382400000 1651200000 + 1843200000 1920000000 2208000000>; + + /* TT/FF parts i.e. turbo L1 OL voltage < 1048 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-0 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 12000>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* SS parts i.e turbo L1 OL voltage >= 1048 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-1 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 20000 26000 0 20000>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - FF parts i.e. turbo L1 OL voltage < 992 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-0 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - SS/TT parts i.e turbo L1 OL voltage >= 992 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-1 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 7000 36000 4000>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - FF parts i.e. turbo L1 OL voltage < 992 mV */ + qcom,cpr-closed-loop-voltage-adjustment-v2-0 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - SS/TT parts i.e turbo L1 OL voltage >= 992 mV */ + qcom,cpr-closed-loop-voltage-adjustment-v2-1 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 19000 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + qcom,cpr-ro-scaling-factor = + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >, + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >, + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >, + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >; + + qcom,cpr-floor-to-ceiling-max-range = + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>; + regulator-always-on; + }; + }; + }; + + npu_cpr: npu-cpr { + compatible = "qcom,cpr3-ipq807x-npu-regulator"; + reg = <0xa4000 0x1000>, <0x0193d008 0x4>; + reg-names = "fuse_base", "cpr_tcsr_reg"; + qcom,cpr-ctrl-name = "npu"; + vdd-supply = <&s4>; + qcom,voltage-step = <8000>; + thread@0 { + qcom,cpr-thread-id = <0>; + qcom,cpr-consecutive-up = <0>; + qcom,cpr-consecutive-down = <2>; + qcom,cpr-up-threshold = <2>; + qcom,cpr-down-threshold = <1>; + + npu_vreg: regulator { + regulator-name = "npu_corner"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <3>; + qcom,cpr-part-types = <2>; + qcom,cpr-parts-voltage = <968000>; + qcom,cpr-parts-voltage-v2 = <832001>; + qcom,cpr-cold-temp-threshold-v2 = <30>; + qcom,cpr-fuse-corners = <2>; + qcom,cpr-fuse-combos = <1>; + qcom,cpr-corners = <2>; + qcom,cpr-speed-bins = <1>; + qcom,cpr-speed-bin-corners = <2>; + qcom,allow-voltage-interpolation; + qcom,cpr-corner-fmax-map = <1 2>; + qcom,cpr-voltage-ceiling = + <912000 992000>; + qcom,cpr-voltage-floor = + <752000 792000>; + qcom,corner-frequencies = + <1497600000 1689600000>; + + /* TT/FF parts i.e. turbo OL voltage < 968 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-0 = + < 40000 40000>; + + /* SS parts i.e turbo OL voltage >= 968 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-1 = + < 24000 24000>; + + /* FF parts i.e. turbo OL voltage <= 832 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-0= + <40000 40000>; + + /* TT/SS parts i.e turbo OL voltage > 832 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-1= + <40000 40000>; + + /* FF parts i.e. turbo OL voltage <= 832 mV */ + qcom,cpr-cold-temp-voltage-adjustment-v2-0 = + <0 0>; + + /* TT/SS parts i.e turbo OL voltage > 832 mV */ + qcom,cpr-cold-temp-voltage-adjustment-v2-1 = + <35000 27000>; + }; + }; + }; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi new file mode 100644 index 00000000000000..7fb30dbddb8af0 --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-only + +&clocks { + bias_pll_cc_clk { + compatible = "fixed-clock"; + clock-frequency = <300000000>; + #clock-cells = <0>; + }; + + bias_pll_nss_noc_clk { + compatible = "fixed-clock"; + clock-frequency = <416500000>; + #clock-cells = <0>; + }; +}; + +&soc { + switch: ess-switch@3a000000 { + compatible = "qcom,ess-switch-ipq807x"; + reg = <0x3a000000 0x1000000>; + switch_access_mode = "local bus"; + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_inner_bmp = <0x80>; /*inner port bitmap*/ + clocks = <&gcc GCC_CMN_12GPLL_AHB_CLK>, + <&gcc GCC_CMN_12GPLL_SYS_CLK>, + <&gcc GCC_UNIPHY0_AHB_CLK>, + <&gcc GCC_UNIPHY0_SYS_CLK>, + <&gcc GCC_UNIPHY1_AHB_CLK>, + <&gcc GCC_UNIPHY1_SYS_CLK>, + <&gcc GCC_UNIPHY2_AHB_CLK>, + <&gcc GCC_UNIPHY2_SYS_CLK>, + <&gcc GCC_PORT1_MAC_CLK>, + <&gcc GCC_PORT2_MAC_CLK>, + <&gcc GCC_PORT3_MAC_CLK>, + <&gcc GCC_PORT4_MAC_CLK>, + <&gcc GCC_PORT5_MAC_CLK>, + <&gcc GCC_PORT6_MAC_CLK>, + <&gcc GCC_NSS_PPE_CLK>, + <&gcc GCC_NSS_PPE_CFG_CLK>, + <&gcc GCC_NSSNOC_PPE_CLK>, + <&gcc GCC_NSSNOC_PPE_CFG_CLK>, + <&gcc GCC_NSS_EDMA_CLK>, + <&gcc GCC_NSS_EDMA_CFG_CLK>, + <&gcc GCC_NSS_PPE_IPE_CLK>, + <&gcc GCC_NSS_PPE_BTQ_CLK>, + <&gcc GCC_MDIO_AHB_CLK>, + <&gcc GCC_NSS_NOC_CLK>, + <&gcc GCC_NSSNOC_SNOC_CLK>, + <&gcc GCC_MEM_NOC_NSS_AXI_CLK>, + <&gcc GCC_NSS_CRYPTO_CLK>, + <&gcc GCC_NSS_IMEM_CLK>, + <&gcc GCC_NSS_PTP_REF_CLK>, + <&gcc GCC_NSS_PORT1_RX_CLK>, + <&gcc GCC_NSS_PORT1_TX_CLK>, + <&gcc GCC_NSS_PORT2_RX_CLK>, + <&gcc GCC_NSS_PORT2_TX_CLK>, + <&gcc GCC_NSS_PORT3_RX_CLK>, + <&gcc GCC_NSS_PORT3_TX_CLK>, + <&gcc GCC_NSS_PORT4_RX_CLK>, + <&gcc GCC_NSS_PORT4_TX_CLK>, + <&gcc GCC_NSS_PORT5_RX_CLK>, + <&gcc GCC_NSS_PORT5_TX_CLK>, + <&gcc GCC_NSS_PORT6_RX_CLK>, + <&gcc GCC_NSS_PORT6_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT1_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT1_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT2_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT2_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT3_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT3_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT4_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT4_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT5_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT5_TX_CLK>, + <&gcc GCC_UNIPHY1_PORT5_RX_CLK>, + <&gcc GCC_UNIPHY1_PORT5_TX_CLK>, + <&gcc GCC_UNIPHY2_PORT6_RX_CLK>, + <&gcc GCC_UNIPHY2_PORT6_TX_CLK>, + <&gcc NSS_PORT5_RX_CLK_SRC>, + <&gcc NSS_PORT5_TX_CLK_SRC>; + clock-names = "cmn_ahb_clk", "cmn_sys_clk", + "uniphy0_ahb_clk", "uniphy0_sys_clk", + "uniphy1_ahb_clk", "uniphy1_sys_clk", + "uniphy2_ahb_clk", "uniphy2_sys_clk", + "port1_mac_clk", "port2_mac_clk", + "port3_mac_clk", "port4_mac_clk", + "port5_mac_clk", "port6_mac_clk", + "nss_ppe_clk", "nss_ppe_cfg_clk", + "nssnoc_ppe_clk", "nssnoc_ppe_cfg_clk", + "nss_edma_clk", "nss_edma_cfg_clk", + "nss_ppe_ipe_clk", "nss_ppe_btq_clk", + "gcc_mdio_ahb_clk", "gcc_nss_noc_clk", + "gcc_nssnoc_snoc_clk", + "gcc_mem_noc_nss_axi_clk", + "gcc_nss_crypto_clk", + "gcc_nss_imem_clk", + "gcc_nss_ptp_ref_clk", + "nss_port1_rx_clk", "nss_port1_tx_clk", + "nss_port2_rx_clk", "nss_port2_tx_clk", + "nss_port3_rx_clk", "nss_port3_tx_clk", + "nss_port4_rx_clk", "nss_port4_tx_clk", + "nss_port5_rx_clk", "nss_port5_tx_clk", + "nss_port6_rx_clk", "nss_port6_tx_clk", + "uniphy0_port1_rx_clk", + "uniphy0_port1_tx_clk", + "uniphy0_port2_rx_clk", + "uniphy0_port2_tx_clk", + "uniphy0_port3_rx_clk", + "uniphy0_port3_tx_clk", + "uniphy0_port4_rx_clk", + "uniphy0_port4_tx_clk", + "uniphy0_port5_rx_clk", + "uniphy0_port5_tx_clk", + "uniphy1_port5_rx_clk", + "uniphy1_port5_tx_clk", + "uniphy2_port6_rx_clk", + "uniphy2_port6_tx_clk", + "nss_port5_rx_clk_src", + "nss_port5_tx_clk_src"; + resets = <&gcc GCC_PPE_FULL_RESET>, + <&gcc GCC_UNIPHY0_SOFT_RESET>, + <&gcc GCC_UNIPHY0_XPCS_RESET>, + <&gcc GCC_UNIPHY1_SOFT_RESET>, + <&gcc GCC_UNIPHY1_XPCS_RESET>, + <&gcc GCC_UNIPHY2_SOFT_RESET>, + <&gcc GCC_UNIPHY2_XPCS_RESET>, + <&gcc GCC_NSSPORT1_RESET>, + <&gcc GCC_NSSPORT2_RESET>, + <&gcc GCC_NSSPORT3_RESET>, + <&gcc GCC_NSSPORT4_RESET>, + <&gcc GCC_NSSPORT5_RESET>, + <&gcc GCC_NSSPORT6_RESET>; + reset-names = "ppe_rst", "uniphy0_soft_rst", + "uniphy0_xpcs_rst", "uniphy1_soft_rst", + "uniphy1_xpcs_rst", "uniphy2_soft_rst", + "uniphy2_xpcs_rst", "nss_port1_rst", + "nss_port2_rst", "nss_port3_rst", + "nss_port4_rst", "nss_port5_rst", + "nss_port6_rst"; + mdio-bus = <&mdio>; + status = "disabled"; + }; + + ess-uniphy@7a00000 { + compatible = "qcom,ess-uniphy"; + reg = <0x7a00000 0x30000>; + uniphy_access_mode = "local bus"; + }; + + edma: edma@3ab00000 { + compatible = "qcom,edma"; + reg = <0x3ab00000 0x76900>; + reg-names = "edma-reg-base"; + qcom,txdesc-ring-start = <23>; + qcom,txdesc-rings = <1>; + qcom,txcmpl-ring-start = <7>; + qcom,txcmpl-rings = <1>; + qcom,rxfill-ring-start = <7>; + qcom,rxfill-rings = <1>; + qcom,rxdesc-ring-start = <15>; + qcom,rxdesc-rings = <1>; + interrupts = , + , + , + ; + resets = <&gcc GCC_EDMA_HW_RESET>; + reset-names = "edma_rst"; + status = "disabled"; + }; +}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi new file mode 100644 index 00000000000000..861460c932f859 --- /dev/null +++ b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "ipq8074-cpr-regulator.dtsi" + +&CPU0 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&CPU1 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&CPU2 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&CPU3 { + cpu-supply = <&apc_vreg>; + operating-points-v2 = <&cpu_opp_table>; + voltage-tolerance = <1>; +}; + +&cpus { + cpu_opp_table: cpu_opp_table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1017600000 { + opp-hz = /bits/ 64 <1017600000>; + opp-microvolt = <1>; + clock-latency-ns = <200000>; + }; + opp-1382400000 { + opp-hz = /bits/ 64 <1382400000>; + opp-microvolt = <2>; + clock-latency-ns = <200000>; + }; + opp-1651200000 { + opp-hz = /bits/ 64 <1651200000>; + opp-microvolt = <3>; + clock-latency-ns = <200000>; + }; + opp-1843200000 { + opp-hz = /bits/ 64 <1843200000>; + opp-microvolt = <4>; + clock-latency-ns = <200000>; + }; + opp-1920000000 { + opp-hz = /bits/ 64 <1920000000>; + opp-microvolt = <5>; + clock-latency-ns = <200000>; + }; + opp-2208000000 { + opp-hz = /bits/ 64 <2208000000>; + opp-microvolt = <6>; + clock-latency-ns = <200000>; + }; + }; +}; + +&cpu0_thermal { + trips { + cpu0_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu0_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu0_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu0_passive_low>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu0_passive_high>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu1_thermal { + trips { + cpu1_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu1_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu1_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu1_passive_low>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu1_passive_high>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu2_thermal { + trips { + cpu2_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu2_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu2_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu2_passive_low>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu2_passive_high>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu3_thermal { + trips { + cpu3_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu3_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu3_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu3_passive_low>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu3_passive_high>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cluster_thermal { + trips { + cluster_passive_low: cluster-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cluster_passive_high: cluster-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + + cluster_crit: cluster_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cluster_passive_low>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cluster_passive_high>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; diff --git a/target/linux/ipq807x/generic/target.mk b/target/linux/ipq807x/generic/target.mk new file mode 100644 index 00000000000000..f5cb1fb19b943f --- /dev/null +++ b/target/linux/ipq807x/generic/target.mk @@ -0,0 +1 @@ +BOARDNAME:=Generic diff --git a/target/linux/ipq807x/image/Makefile b/target/linux/ipq807x/image/Makefile new file mode 100644 index 00000000000000..4b0a657c0ced56 --- /dev/null +++ b/target/linux/ipq807x/image/Makefile @@ -0,0 +1,18 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk + +define Device/Default + PROFILES := Default + KERNEL_DEPENDS = $$(wildcard $(DTS_DIR)/$$(DEVICE_DTS).dts) + KERNEL_LOADADDR := 0x41000000 + DEVICE_DTS = $$(SOC)-$(lastword $(subst _, ,$(1))) + DEVICE_DTS_CONFIG := config@1 + DEVICE_DTS_DIR := $(DTS_DIR)/qcom + IMAGES := sysupgrade.bin + IMAGE/sysupgrade.bin = sysupgrade-tar | append-metadata + IMAGE/sysupgrade.bin/squashfs := +endef + +include $(SUBTARGET).mk + +$(eval $(call BuildImage)) diff --git a/target/linux/ipq807x/image/generic.mk b/target/linux/ipq807x/image/generic.mk new file mode 100644 index 00000000000000..8c6531b3f1d047 --- /dev/null +++ b/target/linux/ipq807x/image/generic.mk @@ -0,0 +1,100 @@ +define Device/FitImage + KERNEL_SUFFIX := -fit-uImage.itb + KERNEL = kernel-bin | gzip | fit gzip $$(DEVICE_DTS_DIR)/$$(DEVICE_DTS).dtb + KERNEL_NAME := Image +endef + +define Device/FitImageLzma + KERNEL_SUFFIX := -fit-uImage.itb + KERNEL = kernel-bin | lzma | fit lzma $$(DEVICE_DTS_DIR)/$$(DEVICE_DTS).dtb + KERNEL_NAME := Image +endef + +define Device/FitzImage + KERNEL_SUFFIX := -fit-zImage.itb + KERNEL = kernel-bin | fit none $$(DEVICE_DTS_DIR)/$$(DEVICE_DTS).dtb + KERNEL_NAME := zImage +endef + +define Device/UbiFit + KERNEL_IN_UBI := 1 + IMAGES := nand-factory.ubi nand-sysupgrade.bin + IMAGE/nand-factory.ubi := append-ubi + IMAGE/nand-sysupgrade.bin := sysupgrade-tar | append-metadata +endef + +define Device/edgecore_eap102 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Edgecore + DEVICE_MODEL := EAP102 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@ac02 + SOC := ipq8071 + DEVICE_PACKAGES := ipq-wifi-edgecore_eap102 + IMAGE/nand-factory.ubi := append-ubi | qsdk-ipq-factory-nand +endef +TARGET_DEVICES += edgecore_eap102 + +define Device/edimax_cax1800 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Edimax + DEVICE_MODEL := CAX1800 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@ac03 + SOC := ipq8070 + DEVICE_PACKAGES := ipq-wifi-edimax_cax1800 +endef +TARGET_DEVICES += edimax_cax1800 + +define Device/qnap_301w + $(call Device/FitImage) + DEVICE_VENDOR := QNAP + DEVICE_MODEL := 301w + DEVICE_DTS_CONFIG := config@hk01 + KERNEL_SIZE := 16384k + BLOCKSIZE := 512k + SOC := ipq8072 + IMAGES += factory.bin sysupgrade.bin + IMAGE/factory.bin := append-rootfs | pad-rootfs | pad-to 64k + IMAGE/sysupgrade.bin/squashfs := append-rootfs | pad-to 64k | sysupgrade-tar rootfs=$$$$@ | append-metadata + DEVICE_PACKAGES := ipq-wifi-qnap_301w e2fsprogs kmod-fs-ext4 losetup +endef +TARGET_DEVICES += qnap_301w + +define Device/redmi_ax6 + $(call Device/xiaomi_ax3600) + DEVICE_VENDOR := Redmi + DEVICE_MODEL := AX6 + DEVICE_PACKAGES := ipq-wifi-redmi_ax6 +endef +TARGET_DEVICES += redmi_ax6 + +define Device/xiaomi_ax3600 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Xiaomi + DEVICE_MODEL := AX3600 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@ac04 + SOC := ipq8071 + DEVICE_PACKAGES := ipq-wifi-xiaomi_ax3600 kmod-ath10k-ct-smallbuffers ath10k-firmware-qca9887-ct +endef +TARGET_DEVICES += xiaomi_ax3600 + +define Device/xiaomi_ax9000 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Xiaomi + DEVICE_MODEL := AX9000 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@hk14 + SOC := ipq8072 + DEVICE_PACKAGES := ipq-wifi-xiaomi_ax9000 kmod-ath10k-ct ath10k-firmware-qca9887-ct +endef +TARGET_DEVICES += xiaomi_ax9000 diff --git a/target/linux/ipq807x/patches-5.15/0001-v5.16-arm64-dts-qcom-ipq8074-add-SPMI-bus.patch b/target/linux/ipq807x/patches-5.15/0001-v5.16-arm64-dts-qcom-ipq8074-add-SPMI-bus.patch new file mode 100644 index 00000000000000..b80bc585be86ab --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0001-v5.16-arm64-dts-qcom-ipq8074-add-SPMI-bus.patch @@ -0,0 +1,48 @@ +From cbcdbf585ae995320bd2653d30633e40dc8c9f82 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 5 Sep 2021 18:58:16 +0200 +Subject: [PATCH 01/11] arm64: dts: qcom: ipq8074: add SPMI bus + +IPQ8074 uses SPMI for communication with the PMIC, so +since its already supported add the DT node for it. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20210905165816.655275-1-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 97f99663c132e..cc3a178eec8af 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -293,6 +293,25 @@ gcc: gcc@1800000 { + #reset-cells = <0x1>; + }; + ++ spmi_bus: spmi@200f000 { ++ compatible = "qcom,spmi-pmic-arb"; ++ reg = <0x0200f000 0x001000>, ++ <0x02400000 0x800000>, ++ <0x02c00000 0x800000>, ++ <0x03800000 0x200000>, ++ <0x0200a000 0x000700>; ++ reg-names = "core", "chnls", "obsrvr", "intr", "cnfg"; ++ interrupts = ; ++ interrupt-names = "periph_irq"; ++ qcom,ee = <0>; ++ qcom,channel = <0>; ++ #address-cells = <2>; ++ #size-cells = <0>; ++ interrupt-controller; ++ #interrupt-cells = <4>; ++ cell-index = <0>; ++ }; ++ + sdhc_1: sdhci@7824900 { + compatible = "qcom,sdhci-msm-v4"; + reg = <0x7824900 0x500>, <0x7824000 0x800>; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0002-v5.16-arm64-dts-qcom-Update-BAM-DMA-node-name-per-DT-schem.patch b/target/linux/ipq807x/patches-5.15/0002-v5.16-arm64-dts-qcom-Update-BAM-DMA-node-name-per-DT-schem.patch new file mode 100644 index 00000000000000..6b153b60a7a540 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0002-v5.16-arm64-dts-qcom-Update-BAM-DMA-node-name-per-DT-schem.patch @@ -0,0 +1,83 @@ +From 8919d53509023f880358b3a6cf87b17ca954d383 Mon Sep 17 00:00:00 2001 +From: Shawn Guo +Date: Tue, 31 Aug 2021 13:23:25 +0800 +Subject: [PATCH 02/11] arm64: dts: qcom: Update BAM DMA node name per DT + schema + +Follow dma-controller.yaml schema to use `dma-controller` as node name +of BAM DMA devices. + +Signed-off-by: Shawn Guo +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20210831052325.21229-1-shawn.guo@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 +- + arch/arm64/boot/dts/qcom/msm8996.dtsi | 4 ++-- + arch/arm64/boot/dts/qcom/msm8998.dtsi | 2 +- + arch/arm64/boot/dts/qcom/sdm845.dtsi | 2 +- + 4 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index cc3a178eec8af..aebd0949ac81a 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -212,7 +212,7 @@ prng: rng@e3000 { + status = "disabled"; + }; + +- cryptobam: dma@704000 { ++ cryptobam: dma-controller@704000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0x00704000 0x20000>; + interrupts = ; +diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi +index 6077c36019514..0704d00c3860b 100644 +--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi ++++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi +@@ -2683,7 +2683,7 @@ sdhc2: sdhci@74a4900 { + status = "disabled"; + }; + +- blsp1_dma: dma@7544000 { ++ blsp1_dma: dma-controller@7544000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0x07544000 0x2b000>; + interrupts = ; +@@ -2740,7 +2740,7 @@ blsp1_i2c3: i2c@7577000 { + status = "disabled"; + }; + +- blsp2_dma: dma@7584000 { ++ blsp2_dma: dma-controller@7584000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0x07584000 0x2b000>; + interrupts = ; +diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi +index 228339f81c327..ce88670baca99 100644 +--- a/arch/arm64/boot/dts/qcom/msm8998.dtsi ++++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi +@@ -2191,7 +2191,7 @@ blsp1_i2c6: i2c@c17a000 { + #size-cells = <0>; + }; + +- blsp2_dma: dma@c184000 { ++ blsp2_dma: dma-controller@c184000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0x0c184000 0x25000>; + interrupts = ; +diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi +index d20eacfc10176..5bcbbbd61e0f6 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi ++++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi +@@ -2312,7 +2312,7 @@ ufs_mem_phy_lanes: lanes@1d87400 { + }; + }; + +- cryptobam: dma@1dc4000 { ++ cryptobam: dma-controller@1dc4000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0 0x01dc4000 0 0x24000>; + interrupts = ; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0003-v5.16-arm64-dts-qcom-ipq8074-Add-QUP5-I2C-node.patch b/target/linux/ipq807x/patches-5.15/0003-v5.16-arm64-dts-qcom-ipq8074-Add-QUP5-I2C-node.patch new file mode 100644 index 00000000000000..2bd6051f43f0ae --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0003-v5.16-arm64-dts-qcom-ipq8074-Add-QUP5-I2C-node.patch @@ -0,0 +1,45 @@ +From 3dbde4cec93b909ed1aa7148cbd8208293038cda Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Fri, 1 Oct 2021 22:54:21 +0800 +Subject: [PATCH 03/11] arm64: dts: qcom: ipq8074: Add QUP5 I2C node + +Add node to support the QUP5 I2C controller inside of IPQ8074. +It is exactly the same as QUP2 controllers. +Some routers like ZTE MF269 use this bus. + +Signed-off-by: Chukun Pan +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20211001145421.18302-1-amadeus@jmu.edu.cn +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index aebd0949ac81a..9ab4654e39d3b 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -430,6 +430,21 @@ blsp1_i2c3: i2c@78b7000 { + status = "disabled"; + }; + ++ blsp1_i2c5: i2c@78b9000 { ++ compatible = "qcom,i2c-qup-v2.2.1"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x78b9000 0x600>; ++ interrupts = ; ++ clocks = <&gcc GCC_BLSP1_AHB_CLK>, ++ <&gcc GCC_BLSP1_QUP5_I2C_APPS_CLK>; ++ clock-names = "iface", "core"; ++ clock-frequency = <400000>; ++ dmas = <&blsp_dma 21>, <&blsp_dma 20>; ++ dma-names = "rx", "tx"; ++ status = "disabled"; ++ }; ++ + blsp1_i2c6: i2c@78ba000 { + compatible = "qcom,i2c-qup-v2.2.1"; + #address-cells = <1>; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0004-v5.16-arm64-dts-qcom-msm8996-Move-clock-cells-to-QMP-PHY-c.patch b/target/linux/ipq807x/patches-5.15/0004-v5.16-arm64-dts-qcom-msm8996-Move-clock-cells-to-QMP-PHY-c.patch new file mode 100644 index 00000000000000..282d6bfea4ec9a --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0004-v5.16-arm64-dts-qcom-msm8996-Move-clock-cells-to-QMP-PHY-c.patch @@ -0,0 +1,145 @@ +From b3571ad1a3b534498936f5709ce68560b77b2d4c Mon Sep 17 00:00:00 2001 +From: Shawn Guo +Date: Wed, 29 Sep 2021 11:42:46 +0800 +Subject: [PATCH 04/11] arm64: dts: qcom: msm8996: Move '#clock-cells' to QMP + PHY child node + +'#clock-cells' is a required property of QMP PHY child node, not itself. +Move it to fix the dtbs_check warnings. + +There are only '#clock-cells' removal from SM8350 QMP PHY nodes, because +child nodes already have the property. + +Signed-off-by: Shawn Guo +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20210929034253.24570-4-shawn.guo@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++-- + arch/arm64/boot/dts/qcom/msm8996.dtsi | 4 ++-- + arch/arm64/boot/dts/qcom/msm8998.dtsi | 2 +- + arch/arm64/boot/dts/qcom/sm8350.dtsi | 3 --- + 4 files changed, 5 insertions(+), 8 deletions(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 9ab4654e39d3b..bf579a90a0fdf 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -91,7 +91,6 @@ soc: soc { + ssphy_1: phy@58000 { + compatible = "qcom,ipq8074-qmp-usb3-phy"; + reg = <0x00058000 0x1c4>; +- #clock-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges; +@@ -112,6 +111,7 @@ usb1_ssphy: lane@58200 { + <0x00058800 0x1f8>, /* PCS */ + <0x00058600 0x044>; /* PCS misc*/ + #phy-cells = <0>; ++ #clock-cells = <1>; + clocks = <&gcc GCC_USB1_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "gcc_usb1_pipe_clk_src"; +@@ -134,7 +134,6 @@ qusb_phy_1: phy@59000 { + ssphy_0: phy@78000 { + compatible = "qcom,ipq8074-qmp-usb3-phy"; + reg = <0x00078000 0x1c4>; +- #clock-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges; +@@ -155,6 +154,7 @@ usb0_ssphy: lane@78200 { + <0x00078800 0x1f8>, /* PCS */ + <0x00078600 0x044>; /* PCS misc*/ + #phy-cells = <0>; ++ #clock-cells = <1>; + clocks = <&gcc GCC_USB0_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "gcc_usb0_pipe_clk_src"; +diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi +index 0704d00c3860b..4f2552b0ad10b 100644 +--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi ++++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi +@@ -582,7 +582,6 @@ soc: soc { + pcie_phy: phy@34000 { + compatible = "qcom,msm8996-qmp-pcie-phy"; + reg = <0x00034000 0x488>; +- #clock-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges; +@@ -604,6 +603,7 @@ pciephy_0: lane@35000 { + <0x00035400 0x1dc>; + #phy-cells = <0>; + ++ #clock-cells = <1>; + clock-output-names = "pcie_0_pipe_clk_src"; + clocks = <&gcc GCC_PCIE_0_PIPE_CLK>; + clock-names = "pipe0"; +@@ -2583,7 +2583,6 @@ usb3_dwc3: dwc3@6a00000 { + usb3phy: phy@7410000 { + compatible = "qcom,msm8996-qmp-usb3-phy"; + reg = <0x07410000 0x1c4>; +- #clock-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges; +@@ -2604,6 +2603,7 @@ ssusb_phy_0: lane@7410200 { + <0x07410600 0x1a8>; + #phy-cells = <0>; + ++ #clock-cells = <1>; + clock-output-names = "usb3_phy_pipe_clk_src"; + clocks = <&gcc GCC_USB3_PHY_PIPE_CLK>; + clock-names = "pipe0"; +diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi +index ce88670baca99..5146c9a133e19 100644 +--- a/arch/arm64/boot/dts/qcom/msm8998.dtsi ++++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi +@@ -1985,7 +1985,6 @@ usb3phy: phy@c010000 { + compatible = "qcom,msm8998-qmp-usb3-phy"; + reg = <0x0c010000 0x18c>; + status = "disabled"; +- #clock-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges; +@@ -2006,6 +2005,7 @@ usb1_ssphy: lane@c010200 { + <0xc010600 0x128>, + <0xc010800 0x200>; + #phy-cells = <0>; ++ #clock-cells = <1>; + clocks = <&gcc GCC_USB3_PHY_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "usb3_phy_pipe_clk_src"; +diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi +index 9ffb7355850c7..2b7c6963469d2 100644 +--- a/arch/arm64/boot/dts/qcom/sm8350.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi +@@ -1112,7 +1112,6 @@ ufs_mem_phy: phy@1d87000 { + reg = <0 0x01d87000 0 0xe10>; + #address-cells = <2>; + #size-cells = <2>; +- #clock-cells = <1>; + ranges; + clock-names = "ref", + "ref_aux"; +@@ -1247,7 +1246,6 @@ usb_1_qmpphy: phy-wrapper@88e9000 { + <0 0x088e8000 0 0x20>; + reg-names = "reg-base", "dp_com"; + status = "disabled"; +- #clock-cells = <1>; + #address-cells = <2>; + #size-cells = <2>; + ranges; +@@ -1280,7 +1278,6 @@ usb_2_qmpphy: phy-wrapper@88eb000 { + compatible = "qcom,sm8350-qmp-usb3-uni-phy"; + reg = <0 0x088eb000 0 0x200>; + status = "disabled"; +- #clock-cells = <1>; + #address-cells = <2>; + #size-cells = <2>; + ranges; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0005-v5.16-arm64-dts-qcom-Correct-QMP-PHY-child-node-name.patch b/target/linux/ipq807x/patches-5.15/0005-v5.16-arm64-dts-qcom-Correct-QMP-PHY-child-node-name.patch new file mode 100644 index 00000000000000..2ac7e2e46d88dc --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0005-v5.16-arm64-dts-qcom-Correct-QMP-PHY-child-node-name.patch @@ -0,0 +1,292 @@ +From af52ce720bf8ed2bb2ab8059d3c09371d62254e2 Mon Sep 17 00:00:00 2001 +From: Shawn Guo +Date: Wed, 29 Sep 2021 11:42:47 +0800 +Subject: [PATCH 05/11] arm64: dts: qcom: Correct QMP PHY child node name + +Many child nodes of QMP PHY are named without following bindings schema +and causing dtbs_check warnings like below. + +phy@1c06000: 'lane@1c06800' does not match any of the regexes: '^phy@[0-9a-f]+$' + arch/arm64/boot/dts/qcom/msm8998-asus-novago-tp370ql.dt.yaml + arch/arm64/boot/dts/qcom/msm8998-hp-envy-x2.dt.yaml + arch/arm64/boot/dts/qcom/msm8998-lenovo-miix-630.dt.yaml + arch/arm64/boot/dts/qcom/msm8998-mtp.dt.yaml + arch/arm64/boot/dts/qcom/msm8998-oneplus-cheeseburger.dt.yaml + arch/arm64/boot/dts/qcom/msm8998-oneplus-dumpling.dt.yaml + +Correct them to fix the warnings. + +Signed-off-by: Shawn Guo +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20210929034253.24570-5-shawn.guo@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq6018.dtsi | 2 +- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++-- + arch/arm64/boot/dts/qcom/msm8996.dtsi | 10 +++++----- + arch/arm64/boot/dts/qcom/msm8998.dtsi | 6 +++--- + arch/arm64/boot/dts/qcom/sdm845.dtsi | 10 +++++----- + arch/arm64/boot/dts/qcom/sm8150.dtsi | 6 +++--- + arch/arm64/boot/dts/qcom/sm8250.dtsi | 10 +++++----- + arch/arm64/boot/dts/qcom/sm8350.dtsi | 2 +- + 8 files changed, 25 insertions(+), 25 deletions(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq6018.dtsi b/arch/arm64/boot/dts/qcom/ipq6018.dtsi +index ce4c2b4a5fc07..30ac0b2e8c896 100644 +--- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi +@@ -401,7 +401,7 @@ pcie_phy: phy@84000 { + reset-names = "phy", + "common"; + +- pcie_phy0: lane@84200 { ++ pcie_phy0: phy@84200 { + reg = <0x0 0x84200 0x0 0x16c>, /* Serdes Tx */ + <0x0 0x84400 0x0 0x200>, /* Serdes Rx */ + <0x0 0x84800 0x0 0x4f4>; /* PCS: Lane0, COM, PCIE */ +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index bf579a90a0fdf..3e30e1a0574ed 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -105,7 +105,7 @@ ssphy_1: phy@58000 { + reset-names = "phy","common"; + status = "disabled"; + +- usb1_ssphy: lane@58200 { ++ usb1_ssphy: phy@58200 { + reg = <0x00058200 0x130>, /* Tx */ + <0x00058400 0x200>, /* Rx */ + <0x00058800 0x1f8>, /* PCS */ +@@ -148,7 +148,7 @@ ssphy_0: phy@78000 { + reset-names = "phy","common"; + status = "disabled"; + +- usb0_ssphy: lane@78200 { ++ usb0_ssphy: phy@78200 { + reg = <0x00078200 0x130>, /* Tx */ + <0x00078400 0x200>, /* Rx */ + <0x00078800 0x1f8>, /* PCS */ +diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi +index 4f2552b0ad10b..05fd6bb617c6f 100644 +--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi ++++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi +@@ -597,7 +597,7 @@ pcie_phy: phy@34000 { + reset-names = "phy", "common", "cfg"; + status = "disabled"; + +- pciephy_0: lane@35000 { ++ pciephy_0: phy@35000 { + reg = <0x00035000 0x130>, + <0x00035200 0x200>, + <0x00035400 0x1dc>; +@@ -611,7 +611,7 @@ pciephy_0: lane@35000 { + reset-names = "lane0"; + }; + +- pciephy_1: lane@36000 { ++ pciephy_1: phy@36000 { + reg = <0x00036000 0x130>, + <0x00036200 0x200>, + <0x00036400 0x1dc>; +@@ -624,7 +624,7 @@ pciephy_1: lane@36000 { + reset-names = "lane1"; + }; + +- pciephy_2: lane@37000 { ++ pciephy_2: phy@37000 { + reg = <0x00037000 0x130>, + <0x00037200 0x200>, + <0x00037400 0x1dc>; +@@ -1743,7 +1743,7 @@ ufsphy: phy@627000 { + reset-names = "ufsphy"; + status = "disabled"; + +- ufsphy_lane: lanes@627400 { ++ ufsphy_lane: phy@627400 { + reg = <0x627400 0x12c>, + <0x627600 0x200>, + <0x627c00 0x1b4>; +@@ -2597,7 +2597,7 @@ usb3phy: phy@7410000 { + reset-names = "phy", "common"; + status = "disabled"; + +- ssusb_phy_0: lane@7410200 { ++ ssusb_phy_0: phy@7410200 { + reg = <0x07410200 0x200>, + <0x07410400 0x130>, + <0x07410600 0x1a8>; +diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi +index 5146c9a133e19..e6464b902da97 100644 +--- a/arch/arm64/boot/dts/qcom/msm8998.dtsi ++++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi +@@ -994,7 +994,7 @@ pcie_phy: phy@1c06000 { + vdda-phy-supply = <&vreg_l1a_0p875>; + vdda-pll-supply = <&vreg_l2a_1p2>; + +- pciephy: lane@1c06800 { ++ pciephy: phy@1c06800 { + reg = <0x01c06200 0x128>, <0x01c06400 0x1fc>, <0x01c06800 0x20c>; + #phy-cells = <0>; + +@@ -1066,7 +1066,7 @@ ufsphy: phy@1da7000 { + reset-names = "ufsphy"; + resets = <&ufshc 0>; + +- ufsphy_lanes: lanes@1da7400 { ++ ufsphy_lanes: phy@1da7400 { + reg = <0x01da7400 0x128>, + <0x01da7600 0x1fc>, + <0x01da7c00 0x1dc>, +@@ -1998,7 +1998,7 @@ usb3phy: phy@c010000 { + <&gcc GCC_USB3PHY_PHY_BCR>; + reset-names = "phy", "common"; + +- usb1_ssphy: lane@c010200 { ++ usb1_ssphy: phy@c010200 { + reg = <0xc010200 0x128>, + <0xc010400 0x200>, + <0xc010c00 0x20c>, +diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi +index 5bcbbbd61e0f6..b5ed13e0fdf56 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi ++++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi +@@ -2064,7 +2064,7 @@ pcie0_phy: phy@1c06000 { + + status = "disabled"; + +- pcie0_lane: lanes@1c06200 { ++ pcie0_lane: phy@1c06200 { + reg = <0 0x01c06200 0 0x128>, + <0 0x01c06400 0 0x1fc>, + <0 0x01c06800 0 0x218>, +@@ -2174,7 +2174,7 @@ pcie1_phy: phy@1c0a000 { + + status = "disabled"; + +- pcie1_lane: lanes@1c06200 { ++ pcie1_lane: phy@1c06200 { + reg = <0 0x01c0a800 0 0x800>, + <0 0x01c0a800 0 0x800>, + <0 0x01c0b800 0 0x400>; +@@ -2302,7 +2302,7 @@ ufs_mem_phy: phy@1d87000 { + reset-names = "ufsphy"; + status = "disabled"; + +- ufs_mem_phy_lanes: lanes@1d87400 { ++ ufs_mem_phy_lanes: phy@1d87400 { + reg = <0 0x01d87400 0 0x108>, + <0 0x01d87600 0 0x1e0>, + <0 0x01d87c00 0 0x1dc>, +@@ -3699,7 +3699,7 @@ usb_1_qmpphy: phy@88e9000 { + <&gcc GCC_USB3_PHY_PRIM_BCR>; + reset-names = "phy", "common"; + +- usb_1_ssphy: lanes@88e9200 { ++ usb_1_ssphy: phy@88e9200 { + reg = <0 0x088e9200 0 0x128>, + <0 0x088e9400 0 0x200>, + <0 0x088e9c00 0 0x218>, +@@ -3732,7 +3732,7 @@ usb_2_qmpphy: phy@88eb000 { + <&gcc GCC_USB3_PHY_SEC_BCR>; + reset-names = "phy", "common"; + +- usb_2_ssphy: lane@88eb200 { ++ usb_2_ssphy: phy@88eb200 { + reg = <0 0x088eb200 0 0x128>, + <0 0x088eb400 0 0x1fc>, + <0 0x088eb800 0 0x218>, +diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi +index f347f752d536d..74c4acb8598b1 100644 +--- a/arch/arm64/boot/dts/qcom/sm8150.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi +@@ -1692,7 +1692,7 @@ ufs_mem_phy: phy@1d87000 { + reset-names = "ufsphy"; + status = "disabled"; + +- ufs_mem_phy_lanes: lanes@1d87400 { ++ ufs_mem_phy_lanes: phy@1d87400 { + reg = <0 0x01d87400 0 0x108>, + <0 0x01d87600 0 0x1e0>, + <0 0x01d87c00 0 0x1dc>, +@@ -3010,7 +3010,7 @@ usb_1_qmpphy: phy@88e9000 { + <&gcc GCC_USB3_PHY_PRIM_BCR>; + reset-names = "phy", "common"; + +- usb_1_ssphy: lanes@88e9200 { ++ usb_1_ssphy: phy@88e9200 { + reg = <0 0x088e9200 0 0x200>, + <0 0x088e9400 0 0x200>, + <0 0x088e9c00 0 0x218>, +@@ -3043,7 +3043,7 @@ usb_2_qmpphy: phy@88eb000 { + <&gcc GCC_USB3_PHY_SEC_BCR>; + reset-names = "phy", "common"; + +- usb_2_ssphy: lane@88eb200 { ++ usb_2_ssphy: phy@88eb200 { + reg = <0 0x088eb200 0 0x200>, + <0 0x088eb400 0 0x200>, + <0 0x088eb800 0 0x800>, +diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi +index 2786e2c8e5659..81af8cdc18221 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi +@@ -1463,7 +1463,7 @@ pcie0_phy: phy@1c06000 { + + status = "disabled"; + +- pcie0_lane: lanes@1c06200 { ++ pcie0_lane: phy@1c06200 { + reg = <0 0x1c06200 0 0x170>, /* tx */ + <0 0x1c06400 0 0x200>, /* rx */ + <0 0x1c06800 0 0x1f0>, /* pcs */ +@@ -1567,7 +1567,7 @@ pcie1_phy: phy@1c0e000 { + + status = "disabled"; + +- pcie1_lane: lanes@1c0e200 { ++ pcie1_lane: phy@1c0e200 { + reg = <0 0x1c0e200 0 0x170>, /* tx0 */ + <0 0x1c0e400 0 0x200>, /* rx0 */ + <0 0x1c0ea00 0 0x1f0>, /* pcs */ +@@ -1673,7 +1673,7 @@ pcie2_phy: phy@1c16000 { + + status = "disabled"; + +- pcie2_lane: lanes@1c16200 { ++ pcie2_lane: phy@1c16200 { + reg = <0 0x1c16200 0 0x170>, /* tx0 */ + <0 0x1c16400 0 0x200>, /* rx0 */ + <0 0x1c16a00 0 0x1f0>, /* pcs */ +@@ -1750,7 +1750,7 @@ ufs_mem_phy: phy@1d87000 { + reset-names = "ufsphy"; + status = "disabled"; + +- ufs_mem_phy_lanes: lanes@1d87400 { ++ ufs_mem_phy_lanes: phy@1d87400 { + reg = <0 0x01d87400 0 0x108>, + <0 0x01d87600 0 0x1e0>, + <0 0x01d87c00 0 0x1dc>, +@@ -2330,7 +2330,7 @@ usb_2_qmpphy: phy@88eb000 { + <&gcc GCC_USB3_PHY_SEC_BCR>; + reset-names = "phy", "common"; + +- usb_2_ssphy: lanes@88eb200 { ++ usb_2_ssphy: phy@88eb200 { + reg = <0 0x088eb200 0 0x200>, + <0 0x088eb400 0 0x200>, + <0 0x088eb800 0 0x800>; +diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi +index 2b7c6963469d2..7d75be1be386f 100644 +--- a/arch/arm64/boot/dts/qcom/sm8350.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi +@@ -1122,7 +1122,7 @@ ufs_mem_phy: phy@1d87000 { + reset-names = "ufsphy"; + status = "disabled"; + +- ufs_mem_phy_lanes: lanes@1d87400 { ++ ufs_mem_phy_lanes: phy@1d87400 { + reg = <0 0x01d87400 0 0x108>, + <0 0x01d87600 0 0x1e0>, + <0 0x01d87c00 0 0x1dc>, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0006-v5.16-arm64-dts-qcom-Fix-IPQ8074-PCIe-PHY-nodes.patch b/target/linux/ipq807x/patches-5.15/0006-v5.16-arm64-dts-qcom-Fix-IPQ8074-PCIe-PHY-nodes.patch new file mode 100644 index 00000000000000..56a2144534317c --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0006-v5.16-arm64-dts-qcom-Fix-IPQ8074-PCIe-PHY-nodes.patch @@ -0,0 +1,99 @@ +From 24c82d74786b419c99ef404dad0540e3edb115dd Mon Sep 17 00:00:00 2001 +From: Shawn Guo +Date: Wed, 29 Sep 2021 11:42:51 +0800 +Subject: [PATCH 06/11] arm64: dts: qcom: Fix IPQ8074 PCIe PHY nodes + +IPQ8074 PCIe PHY nodes are broken in the many ways: + +- '#address-cells', '#size-cells' and 'ranges' are missing. +- Child phy/lane node is missing, and the child properties like + '#phy-cells' and 'clocks' are mistakenly put into parent node. +- The clocks properties for parent node are missing. + +Fix them to get the nodes comply with the bindings schema. + +Signed-off-by: Shawn Guo +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20210929034253.24570-9-shawn.guo@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 46 +++++++++++++++++++++------ + 1 file changed, 36 insertions(+), 10 deletions(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 3e30e1a0574ed..6c6a0f853669e 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -174,34 +174,60 @@ qusb_phy_0: phy@79000 { + status = "disabled"; + }; + +- pcie_phy0: phy@86000 { ++ pcie_qmp0: phy@86000 { + compatible = "qcom,ipq8074-qmp-pcie-phy"; + reg = <0x00086000 0x1000>; +- #phy-cells = <0>; +- clocks = <&gcc GCC_PCIE0_PIPE_CLK>; +- clock-names = "pipe_clk"; +- clock-output-names = "pcie20_phy0_pipe_clk"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; + ++ clocks = <&gcc GCC_PCIE0_AUX_CLK>, ++ <&gcc GCC_PCIE0_AHB_CLK>; ++ clock-names = "aux", "cfg_ahb"; + resets = <&gcc GCC_PCIE0_PHY_BCR>, + <&gcc GCC_PCIE0PHY_PHY_BCR>; + reset-names = "phy", + "common"; + status = "disabled"; ++ ++ pcie_phy0: phy@86200 { ++ reg = <0x86200 0x16c>, ++ <0x86400 0x200>, ++ <0x86800 0x4f4>; ++ #phy-cells = <0>; ++ #clock-cells = <0>; ++ clocks = <&gcc GCC_PCIE0_PIPE_CLK>; ++ clock-names = "pipe0"; ++ clock-output-names = "pcie_0_pipe_clk"; ++ }; + }; + +- pcie_phy1: phy@8e000 { ++ pcie_qmp1: phy@8e000 { + compatible = "qcom,ipq8074-qmp-pcie-phy"; + reg = <0x0008e000 0x1000>; +- #phy-cells = <0>; +- clocks = <&gcc GCC_PCIE1_PIPE_CLK>; +- clock-names = "pipe_clk"; +- clock-output-names = "pcie20_phy1_pipe_clk"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; + ++ clocks = <&gcc GCC_PCIE1_AUX_CLK>, ++ <&gcc GCC_PCIE1_AHB_CLK>; ++ clock-names = "aux", "cfg_ahb"; + resets = <&gcc GCC_PCIE1_PHY_BCR>, + <&gcc GCC_PCIE1PHY_PHY_BCR>; + reset-names = "phy", + "common"; + status = "disabled"; ++ ++ pcie_phy1: phy@8e200 { ++ reg = <0x8e200 0x16c>, ++ <0x8e400 0x200>, ++ <0x8e800 0x4f4>; ++ #phy-cells = <0>; ++ #clock-cells = <0>; ++ clocks = <&gcc GCC_PCIE1_PIPE_CLK>; ++ clock-names = "pipe0"; ++ clock-output-names = "pcie_1_pipe_clk"; ++ }; + }; + + prng: rng@e3000 { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0007-v5.17-arm64-dts-qcom-ipq8074-add-MDIO-bus.patch b/target/linux/ipq807x/patches-5.15/0007-v5.17-arm64-dts-qcom-ipq8074-add-MDIO-bus.patch new file mode 100644 index 00000000000000..387158ce1b4b00 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0007-v5.17-arm64-dts-qcom-ipq8074-add-MDIO-bus.patch @@ -0,0 +1,41 @@ +From af9bd5108e9008bc5c39405d834c076279823a91 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 7 Oct 2021 13:58:46 +0200 +Subject: [PATCH 07/11] arm64: dts: qcom: ipq8074: add MDIO bus + +IPQ8074 uses an IPQ4019 compatible MDIO controller that is already +supported in the kernel, so add the DT node in order to use it. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20211007115846.26255-1-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 6c6a0f853669e..e6cc261201efe 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -230,6 +230,18 @@ pcie_phy1: phy@8e200 { + }; + }; + ++ mdio: mdio@90000 { ++ compatible = "qcom,ipq4019-mdio"; ++ reg = <0x00090000 0x64>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ clocks = <&gcc GCC_MDIO_AHB_CLK>; ++ clock-names = "gcc_mdio_ahb_clk"; ++ ++ status = "disabled"; ++ }; ++ + prng: rng@e3000 { + compatible = "qcom,prng-ee"; + reg = <0x000e3000 0x1000>; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0008-v5.18-arm64-dts-qcom-ipq8074-add-SMEM-support.patch b/target/linux/ipq807x/patches-5.15/0008-v5.18-arm64-dts-qcom-ipq8074-add-SMEM-support.patch new file mode 100644 index 00000000000000..63b99abf6f03ac --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0008-v5.18-arm64-dts-qcom-ipq8074-add-SMEM-support.patch @@ -0,0 +1,56 @@ +From dcbde5962d07cc86de684e44a0ec2f7ff88011d7 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 6 Jan 2022 22:25:12 +0100 +Subject: [PATCH 08/11] arm64: dts: qcom: ipq8074: add SMEM support + +IPQ8074 uses SMEM like other modern QCA SoC-s, so since its already +supported by the kernel add the required DT nodes. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220106212512.1970828-1-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index e6cc261201efe..bd70092b7156b 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -76,6 +76,20 @@ psci { + method = "smc"; + }; + ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ smem@4ab00000 { ++ compatible = "qcom,smem"; ++ reg = <0x0 0x4ab00000 0x0 0x00100000>; ++ no-map; ++ ++ hwlocks = <&tcsr_mutex 0>; ++ }; ++ }; ++ + firmware { + scm { + compatible = "qcom,scm-ipq8074", "qcom,scm"; +@@ -331,6 +345,12 @@ gcc: gcc@1800000 { + #reset-cells = <0x1>; + }; + ++ tcsr_mutex: hwlock@1905000 { ++ compatible = "qcom,tcsr-mutex"; ++ reg = <0x01905000 0x20000>; ++ #hwlock-cells = <1>; ++ }; ++ + spmi_bus: spmi@200f000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0x0200f000 0x001000>, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0009-v5.18-arm64-dts-qcom-ipq8074-add-the-reserved-memory-node.patch b/target/linux/ipq807x/patches-5.15/0009-v5.18-arm64-dts-qcom-ipq8074-add-the-reserved-memory-node.patch new file mode 100644 index 00000000000000..8e6ff98c7aa9ee --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0009-v5.18-arm64-dts-qcom-ipq8074-add-the-reserved-memory-node.patch @@ -0,0 +1,35 @@ +From 651e29df084ab341398ad3a9e0de3380432ede13 Mon Sep 17 00:00:00 2001 +From: Kathiravan T +Date: Fri, 7 Jan 2022 18:24:38 +0530 +Subject: [PATCH 09/11] arm64: dts: qcom: ipq8074: add the reserved-memory node + +On IPQ8074, 4MB of memory is needed for TZ. So mark that region +as reserved. + +Signed-off-by: Kathiravan T +[bjorn: Squash with existing reserved-memory node] +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/1641560078-860-1-git-send-email-quic_kathirav@quicinc.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index bd70092b7156b..27624f5a56ba8 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -88,6 +88,11 @@ smem@4ab00000 { + + hwlocks = <&tcsr_mutex 0>; + }; ++ ++ memory@4ac00000 { ++ no-map; ++ reg = <0x0 0x4ac00000 0x0 0x00400000>; ++ }; + }; + + firmware { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0010-v5.18-arm64-dts-qcom-ipq8074-enable-the-GICv2m-support.patch b/target/linux/ipq807x/patches-5.15/0010-v5.18-arm64-dts-qcom-ipq8074-enable-the-GICv2m-support.patch new file mode 100644 index 00000000000000..4658a82c14745e --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0010-v5.18-arm64-dts-qcom-ipq8074-enable-the-GICv2m-support.patch @@ -0,0 +1,41 @@ +From 4908f8d304c28722e7d2c9fe063b795bf98dd5d5 Mon Sep 17 00:00:00 2001 +From: Kathiravan T +Date: Tue, 8 Feb 2022 21:05:24 +0530 +Subject: [PATCH 10/11] arm64: dts: qcom: ipq8074: enable the GICv2m support + +GIC used in the IPQ8074 SoCs has one instance of the GICv2m extension, +which supports upto 32 MSI interrupts. Lets add support for the same. + +Signed-off-by: Kathiravan T +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/1644334525-11577-2-git-send-email-quic_kathirav@quicinc.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 27624f5a56ba8..642f9e71dbcfe 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -634,9 +634,18 @@ dwc_1: dwc3@8c00000 { + + intc: interrupt-controller@b000000 { + compatible = "qcom,msm-qgic2"; ++ #address-cells = <1>; ++ #size-cells = <1>; + interrupt-controller; + #interrupt-cells = <0x3>; + reg = <0x0b000000 0x1000>, <0x0b002000 0x1000>; ++ ranges = <0 0xb00a000 0xffd>; ++ ++ v2m@0 { ++ compatible = "arm,gic-v2m-frame"; ++ msi-controller; ++ reg = <0x0 0xffd>; ++ }; + }; + + timer { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0011-v5.18-arm64-dts-qcom-ipq8074-drop-the-clock-frequency-prop.patch b/target/linux/ipq807x/patches-5.15/0011-v5.18-arm64-dts-qcom-ipq8074-drop-the-clock-frequency-prop.patch new file mode 100644 index 00000000000000..fc143870d3fff5 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0011-v5.18-arm64-dts-qcom-ipq8074-drop-the-clock-frequency-prop.patch @@ -0,0 +1,31 @@ +From 2a6f117903e6817f65c32da9597c7bd304bb5694 Mon Sep 17 00:00:00 2001 +From: Kathiravan T +Date: Wed, 2 Feb 2022 22:05:08 +0530 +Subject: [PATCH 11/11] arm64: dts: qcom: ipq8074: drop the clock-frequency + property + +Drop the clock-frequency property from the MMIO timer node, since it +is already configured by the bootloader. + +Signed-off-by: Kathiravan T +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/1643819709-5410-2-git-send-email-quic_kathirav@quicinc.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 642f9e71dbcfe..d80b1cefab100 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -670,7 +670,6 @@ timer@b120000 { + ranges; + compatible = "arm,armv7-timer-mem"; + reg = <0x0b120000 0x1000>; +- clock-frequency = <19200000>; + + frame@b120000 { + frame-number = <0>; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0012-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch b/target/linux/ipq807x/patches-5.15/0012-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch new file mode 100644 index 00000000000000..ff3b84c247fecb --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0012-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch @@ -0,0 +1,172 @@ +From 8a47f6e8d82adccef00329031f04d88cddab6a56 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson +Date: Thu, 30 Sep 2021 11:21:10 -0700 +Subject: [PATCH 12/14] soc: qcom: smem: Support reserved-memory description + +Practically all modern Qualcomm platforms has a single reserved-memory +region for SMEM. So rather than having to describe SMEM in the form of a +node with a reference to a reserved-memory node, allow the SMEM device +to be instantiated directly from the reserved-memory node. + +The current means of falling back to dereferencing the "memory-region" +is kept as a fallback, if it's determined that the SMEM node is a +reserved-memory node. + +The "qcom,smem" compatible is added to the reserved_mem_matches list, to +allow the reserved-memory device to be probed. + +In order to retain the readability of the code, the resolution of +resources is split from the actual ioremapping. + +Signed-off-by: Bjorn Andersson +Acked-by: Rob Herring +Reviewed-by: Vladimir Zapolskiy +Link: https://lore.kernel.org/r/20210930182111.57353-4-bjorn.andersson@linaro.org +--- + drivers/of/platform.c | 1 + + drivers/soc/qcom/smem.c | 57 ++++++++++++++++++++++++++++------------- + 2 files changed, 40 insertions(+), 18 deletions(-) + +diff --git a/drivers/of/platform.c b/drivers/of/platform.c +index 74afbb7a4f5ec..19ba8e4d4f8dc 100644 +--- a/drivers/of/platform.c ++++ b/drivers/of/platform.c +@@ -509,6 +509,7 @@ EXPORT_SYMBOL_GPL(of_platform_default_populate); + static const struct of_device_id reserved_mem_matches[] = { + { .compatible = "qcom,rmtfs-mem" }, + { .compatible = "qcom,cmd-db" }, ++ { .compatible = "qcom,smem" }, + { .compatible = "ramoops" }, + { .compatible = "nvmem-rmem" }, + {} +diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c +index 4fb5aeeb08439..c7e519bfdc8ae 100644 +--- a/drivers/soc/qcom/smem.c ++++ b/drivers/soc/qcom/smem.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -240,7 +241,7 @@ static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */ + * @size: size of the memory region + */ + struct smem_region { +- u32 aux_base; ++ phys_addr_t aux_base; + void __iomem *virt_base; + size_t size; + }; +@@ -499,7 +500,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, + for (i = 0; i < smem->num_regions; i++) { + region = &smem->regions[i]; + +- if (region->aux_base == aux_base || !aux_base) { ++ if ((u32)region->aux_base == aux_base || !aux_base) { + if (size != NULL) + *size = le32_to_cpu(entry->size); + return region->virt_base + le32_to_cpu(entry->offset); +@@ -664,7 +665,7 @@ phys_addr_t qcom_smem_virt_to_phys(void *p) + if (p < region->virt_base + region->size) { + u64 offset = p - region->virt_base; + +- return (phys_addr_t)region->aux_base + offset; ++ return region->aux_base + offset; + } + } + +@@ -863,12 +864,12 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host) + return 0; + } + +-static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev, +- const char *name, int i) ++static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name, ++ struct smem_region *region) + { ++ struct device *dev = smem->dev; + struct device_node *np; + struct resource r; +- resource_size_t size; + int ret; + + np = of_parse_phandle(dev->of_node, name, 0); +@@ -881,13 +882,9 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev, + of_node_put(np); + if (ret) + return ret; +- size = resource_size(&r); + +- smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size); +- if (!smem->regions[i].virt_base) +- return -ENOMEM; +- smem->regions[i].aux_base = (u32)r.start; +- smem->regions[i].size = size; ++ region->aux_base = r.start; ++ region->size = resource_size(&r); + + return 0; + } +@@ -895,12 +892,14 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev, + static int qcom_smem_probe(struct platform_device *pdev) + { + struct smem_header *header; ++ struct reserved_mem *rmem; + struct qcom_smem *smem; + size_t array_size; + int num_regions; + int hwlock_id; + u32 version; + int ret; ++ int i; + + num_regions = 1; + if (of_find_property(pdev->dev.of_node, "qcom,rpm-msg-ram", NULL)) +@@ -914,13 +913,35 @@ static int qcom_smem_probe(struct platform_device *pdev) + smem->dev = &pdev->dev; + smem->num_regions = num_regions; + +- ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0); +- if (ret) +- return ret; ++ rmem = of_reserved_mem_lookup(pdev->dev.of_node); ++ if (rmem) { ++ smem->regions[0].aux_base = rmem->base; ++ smem->regions[0].size = rmem->size; ++ } else { ++ /* ++ * Fall back to the memory-region reference, if we're not a ++ * reserved-memory node. ++ */ ++ ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]); ++ if (ret) ++ return ret; ++ } + +- if (num_regions > 1 && (ret = qcom_smem_map_memory(smem, &pdev->dev, +- "qcom,rpm-msg-ram", 1))) +- return ret; ++ if (num_regions > 1) { ++ ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]); ++ if (ret) ++ return ret; ++ } ++ ++ for (i = 0; i < num_regions; i++) { ++ smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev, ++ smem->regions[i].aux_base, ++ smem->regions[i].size); ++ if (!smem->regions[i].virt_base) { ++ dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base); ++ return -ENOMEM; ++ } ++ } + + header = smem->regions[0].virt_base; + if (le32_to_cpu(header->initialized) != 1 || +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0013-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch b/target/linux/ipq807x/patches-5.15/0013-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch new file mode 100644 index 00000000000000..d0fe755989bd12 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0013-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch @@ -0,0 +1,37 @@ +From 26bccc9671ba5e01f7153addbe94e7dc3f677375 Mon Sep 17 00:00:00 2001 +From: Bryan O'Donoghue +Date: Mon, 3 Jan 2022 03:03:16 +0000 +Subject: [PATCH 13/14] mtd: parsers: qcom: Don't print error message on + -EPROBE_DEFER + +Its possible for the main smem driver to not be loaded by the time we come +along to parse the smem partition description but, this is a perfectly +normal thing. + +No need to print out an error message in this case. + +Signed-off-by: Bryan O'Donoghue +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20220103030316.58301-3-bryan.odonoghue@linaro.org +--- + drivers/mtd/parsers/qcomsmempart.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/mtd/parsers/qcomsmempart.c b/drivers/mtd/parsers/qcomsmempart.c +index 32ddfea701423..4311b89d8df00 100644 +--- a/drivers/mtd/parsers/qcomsmempart.c ++++ b/drivers/mtd/parsers/qcomsmempart.c +@@ -75,7 +75,8 @@ static int parse_qcomsmem_part(struct mtd_info *mtd, + pr_debug("Parsing partition table info from SMEM\n"); + ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); + if (IS_ERR(ptable)) { +- pr_err("Error reading partition table header\n"); ++ if (PTR_ERR(ptable) != -EPROBE_DEFER) ++ pr_err("Error reading partition table header\n"); + return PTR_ERR(ptable); + } + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0014-v5.16-soc-qcom-socinfo-Add-IPQ8074-family-ID-s.patch b/target/linux/ipq807x/patches-5.15/0014-v5.16-soc-qcom-socinfo-Add-IPQ8074-family-ID-s.patch new file mode 100644 index 00000000000000..3b7f958caeae15 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0014-v5.16-soc-qcom-socinfo-Add-IPQ8074-family-ID-s.patch @@ -0,0 +1,55 @@ +From 0582f647b53f1f26d4d24b025086d4295dc65122 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 5 Sep 2021 19:11:31 +0200 +Subject: [PATCH 14/14] soc: qcom: socinfo: Add IPQ8074 family ID-s + +IPQ8074 family SoC ID-s are missing, so lets add them based on +the downstream driver. + +Signed-off-by: Robert Marko +Reviewed-by: Kathiravan T +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20210905171131.660885-1-robimarko@gmail.com +--- + drivers/soc/qcom/socinfo.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c +index 5beb452f24013..302a6a895c2f5 100644 +--- a/drivers/soc/qcom/socinfo.c ++++ b/drivers/soc/qcom/socinfo.c +@@ -281,19 +281,31 @@ static const struct soc_id soc_id[] = { + { 319, "APQ8098" }, + { 321, "SDM845" }, + { 322, "MDM9206" }, ++ { 323, "IPQ8074" }, + { 324, "SDA660" }, + { 325, "SDM658" }, + { 326, "SDA658" }, + { 327, "SDA630" }, + { 338, "SDM450" }, + { 341, "SDA845" }, ++ { 342, "IPQ8072" }, ++ { 343, "IPQ8076" }, ++ { 344, "IPQ8078" }, + { 345, "SDM636" }, + { 346, "SDA636" }, + { 349, "SDM632" }, + { 350, "SDA632" }, + { 351, "SDA450" }, + { 356, "SM8250" }, ++ { 375, "IPQ8070" }, ++ { 376, "IPQ8071" }, ++ { 389, "IPQ8072A" }, ++ { 390, "IPQ8074A" }, ++ { 391, "IPQ8076A" }, ++ { 392, "IPQ8078A" }, + { 394, "SM6125" }, ++ { 395, "IPQ8070A" }, ++ { 396, "IPQ8071A" }, + { 402, "IPQ6018" }, + { 403, "IPQ6028" }, + { 421, "IPQ6000" }, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0100-arm64-dts-ipq8074-add-reserved-memory-nodes.patch b/target/linux/ipq807x/patches-5.15/0100-arm64-dts-ipq8074-add-reserved-memory-nodes.patch new file mode 100644 index 00000000000000..df20524f80add6 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0100-arm64-dts-ipq8074-add-reserved-memory-nodes.patch @@ -0,0 +1,75 @@ +From 5fa912a3d39c675d876fa900b3f06e1a2eef5e2c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 22 Dec 2021 12:23:34 +0100 +Subject: [PATCH 100/158] arm64: dts: ipq8074: add reserved memory nodes + +IPQ8074 has multiple reserved memory ranges, if they are not defined +then weird things tend to happen, board hangs and resets when PCI or +WLAN is used etc. + +So, to avoid all of that add the reserved memory nodes from the downstream +5.4 kernel from QCA. +This is their default layout meant for devices with 1GB of RAM, but +devices with lower ammounts can override the Q6 node. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 35 +++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index d80b1cefab100..ba81c510dd395 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -81,6 +81,26 @@ reserved-memory { + #size-cells = <2>; + ranges; + ++ nss@40000000 { ++ no-map; ++ reg = <0x0 0x40000000 0x0 0x01000000>; ++ }; ++ ++ tzapp_region: tzapp@4a400000 { ++ no-map; ++ reg = <0x0 0x4a400000 0x0 0x00200000>; ++ }; ++ ++ uboot@4a600000 { ++ no-map; ++ reg = <0x0 0x4a600000 0x0 0x00400000>; ++ }; ++ ++ sbl@4aa00000 { ++ no-map; ++ reg = <0x0 0x4aa00000 0x0 0x00100000>; ++ }; ++ + smem@4ab00000 { + compatible = "qcom,smem"; + reg = <0x0 0x4ab00000 0x0 0x00100000>; +@@ -93,6 +113,21 @@ memory@4ac00000 { + no-map; + reg = <0x0 0x4ac00000 0x0 0x00400000>; + }; ++ ++ q6_region: wcnss@4b000000 { ++ no-map; ++ reg = <0x0 0x4b000000 0x0 0x05f00000>; ++ }; ++ ++ q6_etr_region: q6_etr_dump@50f00000 { ++ no-map; ++ reg = <0x0 0x50f00000 0x0 0x00100000>; ++ }; ++ ++ m3_dump_region: m3_dump@51000000 { ++ no-map; ++ reg = <0x0 0x51000000 0x0 0x100000>; ++ }; + }; + + firmware { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0101-clk-qcom-ipq8074-fix-NSS-core-PLL-s.patch b/target/linux/ipq807x/patches-5.15/0101-clk-qcom-ipq8074-fix-NSS-core-PLL-s.patch new file mode 100644 index 00000000000000..7a7a47df05aed6 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0101-clk-qcom-ipq8074-fix-NSS-core-PLL-s.patch @@ -0,0 +1,85 @@ +From dc9045988609a680c20d1c767e630bd4cc17eb73 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 1 Jan 2022 18:48:56 +0100 +Subject: [PATCH 101/158] clk: qcom: ipq8074: fix NSS core PLL-s +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Like in IPQ6018 the NSS related Alpha PLL-s require initial configuration +to work. + +So, obtain the regmap that is required for the Alpha PLL configuration +and thus utilize the qcom_cc_really_probe() as we already have the regmap. +Then utilize the Alpha PLL configs from the downstream QCA 5.4 based +kernel to configure them. + +This fixes the UBI32 and NSS crypto PLL-s failing to get enabled by the +kernel. + +Fixes: b8e7e519625f ("clk: qcom: ipq8074: add remaining PLL’s") +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 39 +++++++++++++++++++++++++++++++++- + 1 file changed, 38 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index 541016db3c4bb..1a5141da7e233 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4371,6 +4371,33 @@ static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { + }, + }; + ++static const struct alpha_pll_config ubi32_pll_config = { ++ .l = 0x4e, ++ .config_ctl_val = 0x200d4aa8, ++ .config_ctl_hi_val = 0x3c2, ++ .main_output_mask = BIT(0), ++ .aux_output_mask = BIT(1), ++ .pre_div_val = 0x0, ++ .pre_div_mask = BIT(12), ++ .post_div_val = 0x0, ++ .post_div_mask = GENMASK(9, 8), ++}; ++ ++static const struct alpha_pll_config nss_crypto_pll_config = { ++ .l = 0x3e, ++ .alpha = 0x0, ++ .alpha_hi = 0x80, ++ .config_ctl_val = 0x4001055b, ++ .main_output_mask = BIT(0), ++ .pre_div_val = 0x0, ++ .pre_div_mask = GENMASK(14, 12), ++ .post_div_val = 0x1 << 8, ++ .post_div_mask = GENMASK(11, 8), ++ .vco_mask = GENMASK(21, 20), ++ .vco_val = 0x0, ++ .alpha_en_mask = BIT(24), ++}; ++ + static struct clk_hw *gcc_ipq8074_hws[] = { + &gpll0_out_main_div2.hw, + &gpll6_out_main_div2.hw, +@@ -4772,7 +4799,17 @@ static const struct qcom_cc_desc gcc_ipq8074_desc = { + + static int gcc_ipq8074_probe(struct platform_device *pdev) + { +- return qcom_cc_probe(pdev, &gcc_ipq8074_desc); ++ struct regmap *regmap; ++ ++ regmap = qcom_cc_map(pdev, &gcc_ipq8074_desc); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ clk_alpha_pll_configure(&ubi32_pll_main, regmap, &ubi32_pll_config); ++ clk_alpha_pll_configure(&nss_crypto_pll_main, regmap, ++ &nss_crypto_pll_config); ++ ++ return qcom_cc_really_probe(pdev, &gcc_ipq8074_desc, regmap); + } + + static struct platform_driver gcc_ipq8074_driver = { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0102-clk-qcom-ipq8074-SW-workaround-for-UBI32-PLL-lock.patch b/target/linux/ipq807x/patches-5.15/0102-clk-qcom-ipq8074-SW-workaround-for-UBI32-PLL-lock.patch new file mode 100644 index 00000000000000..024f1b5bfe551a --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0102-clk-qcom-ipq8074-SW-workaround-for-UBI32-PLL-lock.patch @@ -0,0 +1,40 @@ +From 0d0249e8df00c75eb8d91777ee78f7346111473a Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 1 Jan 2022 19:14:59 +0100 +Subject: [PATCH 102/158] clk: qcom: ipq8074: SW workaround for UBI32 PLL lock +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +UBI32 Huayra PLL fails to lock in 5 us in some SoC silicon and thus it +will cause the wait_for_pll() to timeout and thus return the error +indicating that the PLL failed to lock. + +This is bug in Huayra PLL HW for which SW workaround +is to set bit 26 of TEST_CTL register. + +This is ported from the QCA 5.4 based downstream kernel. + +Fixes: b8e7e519625f ("clk: qcom: ipq8074: add remaining PLL’s") +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index 1a5141da7e233..b4291ba53c785 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4805,6 +4805,9 @@ static int gcc_ipq8074_probe(struct platform_device *pdev) + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + ++ /* SW Workaround for UBI32 Huayra PLL */ ++ regmap_update_bits(regmap, 0x2501c, BIT(26), BIT(26)); ++ + clk_alpha_pll_configure(&ubi32_pll_main, regmap, &ubi32_pll_config); + clk_alpha_pll_configure(&nss_crypto_pll_main, regmap, + &nss_crypto_pll_config); +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0103-clk-qcom-ipq8074-fix-NSS-port-frequency-tables.patch b/target/linux/ipq807x/patches-5.15/0103-clk-qcom-ipq8074-fix-NSS-port-frequency-tables.patch new file mode 100644 index 00000000000000..ce248414f6c37d --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0103-clk-qcom-ipq8074-fix-NSS-port-frequency-tables.patch @@ -0,0 +1,69 @@ +From d1bc46817837e8d0a3c842c405d1f4c9f3e90afe Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 1 Jan 2022 19:29:48 +0100 +Subject: [PATCH 103/158] clk: qcom: ipq8074: fix NSS port frequency tables + +NSS port 5 and 6 frequency tables are currently broken and are causing a +wide ranges of issue like 1G not working at all on port 6 or port 5 being +clocked with 312 instead of 125 MHz as UNIPHY1 gets selected. + +So, update the frequency tables with the ones from the downstream QCA 5.4 +based kernel which has already fixed this. + +Fixes: 7117a51ed303 ("clk: qcom: ipq8074: add NSS ethernet port clocks") +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index b4291ba53c785..f1017f2e61bd7 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -1788,8 +1788,10 @@ static struct clk_regmap_div nss_port4_tx_div_clk_src = { + static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY1_RX, 12.5, 0, 0), ++ F(25000000, P_UNIPHY0_RX, 5, 0, 0), + F(78125000, P_UNIPHY1_RX, 4, 0, 0), + F(125000000, P_UNIPHY1_RX, 2.5, 0, 0), ++ F(125000000, P_UNIPHY0_RX, 1, 0, 0), + F(156250000, P_UNIPHY1_RX, 2, 0, 0), + F(312500000, P_UNIPHY1_RX, 1, 0, 0), + { } +@@ -1828,8 +1830,10 @@ static struct clk_regmap_div nss_port5_rx_div_clk_src = { + static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY1_TX, 12.5, 0, 0), ++ F(25000000, P_UNIPHY0_TX, 5, 0, 0), + F(78125000, P_UNIPHY1_TX, 4, 0, 0), + F(125000000, P_UNIPHY1_TX, 2.5, 0, 0), ++ F(125000000, P_UNIPHY0_TX, 1, 0, 0), + F(156250000, P_UNIPHY1_TX, 2, 0, 0), + F(312500000, P_UNIPHY1_TX, 1, 0, 0), + { } +@@ -1867,8 +1871,10 @@ static struct clk_regmap_div nss_port5_tx_div_clk_src = { + + static const struct freq_tbl ftbl_nss_port6_rx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), ++ F(25000000, P_UNIPHY2_RX, 5, 0, 0), + F(25000000, P_UNIPHY2_RX, 12.5, 0, 0), + F(78125000, P_UNIPHY2_RX, 4, 0, 0), ++ F(125000000, P_UNIPHY2_RX, 1, 0, 0), + F(125000000, P_UNIPHY2_RX, 2.5, 0, 0), + F(156250000, P_UNIPHY2_RX, 2, 0, 0), + F(312500000, P_UNIPHY2_RX, 1, 0, 0), +@@ -1907,8 +1913,10 @@ static struct clk_regmap_div nss_port6_rx_div_clk_src = { + + static const struct freq_tbl ftbl_nss_port6_tx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), ++ F(25000000, P_UNIPHY2_TX, 5, 0, 0), + F(25000000, P_UNIPHY2_TX, 12.5, 0, 0), + F(78125000, P_UNIPHY2_TX, 4, 0, 0), ++ F(125000000, P_UNIPHY2_TX, 1, 0, 0), + F(125000000, P_UNIPHY2_TX, 2.5, 0, 0), + F(156250000, P_UNIPHY2_TX, 2, 0, 0), + F(312500000, P_UNIPHY2_TX, 1, 0, 0), +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0104-dt-bindings-clock-qcom-ipq8074-add-PPE-crypto-clock.patch b/target/linux/ipq807x/patches-5.15/0104-dt-bindings-clock-qcom-ipq8074-add-PPE-crypto-clock.patch new file mode 100644 index 00000000000000..4c598877cba663 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0104-dt-bindings-clock-qcom-ipq8074-add-PPE-crypto-clock.patch @@ -0,0 +1,29 @@ +From d359828a9dda050ee667edcab99c4092fd67e7a1 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 25 Apr 2022 20:19:14 +0200 +Subject: [PATCH 104/158] dt-bindings: clock: qcom: ipq8074: add PPE crypto + clock + +Add binding for the PPE crypto clock in IPQ8074. + +Signed-off-by: Robert Marko +Acked-by: Krzysztof Kozlowski +--- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq8074.h b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +index 8e2bec1c91bf9..5f0928785d7a7 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -233,6 +233,7 @@ + #define GCC_PCIE0_AXI_S_BRIDGE_CLK 224 + #define GCC_PCIE0_RCHNG_CLK_SRC 225 + #define GCC_PCIE0_RCHNG_CLK 226 ++#define GCC_CRYPTO_PPE_CLK 227 + + #define GCC_BLSP1_BCR 0 + #define GCC_BLSP1_QUP1_BCR 1 +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0105-clk-qcom-ipq8074-add-PPE-crypto-clock.patch b/target/linux/ipq807x/patches-5.15/0105-clk-qcom-ipq8074-add-PPE-crypto-clock.patch new file mode 100644 index 00000000000000..2927786852aa5b --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0105-clk-qcom-ipq8074-add-PPE-crypto-clock.patch @@ -0,0 +1,55 @@ +From 2c8aa34ba97db41c6116efa930e11240e714bf69 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 13 Mar 2022 12:46:28 +0100 +Subject: [PATCH 105/158] clk: qcom: ipq8074: add PPE crypto clock + +The built-in PPE engine has a dedicated clock for the EIP-197 crypto +engine. + +So, since the required clock currently missing add support for it. + +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index f1017f2e61bd7..c964e43ba68ad 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -3182,6 +3182,24 @@ static struct clk_branch gcc_nss_ptp_ref_clk = { + }, + }; + ++static struct clk_branch gcc_crypto_ppe_clk = { ++ .halt_reg = 0x68310, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x68310, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_crypto_ppe_clk", ++ .parent_names = (const char *[]){ ++ "nss_ppe_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ + static struct clk_branch gcc_nssnoc_ce_apb_clk = { + .halt_reg = 0x6830c, + .clkr = { +@@ -4644,6 +4662,7 @@ static struct clk_regmap *gcc_ipq8074_clks[] = { + [GCC_PCIE0_RCHNG_CLK_SRC] = &pcie0_rchng_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, ++ [GCC_CRYPTO_PPE_CLK] = &gcc_crypto_ppe_clk.clkr, + }; + + static const struct qcom_reset_map gcc_ipq8074_resets[] = { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0106-clk-qcom-ipq8074-set-BRANCH_HALT_DELAY-flag-for-UBI-.patch b/target/linux/ipq807x/patches-5.15/0106-clk-qcom-ipq8074-set-BRANCH_HALT_DELAY-flag-for-UBI-.patch new file mode 100644 index 00000000000000..57cfba8e029894 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0106-clk-qcom-ipq8074-set-BRANCH_HALT_DELAY-flag-for-UBI-.patch @@ -0,0 +1,107 @@ +From 88b49e8ac284422818f60a118c45074b77543b6c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 13 Mar 2022 13:01:55 +0100 +Subject: [PATCH 106/158] clk: qcom: ipq8074: set BRANCH_HALT_DELAY flag for + UBI clocks + +Currently, attempting to enable the UBI clocks will cause the stuck at +off warning to be printed and clk_enable will fail. + +[ 14.936694] gcc_ubi1_ahb_clk status stuck at 'off' + +Downstream 5.4 QCA kernel has fixed this by seting the BRANCH_HALT_DELAY +flag on UBI clocks, so lets do the same. + +Fixes: 5736294aef83 ("clk: qcom: ipq8074: add NSS clocks") +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index c964e43ba68ad..85076c1383c7a 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -3372,6 +3372,7 @@ static struct clk_branch gcc_nssnoc_ubi1_ahb_clk = { + + static struct clk_branch gcc_ubi0_ahb_clk = { + .halt_reg = 0x6820c, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x6820c, + .enable_mask = BIT(0), +@@ -3389,6 +3390,7 @@ static struct clk_branch gcc_ubi0_ahb_clk = { + + static struct clk_branch gcc_ubi0_axi_clk = { + .halt_reg = 0x68200, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68200, + .enable_mask = BIT(0), +@@ -3406,6 +3408,7 @@ static struct clk_branch gcc_ubi0_axi_clk = { + + static struct clk_branch gcc_ubi0_nc_axi_clk = { + .halt_reg = 0x68204, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68204, + .enable_mask = BIT(0), +@@ -3423,6 +3426,7 @@ static struct clk_branch gcc_ubi0_nc_axi_clk = { + + static struct clk_branch gcc_ubi0_core_clk = { + .halt_reg = 0x68210, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68210, + .enable_mask = BIT(0), +@@ -3440,6 +3444,7 @@ static struct clk_branch gcc_ubi0_core_clk = { + + static struct clk_branch gcc_ubi0_mpt_clk = { + .halt_reg = 0x68208, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68208, + .enable_mask = BIT(0), +@@ -3457,6 +3462,7 @@ static struct clk_branch gcc_ubi0_mpt_clk = { + + static struct clk_branch gcc_ubi1_ahb_clk = { + .halt_reg = 0x6822c, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x6822c, + .enable_mask = BIT(0), +@@ -3474,6 +3480,7 @@ static struct clk_branch gcc_ubi1_ahb_clk = { + + static struct clk_branch gcc_ubi1_axi_clk = { + .halt_reg = 0x68220, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68220, + .enable_mask = BIT(0), +@@ -3491,6 +3498,7 @@ static struct clk_branch gcc_ubi1_axi_clk = { + + static struct clk_branch gcc_ubi1_nc_axi_clk = { + .halt_reg = 0x68224, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68224, + .enable_mask = BIT(0), +@@ -3508,6 +3516,7 @@ static struct clk_branch gcc_ubi1_nc_axi_clk = { + + static struct clk_branch gcc_ubi1_core_clk = { + .halt_reg = 0x68230, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68230, + .enable_mask = BIT(0), +@@ -3525,6 +3534,7 @@ static struct clk_branch gcc_ubi1_core_clk = { + + static struct clk_branch gcc_ubi1_mpt_clk = { + .halt_reg = 0x68228, ++ .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x68228, + .enable_mask = BIT(0), +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0107-dt-bindings-clocks-qcom-gcc-ipq8074-support-power-do.patch b/target/linux/ipq807x/patches-5.15/0107-dt-bindings-clocks-qcom-gcc-ipq8074-support-power-do.patch new file mode 100644 index 00000000000000..6e60b5561b8c87 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0107-dt-bindings-clocks-qcom-gcc-ipq8074-support-power-do.patch @@ -0,0 +1,50 @@ +From 3ca5ee9ebb3397ffe7668bbd3d2d118fc304c833 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 6 May 2022 23:43:56 +0200 +Subject: [PATCH 107/158] dt-bindings: clocks: qcom,gcc-ipq8074: support power + domains + +GCC inside of IPQ8074 also provides power management via built-in GDSCs. +In order to do so, '#power-domain-cells' must be set to 1. + +Signed-off-by: Robert Marko +--- +Changes in v4: +* Alphabetically sort the cells properties +--- + .../devicetree/bindings/clock/qcom,gcc-ipq8074.yaml | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml +index 98572b4a9b608..21470f52ce369 100644 +--- a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml ++++ b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml +@@ -24,6 +24,9 @@ properties: + '#clock-cells': + const: 1 + ++ '#power-domain-cells': ++ const: 1 ++ + '#reset-cells': + const: 1 + +@@ -38,6 +41,7 @@ required: + - compatible + - reg + - '#clock-cells' ++ - '#power-domain-cells' + - '#reset-cells' + + additionalProperties: false +@@ -48,6 +52,7 @@ examples: + compatible = "qcom,gcc-ipq8074"; + reg = <0x01800000 0x80000>; + #clock-cells = <1>; ++ #power-domain-cells = <1>; + #reset-cells = <1>; + }; + ... +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0108-dt-bindings-clock-qcom-ipq8074-add-USB-GDSCs.patch b/target/linux/ipq807x/patches-5.15/0108-dt-bindings-clock-qcom-ipq8074-add-USB-GDSCs.patch new file mode 100644 index 00000000000000..dee331796fb223 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0108-dt-bindings-clock-qcom-ipq8074-add-USB-GDSCs.patch @@ -0,0 +1,27 @@ +From ffd5d39b8e1828558c34d44e4ffe4e33f979803c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 6 May 2022 23:55:44 +0200 +Subject: [PATCH 108/158] dt-bindings: clock: qcom: ipq8074: add USB GDSCs + +Add bindings for the USB GDSCs found in IPQ8074 GCC. + +Signed-off-by: Robert Marko +--- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq8074.h b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +index 5f0928785d7a7..e4991d3037080 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -368,4 +368,7 @@ + #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 + #define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 + ++#define USB0_GDSC 0 ++#define USB1_GDSC 1 ++ + #endif +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0109-clk-qcom-ipq8074-add-USB-GDSCs.patch b/target/linux/ipq807x/patches-5.15/0109-clk-qcom-ipq8074-add-USB-GDSCs.patch new file mode 100644 index 00000000000000..87b0a025469ddf --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0109-clk-qcom-ipq8074-add-USB-GDSCs.patch @@ -0,0 +1,87 @@ +From 77e344723ef7d1ead7ceb47cbdc6c99bceb85333 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 6 May 2022 23:59:27 +0200 +Subject: [PATCH 109/158] clk: qcom: ipq8074: add USB GDSCs + +Add GDSC-s for each of the two USB controllers built-in the IPQ8074. + +Signed-off-by: Robert Marko +--- +Changes in v2: +* Use proper GSDCs instead of raw regmap writes. +--- + drivers/clk/qcom/Kconfig | 1 + + drivers/clk/qcom/gcc-ipq8074.c | 24 ++++++++++++++++++++++++ + 2 files changed, 25 insertions(+) + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 9ef007b3cf9b4..3c84e34353a48 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -166,6 +166,7 @@ config IPQ_LCC_806X + + config IPQ_GCC_8074 + tristate "IPQ8074 Global Clock Controller" ++ select QCOM_GDSC + help + Support for global clock controller on ipq8074 devices. + Say Y if you want to use peripheral devices such as UART, SPI, +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index 85076c1383c7a..3204d550ff764 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -22,6 +22,7 @@ + #include "clk-alpha-pll.h" + #include "clk-regmap-divider.h" + #include "clk-regmap-mux.h" ++#include "gdsc.h" + #include "reset.h" + + enum { +@@ -4407,6 +4408,22 @@ static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { + }, + }; + ++static struct gdsc usb0_gdsc = { ++ .gdscr = 0x3e078, ++ .pd = { ++ .name = "usb0_gdsc", ++ }, ++ .pwrsts = PWRSTS_OFF_ON, ++}; ++ ++static struct gdsc usb1_gdsc = { ++ .gdscr = 0x3f078, ++ .pd = { ++ .name = "usb1_gdsc", ++ }, ++ .pwrsts = PWRSTS_OFF_ON, ++}; ++ + static const struct alpha_pll_config ubi32_pll_config = { + .l = 0x4e, + .config_ctl_val = 0x200d4aa8, +@@ -4810,6 +4827,11 @@ static const struct qcom_reset_map gcc_ipq8074_resets[] = { + [GCC_PCIE1_AXI_MASTER_STICKY_ARES] = { 0x76040, 6 }, + }; + ++static struct gdsc *gcc_ipq8074_gdscs[] = { ++ [USB0_GDSC] = &usb0_gdsc, ++ [USB1_GDSC] = &usb1_gdsc, ++}; ++ + static const struct of_device_id gcc_ipq8074_match_table[] = { + { .compatible = "qcom,gcc-ipq8074" }, + { } +@@ -4832,6 +4854,8 @@ static const struct qcom_cc_desc gcc_ipq8074_desc = { + .num_resets = ARRAY_SIZE(gcc_ipq8074_resets), + .clk_hws = gcc_ipq8074_hws, + .num_clk_hws = ARRAY_SIZE(gcc_ipq8074_hws), ++ .gdscs = gcc_ipq8074_gdscs, ++ .num_gdscs = ARRAY_SIZE(gcc_ipq8074_gdscs), + }; + + static int gcc_ipq8074_probe(struct platform_device *pdev) +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0110-clk-qcom-ipq8074-dont-disable-gcc_sleep_clk_src.patch b/target/linux/ipq807x/patches-5.15/0110-clk-qcom-ipq8074-dont-disable-gcc_sleep_clk_src.patch new file mode 100644 index 00000000000000..b98f98d259558d --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0110-clk-qcom-ipq8074-dont-disable-gcc_sleep_clk_src.patch @@ -0,0 +1,78 @@ +From 2c9789138f0d0eab8209a8f1a3317e5f3bac2ce4 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 7 May 2022 20:55:11 +0200 +Subject: [PATCH 110/158] clk: qcom: ipq8074: dont disable gcc_sleep_clk_src + +Once the usb sleep clocks are disabled, clock framework is trying to +disable the sleep clock source also. + +However, it seems that it cannot be disabled and trying to do so produces: +[ 245.436390] ------------[ cut here ]------------ +[ 245.441233] gcc_sleep_clk_src status stuck at 'on' +[ 245.441254] WARNING: CPU: 2 PID: 223 at clk_branch_wait+0x130/0x140 +[ 245.450435] Modules linked in: xhci_plat_hcd xhci_hcd dwc3 dwc3_qcom leds_gpio +[ 245.456601] CPU: 2 PID: 223 Comm: sh Not tainted 5.18.0-rc4 #215 +[ 245.463889] Hardware name: Xiaomi AX9000 (DT) +[ 245.470050] pstate: 204000c5 (nzCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) +[ 245.474307] pc : clk_branch_wait+0x130/0x140 +[ 245.481073] lr : clk_branch_wait+0x130/0x140 +[ 245.485588] sp : ffffffc009f2bad0 +[ 245.489838] x29: ffffffc009f2bad0 x28: ffffff8003e6c800 x27: 0000000000000000 +[ 245.493057] x26: 0000000000000000 x25: 0000000000000000 x24: ffffff800226ef20 +[ 245.500175] x23: ffffffc0089ff550 x22: 0000000000000000 x21: ffffffc008476ad0 +[ 245.507294] x20: 0000000000000000 x19: ffffffc00965ac70 x18: fffffffffffc51a7 +[ 245.514413] x17: 68702e3030303837 x16: 3a6d726f6674616c x15: ffffffc089f2b777 +[ 245.521531] x14: ffffffc0095c9d18 x13: 0000000000000129 x12: 0000000000000129 +[ 245.528649] x11: 00000000ffffffea x10: ffffffc009621d18 x9 : 0000000000000001 +[ 245.535767] x8 : 0000000000000001 x7 : 0000000000017fe8 x6 : 0000000000000001 +[ 245.542885] x5 : ffffff803fdca6d8 x4 : 0000000000000000 x3 : 0000000000000027 +[ 245.550002] x2 : 0000000000000027 x1 : 0000000000000023 x0 : 0000000000000026 +[ 245.557122] Call trace: +[ 245.564229] clk_branch_wait+0x130/0x140 +[ 245.566490] clk_branch2_disable+0x2c/0x40 +[ 245.570656] clk_core_disable+0x60/0xb0 +[ 245.574561] clk_core_disable+0x68/0xb0 +[ 245.578293] clk_disable+0x30/0x50 +[ 245.582113] dwc3_qcom_remove+0x60/0xc0 [dwc3_qcom] +[ 245.585588] platform_remove+0x28/0x60 +[ 245.590361] device_remove+0x4c/0x80 +[ 245.594179] device_release_driver_internal+0x1dc/0x230 +[ 245.597914] device_driver_detach+0x18/0x30 +[ 245.602861] unbind_store+0xec/0x110 +[ 245.607027] drv_attr_store+0x24/0x40 +[ 245.610847] sysfs_kf_write+0x44/0x60 +[ 245.614405] kernfs_fop_write_iter+0x128/0x1c0 +[ 245.618052] new_sync_write+0xc0/0x130 +[ 245.622391] vfs_write+0x1d4/0x2a0 +[ 245.626123] ksys_write+0x58/0xe0 +[ 245.629508] __arm64_sys_write+0x1c/0x30 +[ 245.632895] invoke_syscall.constprop.0+0x5c/0x110 +[ 245.636890] do_el0_svc+0xa0/0x150 +[ 245.641488] el0_svc+0x18/0x60 +[ 245.644872] el0t_64_sync_handler+0xa4/0x130 +[ 245.647914] el0t_64_sync+0x174/0x178 +[ 245.652340] ---[ end trace 0000000000000000 ]--- + +So, add CLK_IS_CRITICAL flag to the clock so that the kernel won't try +to disable the sleep clock. + +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index 3204d550ff764..42d185fe19c8c 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -663,6 +663,7 @@ static struct clk_branch gcc_sleep_clk_src = { + }, + .num_parents = 1, + .ops = &clk_branch2_ops, ++ .flags = CLK_IS_CRITICAL, + }, + }, + }; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0111-arm64-dts-ipq8074-add-USB-power-domains.patch b/target/linux/ipq807x/patches-5.15/0111-arm64-dts-ipq8074-add-USB-power-domains.patch new file mode 100644 index 00000000000000..184ceb6bc9e51f --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0111-arm64-dts-ipq8074-add-USB-power-domains.patch @@ -0,0 +1,49 @@ +From e4e878ecedaaf0dd2d676f096bdc401e6ef62ba7 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 7 May 2022 00:02:31 +0200 +Subject: [PATCH 111/158] arm64: dts: ipq8074: add USB power domains + +Add USB power domains provided by GCC GDSCs. +Add the required #power-domain-cells to the GCC as well. + +Signed-off-by: Robert Marko +--- +Changes in v4: +* Alphabetically sort the GCC cells properties +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index ba81c510dd395..4d278151cfcf9 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -382,6 +382,7 @@ gcc: gcc@1800000 { + compatible = "qcom,gcc-ipq8074"; + reg = <0x01800000 0x80000>; + #clock-cells = <0x1>; ++ #power-domain-cells = <1>; + #reset-cells = <0x1>; + }; + +@@ -610,6 +611,8 @@ usb_0: usb@8af8800 { + <133330000>, + <19200000>; + ++ power-domains = <&gcc USB0_GDSC>; ++ + resets = <&gcc GCC_USB0_BCR>; + status = "disabled"; + +@@ -650,6 +653,8 @@ usb_1: usb@8cf8800 { + <133330000>, + <19200000>; + ++ power-domains = <&gcc USB1_GDSC>; ++ + resets = <&gcc GCC_USB1_BCR>; + status = "disabled"; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0112-clk-qcom-clk-alpha-pll-add-support-for-APSS-PLL.patch b/target/linux/ipq807x/patches-5.15/0112-clk-qcom-clk-alpha-pll-add-support-for-APSS-PLL.patch new file mode 100644 index 00000000000000..55c4f1e40c88d5 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0112-clk-qcom-clk-alpha-pll-add-support-for-APSS-PLL.patch @@ -0,0 +1,54 @@ +From 531faf774976c9b8df23113228e3e0ed17cb17b4 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 28 Dec 2021 20:32:46 +0100 +Subject: [PATCH 112/158] clk: qcom: clk-alpha-pll: add support for APSS PLL + +APSS PLL type will be used by the IPQ8074 APSS driver for providing the +CPU core clocks and enabling CPU Frequency scaling. + +This is ported from the downstream 5.4 kernel. + +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/clk-alpha-pll.c | 12 ++++++++++++ + drivers/clk/qcom/clk-alpha-pll.h | 1 + + 2 files changed, 13 insertions(+) + +diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c +index 8f65b9bdafce4..d2c3eb4cf4afb 100644 +--- a/drivers/clk/qcom/clk-alpha-pll.c ++++ b/drivers/clk/qcom/clk-alpha-pll.c +@@ -139,6 +139,18 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { + [PLL_OFF_OPMODE] = 0x28, + [PLL_OFF_STATUS] = 0x38, + }, ++ [CLK_ALPHA_PLL_TYPE_APSS] = { ++ [PLL_OFF_L_VAL] = 0x08, ++ [PLL_OFF_ALPHA_VAL] = 0x10, ++ [PLL_OFF_ALPHA_VAL_U] = 0xff, ++ [PLL_OFF_USER_CTL] = 0x18, ++ [PLL_OFF_USER_CTL_U] = 0xff, ++ [PLL_OFF_CONFIG_CTL] = 0x20, ++ [PLL_OFF_CONFIG_CTL_U] = 0x24, ++ [PLL_OFF_TEST_CTL] = 0x30, ++ [PLL_OFF_TEST_CTL_U] = 0x34, ++ [PLL_OFF_STATUS] = 0x28, ++ }, + }; + EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); + +diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h +index 55e4fa47912f4..45e4b93253a98 100644 +--- a/drivers/clk/qcom/clk-alpha-pll.h ++++ b/drivers/clk/qcom/clk-alpha-pll.h +@@ -17,6 +17,7 @@ enum { + CLK_ALPHA_PLL_TYPE_LUCID = CLK_ALPHA_PLL_TYPE_TRION, + CLK_ALPHA_PLL_TYPE_AGERA, + CLK_ALPHA_PLL_TYPE_ZONDA, ++ CLK_ALPHA_PLL_TYPE_APSS, + CLK_ALPHA_PLL_TYPE_MAX, + }; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0113-dt-bindings-clock-Add-support-for-IPQ8074-APSS-clock.patch b/target/linux/ipq807x/patches-5.15/0113-dt-bindings-clock-Add-support-for-IPQ8074-APSS-clock.patch new file mode 100644 index 00000000000000..f3f04c6b60149c --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0113-dt-bindings-clock-Add-support-for-IPQ8074-APSS-clock.patch @@ -0,0 +1,46 @@ +From 1e188b08a016226ac94e86b6f352b8da39c430ff Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 28 Dec 2021 20:36:45 +0100 +Subject: [PATCH 113/158] dt-bindings: clock: Add support for IPQ8074 APSS + clock controller + +Add dt-binding for the IPQ8074 APSS clock controller which provides +clocks to the CPU cores. + +Signed-off-by: Robert Marko +Acked-by: Krzysztof Kozlowski +--- +Changes in v4: +* Dual license the bindings +* Update the copyright year + +Changes in v2: +* Correct subject +--- + include/dt-bindings/clock/qcom,apss-ipq8074.h | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + create mode 100644 include/dt-bindings/clock/qcom,apss-ipq8074.h + +diff --git a/include/dt-bindings/clock/qcom,apss-ipq8074.h b/include/dt-bindings/clock/qcom,apss-ipq8074.h +new file mode 100644 +index 0000000000000..32538c9311ffa +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,apss-ipq8074.h +@@ -0,0 +1,14 @@ ++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ ++/* ++ * Copyright (c) 2022, The Linux Foundation. All rights reserved. ++ */ ++ ++#ifndef _DT_BINDINGS_CLOCK_QCA_APSS_IPQ8074_H ++#define _DT_BINDINGS_CLOCK_QCA_APSS_IPQ8074_H ++ ++#define APSS_PLL_EARLY 0 ++#define APSS_PLL 1 ++#define APCS_ALIAS0_CLK_SRC 2 ++#define APCS_ALIAS0_CORE_CLK 3 ++ ++#endif +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0114-clk-qcom-Add-IPQ8074-APSS-clock-controller.patch b/target/linux/ipq807x/patches-5.15/0114-clk-qcom-Add-IPQ8074-APSS-clock-controller.patch new file mode 100644 index 00000000000000..5b3b4bda4c17d2 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0114-clk-qcom-Add-IPQ8074-APSS-clock-controller.patch @@ -0,0 +1,236 @@ +From 8b1218908bdb462cdb36fb8dd8c4ebba63cbb5a6 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 28 Dec 2021 20:37:55 +0100 +Subject: [PATCH 114/158] clk: qcom: Add IPQ8074 APSS clock controller + +IPQ8074 APSS clock controller provides the clock for the IPQ8074 CPU +cores, thus also providing support for CPU frequency scaling. + +It looks like they are clocked by the XO and a custom APSS type PLL. + +Co-developed-by: Ansuel Smith +Signed-off-by: Ansuel Smith +Signed-off-by: Robert Marko +--- +Changes in v2: +* Convert to using parent-data instead of parent-names +--- + drivers/clk/qcom/Kconfig | 11 +++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/apss-ipq8074.c | 170 ++++++++++++++++++++++++++++++++ + 3 files changed, 182 insertions(+) + create mode 100644 drivers/clk/qcom/apss-ipq8074.c + +diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig +index 3c84e34353a48..1cb63213d59ca 100644 +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -134,6 +134,17 @@ config IPQ_APSS_6018 + Say Y if you want to support CPU frequency scaling on + ipq based devices. + ++config IPQ_APSS_8074 ++ tristate "IPQ8074 APSS Clock Controller" ++ select IPQ_GCC_8074 ++ depends on QCOM_APCS_IPC || COMPILE_TEST ++ help ++ Support for APSS clock controller on IPQ8074 platforms. The ++ APSS clock controller manages the Mux and enable block that feeds the ++ CPUs. ++ Say Y if you want to support CPU frequency scaling on ++ IPQ8074 based devices. ++ + config IPQ_GCC_4019 + tristate "IPQ4019 Global Clock Controller" + help +diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile +index 9825ef843f4a0..a47b0dcc0add4 100644 +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o + obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o + obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o + obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o ++obj-$(CONFIG_IPQ_APSS_8074) += apss-ipq8074.o + obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o + obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o + obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +diff --git a/drivers/clk/qcom/apss-ipq8074.c b/drivers/clk/qcom/apss-ipq8074.c +new file mode 100644 +index 0000000000000..38d03cd0ff767 +--- /dev/null ++++ b/drivers/clk/qcom/apss-ipq8074.c +@@ -0,0 +1,170 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2022, The Linux Foundation. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "common.h" ++#include "clk-regmap.h" ++#include "clk-pll.h" ++#include "clk-rcg.h" ++#include "clk-branch.h" ++#include "clk-alpha-pll.h" ++#include "clk-regmap-divider.h" ++#include "clk-regmap-mux.h" ++ ++#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } ++ ++enum { ++ P_XO, ++ P_GPLL0, ++ P_GPLL2, ++ P_GPLL4, ++ P_APSS_PLL_EARLY, ++ P_APSS_PLL ++}; ++ ++static struct clk_alpha_pll apss_pll_early = { ++ .offset = 0x5000, ++ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_APSS], ++ .clkr = { ++ .enable_reg = 0x5000, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "apss_pll_early", ++ .parent_data = &(const struct clk_parent_data) { ++ .fw_name = "xo", .name = "xo" ++ }, ++ .num_parents = 1, ++ .ops = &clk_alpha_pll_huayra_ops, ++ }, ++ }, ++}; ++ ++static struct clk_alpha_pll_postdiv apss_pll = { ++ .offset = 0x5000, ++ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_APSS], ++ .width = 2, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "apss_pll", ++ .parent_hws = (const struct clk_hw *[]){ ++ &apss_pll_early.clkr.hw }, ++ .num_parents = 1, ++ .ops = &clk_alpha_pll_postdiv_ro_ops, ++ }, ++}; ++ ++static const struct clk_parent_data parents_apcs_alias0_clk_src[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .fw_name = "gpll0", .name = "gpll0" }, ++ { .fw_name = "gpll2", .name = "gpll2" }, ++ { .fw_name = "gpll4", .name = "gpll4" }, ++ { .hw = &apss_pll.clkr.hw }, ++ { .hw = &apss_pll_early.clkr.hw }, ++}; ++ ++static const struct parent_map parents_apcs_alias0_clk_src_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 4 }, ++ { P_GPLL2, 2 }, ++ { P_GPLL4, 1 }, ++ { P_APSS_PLL, 3 }, ++ { P_APSS_PLL_EARLY, 5 }, ++}; ++ ++struct freq_tbl ftbl_apcs_alias0_clk_src[] = { ++ F(19200000, P_XO, 1, 0, 0), ++ F(403200000, P_APSS_PLL_EARLY, 1, 0, 0), ++ F(806400000, P_APSS_PLL_EARLY, 1, 0, 0), ++ F(1017600000, P_APSS_PLL_EARLY, 1, 0, 0), ++ F(1382400000, P_APSS_PLL_EARLY, 1, 0, 0), ++ F(1651200000, P_APSS_PLL_EARLY, 1, 0, 0), ++ F(1843200000, P_APSS_PLL_EARLY, 1, 0, 0), ++ F(1920000000, P_APSS_PLL_EARLY, 1, 0, 0), ++ F(2208000000UL, P_APSS_PLL_EARLY, 1, 0, 0), ++ { } ++}; ++ ++struct clk_rcg2 apcs_alias0_clk_src = { ++ .cmd_rcgr = 0x0050, ++ .freq_tbl = ftbl_apcs_alias0_clk_src, ++ .hid_width = 5, ++ .parent_map = parents_apcs_alias0_clk_src_map, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "apcs_alias0_clk_src", ++ .parent_data = parents_apcs_alias0_clk_src, ++ .num_parents = ARRAY_SIZE(parents_apcs_alias0_clk_src), ++ .ops = &clk_rcg2_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++}; ++ ++static struct clk_branch apcs_alias0_core_clk = { ++ .halt_reg = 0x0058, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x0058, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "apcs_alias0_core_clk", ++ .parent_hws = (const struct clk_hw *[]){ ++ &apcs_alias0_clk_src.clkr.hw }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT | ++ CLK_IS_CRITICAL, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_regmap *apss_ipq8074_clks[] = { ++ [APSS_PLL_EARLY] = &apss_pll_early.clkr, ++ [APSS_PLL] = &apss_pll.clkr, ++ [APCS_ALIAS0_CLK_SRC] = &apcs_alias0_clk_src.clkr, ++ [APCS_ALIAS0_CORE_CLK] = &apcs_alias0_core_clk.clkr, ++}; ++ ++static const struct regmap_config apss_ipq8074_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x5ffc, ++ .fast_io = true, ++}; ++ ++static const struct qcom_cc_desc apss_ipq8074_desc = { ++ .config = &apss_ipq8074_regmap_config, ++ .clks = apss_ipq8074_clks, ++ .num_clks = ARRAY_SIZE(apss_ipq8074_clks), ++}; ++ ++static int apss_ipq8074_probe(struct platform_device *pdev) ++{ ++ struct regmap *regmap; ++ ++ regmap = dev_get_regmap(pdev->dev.parent, NULL); ++ if (!regmap) ++ return -ENODEV; ++ ++ return qcom_cc_really_probe(pdev, &apss_ipq8074_desc, regmap); ++} ++ ++static struct platform_driver apss_ipq8074_driver = { ++ .probe = apss_ipq8074_probe, ++ .driver = { ++ .name = "qcom,apss-ipq8074-clk", ++ }, ++}; ++ ++module_platform_driver(apss_ipq8074_driver); ++ ++MODULE_DESCRIPTION("Qualcomm IPQ8074 APSS clock driver"); ++MODULE_LICENSE("GPL"); +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0115-mailbox-qcom-apcs-ipc-add-IPQ8074-APSS-clock-control.patch b/target/linux/ipq807x/patches-5.15/0115-mailbox-qcom-apcs-ipc-add-IPQ8074-APSS-clock-control.patch new file mode 100644 index 00000000000000..6106dedac499dc --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0115-mailbox-qcom-apcs-ipc-add-IPQ8074-APSS-clock-control.patch @@ -0,0 +1,44 @@ +From 1f21148c43efc129e6fda694429da224c4834635 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 28 Dec 2021 20:59:18 +0100 +Subject: [PATCH 115/158] mailbox: qcom-apcs-ipc: add IPQ8074 APSS clock + controller support + +IPQ8074 has the APSS clock controller utilizing the same register space as +the APCS, so provide access to the APSS utilizing a child device like +IPQ6018 does as well, but just by utilizing the IPQ8074 specific APSS +clock driver. + +Also, APCS register space in IPQ8074 is 0x6000 so max_register needs to be +updated to 0x5FFC. + +Signed-off-by: Robert Marko +--- + drivers/mailbox/qcom-apcs-ipc-mailbox.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c +index 82ccfaf14b249..23407280580ff 100644 +--- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c ++++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c +@@ -34,7 +34,7 @@ static const struct qcom_apcs_ipc_data ipq6018_apcs_data = { + }; + + static const struct qcom_apcs_ipc_data ipq8074_apcs_data = { +- .offset = 8, .clk_name = NULL ++ .offset = 8, .clk_name = "qcom,apss-ipq8074-clk" + }; + + static const struct qcom_apcs_ipc_data msm8916_apcs_data = { +@@ -73,7 +73,7 @@ static const struct regmap_config apcs_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +- .max_register = 0x1008, ++ .max_register = 0x5FFC, + .fast_io = true, + }; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0116-dt-bindings-mailbox-qcom-set-correct-clock-cells.patch b/target/linux/ipq807x/patches-5.15/0116-dt-bindings-mailbox-qcom-set-correct-clock-cells.patch new file mode 100644 index 00000000000000..f47bdf9cbb42c3 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0116-dt-bindings-mailbox-qcom-set-correct-clock-cells.patch @@ -0,0 +1,61 @@ +From 401c13f309458f0b2757645951aeaad2caba90f8 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 29 Apr 2022 13:25:04 +0200 +Subject: [PATCH 116/158] dt-bindings: mailbox: qcom: set correct #clock-cells + +IPQ6018 and IPQ8074 require #clock-cells to be set to 1 as their APSS +clock driver provides multiple clock outputs. + +So allow setting 1 as #clock-cells and check that its set to 1 for IPQ6018 +and IPQ8074, check others for 0 as its currently. + +Signed-off-by: Robert Marko +Reviewed-by: Krzysztof Kozlowski +--- +Changes in v3: +* Drop not needed blank line + +Changes in v2: +* Correct subject name +--- + .../bindings/mailbox/qcom,apcs-kpss-global.yaml | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +index 6395281b0cec7..31a80ac82d99d 100644 +--- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml ++++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +@@ -48,7 +48,7 @@ properties: + const: 1 + + '#clock-cells': +- const: 0 ++ enum: [0, 1] + + clock-names: + minItems: 2 +@@ -94,6 +94,21 @@ allOf: + properties: + clocks: + maxItems: 3 ++ - if: ++ properties: ++ compatible: ++ enum: ++ - qcom,ipq6018-apcs-apps-global ++ - qcom,ipq8074-apcs-apps-global ++ then: ++ properties: ++ '#clock-cells': ++ const: 1 ++ else: ++ properties: ++ '#clock-cells': ++ const: 0 ++ + examples: + + # Example apcs with msm8996 +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0117-arm64-dts-ipq8074-add-APCS-node.patch b/target/linux/ipq807x/patches-5.15/0117-arm64-dts-ipq8074-add-APCS-node.patch new file mode 100644 index 00000000000000..34fa3c1ce14f29 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0117-arm64-dts-ipq8074-add-APCS-node.patch @@ -0,0 +1,41 @@ +From d7617bdacc58137a594e90f54275a98d0aa1f458 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 28 Dec 2021 21:07:17 +0100 +Subject: [PATCH 117/158] arm64: dts: ipq8074: add APCS node + +APCS now has support for providing the APSS clocks as the child device +for IPQ8074. + +So, add the required DT node for it as it will later be used as the CPU +clocksource. + +Signed-off-by: Robert Marko +--- +Changes in v3: +* Node does not currently exist in the upstream kernel, so add it instead +of modifying. +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 4d278151cfcf9..56300f92251d2 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -886,5 +886,13 @@ IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + "axi_m_sticky"; + status = "disabled"; + }; ++ ++ apcs_glb: mailbox@b111000 { ++ compatible = "qcom,ipq8074-apcs-apps-global"; ++ reg = <0x0b111000 0x6000>; ++ ++ #clock-cells = <1>; ++ #mbox-cells = <1>; ++ }; + }; + }; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0118-phy-qcom-qmp-Add-IPQ8074-PCIe-Gen3-QMP-PHY-support.patch b/target/linux/ipq807x/patches-5.15/0118-phy-qcom-qmp-Add-IPQ8074-PCIe-Gen3-QMP-PHY-support.patch new file mode 100644 index 00000000000000..6249b067017348 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0118-phy-qcom-qmp-Add-IPQ8074-PCIe-Gen3-QMP-PHY-support.patch @@ -0,0 +1,377 @@ +From 9b71cf0a8b408eb14b6118d1d4310aec02aef814 Mon Sep 17 00:00:00 2001 +From: Sivaprakash Murugesan +Date: Wed, 29 Jul 2020 21:00:04 +0530 +Subject: [PATCH 118/158] phy: qcom-qmp: Add IPQ8074 PCIe Gen3 QMP PHY support + +IPQ8074 has two PCIe ports, One Gen2 and one Gen3 port. +Since support for Gen2 PHY is already available, add support for +PCIe Gen3 PHY. + +Co-developed-by: Selvam Sathappan Periakaruppan +Signed-off-by: Selvam Sathappan Periakaruppan +Signed-off-by: Sivaprakash Murugesan +Signed-off-by: Robert Marko +--- + drivers/phy/qualcomm/phy-qcom-pcie3-qmp.h | 139 ++++++++++++++++++ + drivers/phy/qualcomm/phy-qcom-qmp.c | 171 +++++++++++++++++++++- + 2 files changed, 308 insertions(+), 2 deletions(-) + create mode 100644 drivers/phy/qualcomm/phy-qcom-pcie3-qmp.h + +diff --git a/drivers/phy/qualcomm/phy-qcom-pcie3-qmp.h b/drivers/phy/qualcomm/phy-qcom-pcie3-qmp.h +new file mode 100644 +index 0000000000000..070bde3558361 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-pcie3-qmp.h +@@ -0,0 +1,139 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++/* ++ * Copyright (c) 2020, The Linux Foundation. All rights reserved. ++ */ ++ ++#ifndef PHY_QCOM_PCIE_H ++#define PHY_QCOM_PCIE_H ++ ++/* QMP V2 PCIE PHY - Found in IPQ8074 gen3 port - QSERDES PLL registers */ ++#define QSERDES_PLL_BG_TIMER 0x00c ++#define QSERDES_PLL_SSC_PER1 0x01c ++#define QSERDES_PLL_SSC_PER2 0x020 ++#define QSERDES_PLL_SSC_STEP_SIZE1_MODE0 0x024 ++#define QSERDES_PLL_SSC_STEP_SIZE2_MODE0 0x028 ++#define QSERDES_PLL_SSC_STEP_SIZE1_MODE1 0x02c ++#define QSERDES_PLL_SSC_STEP_SIZE2_MODE1 0x030 ++#define QSERDES_PLL_BIAS_EN_CLKBUFLR_EN 0x03c ++#define QSERDES_PLL_CLK_ENABLE1 0x040 ++#define QSERDES_PLL_SYS_CLK_CTRL 0x044 ++#define QSERDES_PLL_SYSCLK_BUF_ENABLE 0x048 ++#define QSERDES_PLL_PLL_IVCO 0x050 ++#define QSERDES_PLL_LOCK_CMP1_MODE0 0x054 ++#define QSERDES_PLL_LOCK_CMP2_MODE0 0x058 ++#define QSERDES_PLL_LOCK_CMP1_MODE1 0x060 ++#define QSERDES_PLL_LOCK_CMP2_MODE1 0x064 ++#define QSERDES_PLL_BG_TRIM 0x074 ++#define QSERDES_PLL_CLK_EP_DIV_MODE0 0x078 ++#define QSERDES_PLL_CLK_EP_DIV_MODE1 0x07c ++#define QSERDES_PLL_CP_CTRL_MODE0 0x080 ++#define QSERDES_PLL_CP_CTRL_MODE1 0x084 ++#define QSERDES_PLL_PLL_RCTRL_MODE0 0x088 ++#define QSERDES_PLL_PLL_RCTRL_MODE1 0x08C ++#define QSERDES_PLL_PLL_CCTRL_MODE0 0x090 ++#define QSERDES_PLL_PLL_CCTRL_MODE1 0x094 ++#define QSERDES_PLL_BIAS_EN_CTRL_BY_PSM 0x0a4 ++#define QSERDES_PLL_SYSCLK_EN_SEL 0x0a8 ++#define QSERDES_PLL_RESETSM_CNTRL 0x0b0 ++#define QSERDES_PLL_LOCK_CMP_EN 0x0c4 ++#define QSERDES_PLL_DEC_START_MODE0 0x0cc ++#define QSERDES_PLL_DEC_START_MODE1 0x0d0 ++#define QSERDES_PLL_DIV_FRAC_START1_MODE0 0x0d8 ++#define QSERDES_PLL_DIV_FRAC_START2_MODE0 0x0dc ++#define QSERDES_PLL_DIV_FRAC_START3_MODE0 0x0e0 ++#define QSERDES_PLL_DIV_FRAC_START1_MODE1 0x0e4 ++#define QSERDES_PLL_DIV_FRAC_START2_MODE1 0x0e8 ++#define QSERDES_PLL_DIV_FRAC_START3_MODE1 0x0eC ++#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE0 0x100 ++#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE0 0x104 ++#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE1 0x108 ++#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE1 0x10c ++#define QSERDES_PLL_VCO_TUNE_MAP 0x120 ++#define QSERDES_PLL_VCO_TUNE1_MODE0 0x124 ++#define QSERDES_PLL_VCO_TUNE2_MODE0 0x128 ++#define QSERDES_PLL_VCO_TUNE1_MODE1 0x12c ++#define QSERDES_PLL_VCO_TUNE2_MODE1 0x130 ++#define QSERDES_PLL_VCO_TUNE_TIMER1 0x13c ++#define QSERDES_PLL_VCO_TUNE_TIMER2 0x140 ++#define QSERDES_PLL_CLK_SELECT 0x16c ++#define QSERDES_PLL_HSCLK_SEL 0x170 ++#define QSERDES_PLL_CORECLK_DIV 0x17c ++#define QSERDES_PLL_CORE_CLK_EN 0x184 ++#define QSERDES_PLL_CMN_CONFIG 0x18c ++#define QSERDES_PLL_SVS_MODE_CLK_SEL 0x194 ++#define QSERDES_PLL_CORECLK_DIV_MODE1 0x1b4 ++ ++/* QMP V2 PCIE PHY - Found in IPQ8074 gen3 port - - QSERDES TX registers */ ++#define QSERDES_TX0_RES_CODE_LANE_OFFSET_TX 0x03c ++#define QSERDES_TX0_HIGHZ_DRVR_EN 0x058 ++#define QSERDES_TX0_LANE_MODE_1 0x084 ++#define QSERDES_TX0_RCV_DETECT_LVL_2 0x09c ++ ++/* QMP V2 PCIE PHY - Found in IPQ8074 gen3 port - QSERDES RX registers */ ++#define QSERDES_RX0_UCDR_FO_GAIN 0x008 ++#define QSERDES_RX0_UCDR_SO_GAIN 0x014 ++#define QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE 0x034 ++#define QSERDES_RX0_UCDR_PI_CONTROLS 0x044 ++#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2 0x0ec ++#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3 0x0f0 ++#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4 0x0f4 ++#define QSERDES_RX0_RX_IDAC_TSETTLE_LOW 0x0f8 ++#define QSERDES_RX0_RX_IDAC_TSETTLE_HIGH 0x0fc ++#define QSERDES_RX0_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 ++#define QSERDES_RX0_RX_OFFSET_ADAPTOR_CNTRL2 0x114 ++#define QSERDES_RX0_SIGDET_ENABLES 0x118 ++#define QSERDES_RX0_SIGDET_CNTRL 0x11c ++#define QSERDES_RX0_SIGDET_DEGLITCH_CNTRL 0x124 ++#define QSERDES_RX0_RX_MODE_00_LOW 0x170 ++#define QSERDES_RX0_RX_MODE_00_HIGH 0x174 ++#define QSERDES_RX0_RX_MODE_00_HIGH2 0x178 ++#define QSERDES_RX0_RX_MODE_00_HIGH3 0x17c ++#define QSERDES_RX0_RX_MODE_00_HIGH4 0x180 ++#define QSERDES_RX0_RX_MODE_01_LOW 0x184 ++#define QSERDES_RX0_RX_MODE_01_HIGH 0x188 ++#define QSERDES_RX0_RX_MODE_01_HIGH2 0x18c ++#define QSERDES_RX0_RX_MODE_01_HIGH3 0x190 ++#define QSERDES_RX0_RX_MODE_01_HIGH4 0x194 ++#define QSERDES_RX0_RX_MODE_10_LOW 0x198 ++#define QSERDES_RX0_RX_MODE_10_HIGH 0x19c ++#define QSERDES_RX0_RX_MODE_10_HIGH2 0x1a0 ++#define QSERDES_RX0_RX_MODE_10_HIGH3 0x1a4 ++#define QSERDES_RX0_RX_MODE_10_HIGH4 0x1a8 ++#define QSERDES_RX0_DFE_EN_TIMER 0x1b4 ++ ++/* QMP V2 PCIE PHY - Found in IPQ8074 gen3 port - PCS registers */ ++ ++#define PCS_COM_FLL_CNTRL1 0x098 ++#define PCS_COM_FLL_CNTRL2 0x09c ++#define PCS_COM_FLL_CNT_VAL_L 0x0a0 ++#define PCS_COM_FLL_CNT_VAL_H_TOL 0x0a4 ++#define PCS_COM_FLL_MAN_CODE 0x0a8 ++#define PCS_COM_REFGEN_REQ_CONFIG1 0x0dc ++#define PCS_COM_G12S1_TXDEEMPH_M3P5DB 0x16c ++#define PCS_COM_RX_SIGDET_LVL 0x188 ++#define PCS_COM_P2U3_WAKEUP_DLY_TIME_AUXCLK_L 0x1a4 ++#define PCS_COM_P2U3_WAKEUP_DLY_TIME_AUXCLK_H 0x1a8 ++#define PCS_COM_RX_DCC_CAL_CONFIG 0x1d8 ++#define PCS_COM_EQ_CONFIG5 0x1ec ++ ++/* QMP V2 PCIE PHY - Found in IPQ8074 gen3 port - PCS Misc registers */ ++ ++#define PCS_PCIE_POWER_STATE_CONFIG2 0x40c ++#define PCS_PCIE_POWER_STATE_CONFIG4 0x414 ++#define PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x41c ++#define PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L 0x440 ++#define PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_H 0x444 ++#define PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L 0x448 ++#define PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_H 0x44c ++#define PCS_PCIE_OSC_DTCT_CONFIG2 0x45c ++#define PCS_PCIE_OSC_DTCT_MODE2_CONFIG2 0x478 ++#define PCS_PCIE_OSC_DTCT_MODE2_CONFIG4 0x480 ++#define PCS_PCIE_OSC_DTCT_MODE2_CONFIG5 0x484 ++#define PCS_PCIE_OSC_DTCT_ACTIONS 0x490 ++#define PCS_PCIE_EQ_CONFIG1 0x4a0 ++#define PCS_PCIE_EQ_CONFIG2 0x4a4 ++#define PCS_PCIE_PRESET_P10_PRE 0x4bc ++#define PCS_PCIE_PRESET_P10_POST 0x4e0 ++ ++#endif +diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c +index 06b04606dd7ea..7d139754889a7 100644 +--- a/drivers/phy/qualcomm/phy-qcom-qmp.c ++++ b/drivers/phy/qualcomm/phy-qcom-qmp.c +@@ -23,6 +23,7 @@ + #include + + #include "phy-qcom-qmp.h" ++#include "phy-qcom-pcie3-qmp.h" + + /* QPHY_SW_RESET bit */ + #define SW_RESET BIT(0) +@@ -812,6 +813,132 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3), + }; + ++static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_serdes_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x31), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_IVCO, 0x0f), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TRIM, 0x0f), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CMN_CONFIG, 0x06), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP_EN, 0x42), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_RESETSM_CNTRL, 0x20), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_MAP, 0x04), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_TIMER1, 0xff), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_TIMER2, 0x3f), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x30), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x21), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE0, 0x82), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE0, 0x03), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE0, 0x355), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE0, 0x35555), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE0, 0x1a), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE0, 0x1a0a), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE0, 0xb), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE0, 0x16), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE0, 0x28), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE0, 0x0), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE0, 0x40), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE0, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE0, 0x24), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x20), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV, 0xa), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x32), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_SYS_CLK_CTRL, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_BUF_ENABLE, 0x07), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x08), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TIMER, 0xa), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x1), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE1, 0x68), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE1, 0x2), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE1, 0x2aa), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE1, 0x2aaab), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE1, 0x34), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE1, 0x3414), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE1, 0x0b), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE1, 0x16), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE1, 0x28), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE1, 0x0), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE1, 0x40), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE1, 0x03), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE1, 0xb4), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x0), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV_MODE1, 0x08), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_EP_DIV_MODE0, 0x19), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_EP_DIV_MODE1, 0x28), ++ QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90), ++}; ++ ++static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_tx_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_TX0_RES_CODE_LANE_OFFSET_TX, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_TX0_RCV_DETECT_LVL_2, 0x12), ++ QMP_PHY_INIT_CFG(QSERDES_TX0_HIGHZ_DRVR_EN, 0x10), ++ QMP_PHY_INIT_CFG(QSERDES_TX0_LANE_MODE_1, 0x06), ++}; ++ ++static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_rx_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_RX0_SIGDET_CNTRL, 0x03), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_SIGDET_ENABLES, 0x1c), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_SIGDET_DEGLITCH_CNTRL, 0x14), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2, 0xe), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x4), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4, 0x1b), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_DFE_EN_TIMER, 0x04), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_PI_CONTROLS, 0x70), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x73), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_LOW, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH2, 0xc8), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH3, 0x09), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH4, 0xb1), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_LOW, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH2, 0xc8), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH3, 0x09), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH4, 0xb1), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_LOW, 0xf0), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH, 0x2), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH2, 0x2f), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH3, 0xd3), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH4, 0x40), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_IDAC_TSETTLE_HIGH, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_RX_IDAC_TSETTLE_LOW, 0xc0), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_FO_GAIN, 0x0c), ++ QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_SO_GAIN, 0x02), ++}; ++ ++static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_pcs_tbl[] = { ++ QMP_PHY_INIT_CFG(PCS_COM_FLL_CNTRL2, 0x83), ++ QMP_PHY_INIT_CFG(PCS_COM_FLL_CNT_VAL_L, 0x9), ++ QMP_PHY_INIT_CFG(PCS_COM_FLL_CNT_VAL_H_TOL, 0x42), ++ QMP_PHY_INIT_CFG(PCS_COM_FLL_MAN_CODE, 0x40), ++ QMP_PHY_INIT_CFG(PCS_COM_FLL_CNTRL1, 0x01), ++ QMP_PHY_INIT_CFG(PCS_COM_P2U3_WAKEUP_DLY_TIME_AUXCLK_H, 0x0), ++ QMP_PHY_INIT_CFG(PCS_COM_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x1), ++ QMP_PHY_INIT_CFG(PCS_PCIE_OSC_DTCT_ACTIONS, 0x0), ++ QMP_PHY_INIT_CFG(PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_H, 0x00), ++ QMP_PHY_INIT_CFG(PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), ++ QMP_PHY_INIT_CFG(PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_H, 0x00), ++ QMP_PHY_INIT_CFG(PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), ++ QMP_PHY_INIT_CFG(PCS_PCIE_EQ_CONFIG1, 0x11), ++ QMP_PHY_INIT_CFG(PCS_PCIE_EQ_CONFIG2, 0xb), ++ QMP_PHY_INIT_CFG(PCS_PCIE_POWER_STATE_CONFIG4, 0x07), ++ QMP_PHY_INIT_CFG(PCS_PCIE_OSC_DTCT_CONFIG2, 0x52), ++ QMP_PHY_INIT_CFG(PCS_PCIE_OSC_DTCT_MODE2_CONFIG2, 0x50), ++ QMP_PHY_INIT_CFG(PCS_PCIE_OSC_DTCT_MODE2_CONFIG4, 0x1a), ++ QMP_PHY_INIT_CFG(PCS_PCIE_OSC_DTCT_MODE2_CONFIG5, 0x6), ++ QMP_PHY_INIT_CFG(PCS_COM_G12S1_TXDEEMPH_M3P5DB, 0x10), ++ QMP_PHY_INIT_CFG(PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), ++ QMP_PHY_INIT_CFG(PCS_COM_RX_DCC_CAL_CONFIG, 0x01), ++ QMP_PHY_INIT_CFG(PCS_COM_RX_SIGDET_LVL, 0xaa), ++ QMP_PHY_INIT_CFG(PCS_COM_REFGEN_REQ_CONFIG1, 0x0d), ++}; + static const struct qmp_phy_init_tbl sdm845_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), +@@ -3194,6 +3321,36 @@ static const struct qmp_phy_cfg ipq6018_pciephy_cfg = { + .pwrdn_delay_max = 1005, /* us */ + }; + ++static const struct qmp_phy_cfg ipq8074_pciephy_gen3_cfg = { ++ .type = PHY_TYPE_PCIE, ++ .nlanes = 1, ++ ++ .serdes_tbl = ipq8074_pcie_gen3_serdes_tbl, ++ .serdes_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_serdes_tbl), ++ .tx_tbl = ipq8074_pcie_gen3_tx_tbl, ++ .tx_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_tx_tbl), ++ .rx_tbl = ipq8074_pcie_gen3_rx_tbl, ++ .rx_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_rx_tbl), ++ .pcs_tbl = ipq8074_pcie_gen3_pcs_tbl, ++ .pcs_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_pcs_tbl), ++ .clk_list = ipq8074_pciephy_clk_l, ++ .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), ++ .reset_list = ipq8074_pciephy_reset_l, ++ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), ++ .vreg_list = NULL, ++ .num_vregs = 0, ++ .regs = qmp_v4_usb3phy_regs_layout, ++ ++ .start_ctrl = SERDES_START | PCS_START, ++ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, ++ ++ .has_phy_com_ctrl = false, ++ .has_lane_rst = false, ++ .has_pwrdn_delay = true, ++ .pwrdn_delay_min = 995, /* us */ ++ .pwrdn_delay_max = 1005, /* us */ ++}; ++ + static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 1, +@@ -5138,8 +5295,15 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) + + init.ops = &clk_fixed_rate_ops; + +- /* controllers using QMP phys use 125MHz pipe clock interface */ +- fixed->fixed_rate = 125000000; ++ /* ++ * controllers using QMP phys use 125MHz pipe clock interface unless ++ * other frequency is specified in dts ++ */ ++ ret = of_property_read_u32(np, "clock-output-rate", ++ (u32 *)&fixed->fixed_rate); ++ if (ret) ++ fixed->fixed_rate = 125000000; ++ + fixed->hw.init = &init; + + ret = devm_clk_hw_register(qmp->dev, &fixed->hw); +@@ -5529,6 +5693,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { + }, { + .compatible = "qcom,ipq6018-qmp-usb3-phy", + .data = &ipq8074_usb3phy_cfg, ++ }, { ++ .compatible = "qcom,ipq8074-qmp-pcie-gen3-phy", ++ .data = &ipq8074_pciephy_gen3_cfg, + }, { + .compatible = "qcom,sc7180-qmp-usb3-phy", + .data = &sc7180_usb3phy_cfg, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0119-PCI-dwc-tegra-move-GEN3_RELATED-DBI-register-to-comm.patch b/target/linux/ipq807x/patches-5.15/0119-PCI-dwc-tegra-move-GEN3_RELATED-DBI-register-to-comm.patch new file mode 100644 index 00000000000000..595775c6843a6b --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0119-PCI-dwc-tegra-move-GEN3_RELATED-DBI-register-to-comm.patch @@ -0,0 +1,52 @@ +From 32d94f589ce749246b720ddaa7e4bc1caa7050ec Mon Sep 17 00:00:00 2001 +From: Baruch Siach +Date: Mon, 7 Feb 2022 16:51:24 +0200 +Subject: [PATCH 119/158] PCI: dwc: tegra: move GEN3_RELATED DBI register to + common header + +These are common dwc macros that will be used for other platforms. + +Reviewed-by: Rob Herring +Signed-off-by: Baruch Siach +--- + drivers/pci/controller/dwc/pcie-designware.h | 6 ++++++ + drivers/pci/controller/dwc/pcie-tegra194.c | 6 ------ + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h +index 7d6e9b7576be5..ea87809ee2983 100644 +--- a/drivers/pci/controller/dwc/pcie-designware.h ++++ b/drivers/pci/controller/dwc/pcie-designware.h +@@ -74,6 +74,12 @@ + #define PCIE_MSI_INTR0_MASK 0x82C + #define PCIE_MSI_INTR0_STATUS 0x830 + ++#define GEN3_RELATED_OFF 0x890 ++#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0) ++#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) ++#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 ++#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24) ++ + #define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 + #define PORT_MLTI_UPCFG_SUPPORT BIT(7) + +diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c +index 9049769130819..846c9d154f491 100644 +--- a/drivers/pci/controller/dwc/pcie-tegra194.c ++++ b/drivers/pci/controller/dwc/pcie-tegra194.c +@@ -193,12 +193,6 @@ + #define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK GENMASK(23, 8) + #define GEN3_EQ_CONTROL_OFF_FB_MODE_MASK GENMASK(3, 0) + +-#define GEN3_RELATED_OFF 0x890 +-#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0) +-#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) +-#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 +-#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24) +- + #define PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT 0x8D0 + #define AMBA_ERROR_RESPONSE_CRS_SHIFT 3 + #define AMBA_ERROR_RESPONSE_CRS_MASK GENMASK(1, 0) +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0120-PCI-qcom-Define-slot-capabilities-using-PCI_EXP_SLTC.patch b/target/linux/ipq807x/patches-5.15/0120-PCI-qcom-Define-slot-capabilities-using-PCI_EXP_SLTC.patch new file mode 100644 index 00000000000000..f66f18917968d0 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0120-PCI-qcom-Define-slot-capabilities-using-PCI_EXP_SLTC.patch @@ -0,0 +1,51 @@ +From 77a852dacc6cd9e02eca37f63ae2adbb6ebf2155 Mon Sep 17 00:00:00 2001 +From: Baruch Siach +Date: Mon, 7 Feb 2022 16:51:25 +0200 +Subject: [PATCH 120/158] PCI: qcom: Define slot capabilities using + PCI_EXP_SLTCAP_* + +The PCIE_CAP_LINK1_VAL macro actually defines slot capabilities. Use +PCI_EXP_SLTCAP_* macros to spell its value, and rename it to better +describe its meaning. + +Signed-off-by: Baruch Siach +--- + drivers/pci/controller/dwc/pcie-qcom.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c +index 8a7a300163e5c..f5101258d73b6 100644 +--- a/drivers/pci/controller/dwc/pcie-qcom.c ++++ b/drivers/pci/controller/dwc/pcie-qcom.c +@@ -69,7 +69,18 @@ + #define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c + #define CFG_BRIDGE_SB_INIT BIT(0) + +-#define PCIE_CAP_LINK1_VAL 0x2FD7F ++#define PCIE_CAP_SLOT_POWER_LIMIT_VAL 0x7D00 ++#define PCIE_CAP_SLOT_POWER_LIMIT_SCALE 0x8000 ++#define PCIE_CAP_SLOT_VAL (PCI_EXP_SLTCAP_ABP | \ ++ PCI_EXP_SLTCAP_PCP | \ ++ PCI_EXP_SLTCAP_MRLSP | \ ++ PCI_EXP_SLTCAP_AIP | \ ++ PCI_EXP_SLTCAP_PIP | \ ++ PCI_EXP_SLTCAP_HPS | \ ++ PCI_EXP_SLTCAP_HPC | \ ++ PCI_EXP_SLTCAP_EIP | \ ++ PCIE_CAP_SLOT_POWER_LIMIT_VAL | \ ++ PCIE_CAP_SLOT_POWER_LIMIT_SCALE) + + #define PCIE20_PARF_Q2A_FLUSH 0x1AC + +@@ -1102,7 +1113,7 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) + + writel(PCI_COMMAND_MASTER, pci->dbi_base + PCI_COMMAND); + writel(DBI_RO_WR_EN, pci->dbi_base + PCIE20_MISC_CONTROL_1_REG); +- writel(PCIE_CAP_LINK1_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); ++ writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); + + val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); + val &= ~PCI_EXP_LNKCAP_ASPMS; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0121-PCI-qcom-Add-IPQ60xx-support.patch b/target/linux/ipq807x/patches-5.15/0121-PCI-qcom-Add-IPQ60xx-support.patch new file mode 100644 index 00000000000000..44390e724fcd98 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0121-PCI-qcom-Add-IPQ60xx-support.patch @@ -0,0 +1,219 @@ +From e442557e1a8d5a64cea9cac3a0c951e9af085211 Mon Sep 17 00:00:00 2001 +From: Selvam Sathappan Periakaruppan +Date: Thu, 10 Feb 2022 18:02:47 +0100 +Subject: [PATCH 121/158] PCI: qcom: Add IPQ60xx support + +IPQ60xx series of SoCs have one port of PCIe gen 3. Add support for that +platform. + +The code is based on downstream[1] Codeaurora kernel v5.4 (branch +win.linuxopenwrt.2.0). + +Split out the DBI registers access part from .init into .post_init. DBI +registers are only accessible after phy_power_on(). + +[1] https://source.codeaurora.org/quic/qsdk/oss/kernel/linux-ipq-5.4/ + +Signed-off-by: Selvam Sathappan Periakaruppan +Signed-off-by: Baruch Siach +--- + drivers/pci/controller/dwc/pcie-designware.h | 1 + + drivers/pci/controller/dwc/pcie-qcom.c | 135 +++++++++++++++++++ + 2 files changed, 136 insertions(+) + +diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h +index ea87809ee2983..279c3778a13bd 100644 +--- a/drivers/pci/controller/dwc/pcie-designware.h ++++ b/drivers/pci/controller/dwc/pcie-designware.h +@@ -76,6 +76,7 @@ + + #define GEN3_RELATED_OFF 0x890 + #define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0) ++#define GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS BIT(13) + #define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) + #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 + #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24) +diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c +index f5101258d73b6..05359cfc0e34d 100644 +--- a/drivers/pci/controller/dwc/pcie-qcom.c ++++ b/drivers/pci/controller/dwc/pcie-qcom.c +@@ -52,6 +52,10 @@ + #define PCIE20_PARF_DBI_BASE_ADDR 0x168 + #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C + #define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 ++#define AHB_CLK_EN BIT(0) ++#define MSTR_AXI_CLK_EN BIT(1) ++#define BYPASS BIT(4) ++ + #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 + #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 + #define PCIE20_PARF_LTSSM 0x1B0 +@@ -179,6 +183,11 @@ struct qcom_pcie_resources_2_7_0 { + struct clk *pipe_clk; + }; + ++struct qcom_pcie_resources_2_9_0 { ++ struct clk_bulk_data clks[5]; ++ struct reset_control *rst; ++}; ++ + union qcom_pcie_resources { + struct qcom_pcie_resources_1_0_0 v1_0_0; + struct qcom_pcie_resources_2_1_0 v2_1_0; +@@ -186,6 +195,7 @@ union qcom_pcie_resources { + struct qcom_pcie_resources_2_3_3 v2_3_3; + struct qcom_pcie_resources_2_4_0 v2_4_0; + struct qcom_pcie_resources_2_7_0 v2_7_0; ++ struct qcom_pcie_resources_2_9_0 v2_9_0; + }; + + struct qcom_pcie; +@@ -1277,6 +1287,121 @@ static void qcom_pcie_post_deinit_2_7_0(struct qcom_pcie *pcie) + clk_disable_unprepare(res->pipe_clk); + } + ++static int qcom_pcie_get_resources_2_9_0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; ++ struct dw_pcie *pci = pcie->pci; ++ struct device *dev = pci->dev; ++ int ret; ++ ++ res->clks[0].id = "iface"; ++ res->clks[1].id = "axi_m"; ++ res->clks[2].id = "axi_s"; ++ res->clks[3].id = "axi_bridge"; ++ res->clks[4].id = "rchng"; ++ ++ ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); ++ if (ret < 0) ++ return ret; ++ ++ res->rst = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(res->rst)) ++ return PTR_ERR(res->rst); ++ ++ return 0; ++} ++ ++static void qcom_pcie_deinit_2_9_0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; ++ ++ clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); ++} ++ ++static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; ++ struct device *dev = pcie->pci->dev; ++ int ret; ++ ++ ret = reset_control_assert(res->rst); ++ if (ret) { ++ dev_err(dev, "reset assert failed (%d)\n", ret); ++ return ret; ++ } ++ ++ /* ++ * Delay periods before and after reset deassert are working values ++ * from downstream Codeaurora kernel ++ */ ++ usleep_range(2000, 2500); ++ ++ ret = reset_control_deassert(res->rst); ++ if (ret) { ++ dev_err(dev, "reset deassert failed (%d)\n", ret); ++ return ret; ++ } ++ ++ usleep_range(2000, 2500); ++ ++ ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); ++ if (ret) ++ goto err_reset; ++ ++ return 0; ++ ++err_reset: ++ reset_control_assert(res->rst); ++ ++ return ret; ++} ++ ++static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) ++{ ++ struct dw_pcie *pci = pcie->pci; ++ u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); ++ u32 val; ++ int i; ++ ++ writel(SLV_ADDR_SPACE_SZ, ++ pcie->parf + PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE); ++ ++ val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); ++ val &= ~BIT(0); ++ writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); ++ ++ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); ++ ++ writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE); ++ writel(BYPASS | MSTR_AXI_CLK_EN | AHB_CLK_EN, ++ pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); ++ writel(GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS ++ | GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL, ++ pci->dbi_base + GEN3_RELATED_OFF); ++ ++ writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS ++ | SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS | ++ AUX_PWR_DET | L23_CLK_RMV_DIS | L1_CLK_RMV_DIS, ++ pcie->parf + PCIE20_PARF_SYS_CTRL); ++ ++ writel(0, pcie->parf + PCIE20_PARF_Q2A_FLUSH); ++ ++ dw_pcie_dbi_ro_wr_en(pci); ++ writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); ++ ++ val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); ++ val &= ~PCI_EXP_LNKCAP_ASPMS; ++ writel(val, pci->dbi_base + offset + PCI_EXP_LNKCAP); ++ ++ writel(PCI_EXP_DEVCTL2_COMP_TMOUT_DIS, pci->dbi_base + offset + ++ PCI_EXP_DEVCTL2); ++ ++ for (i = 0; i < 256; i++) ++ writel(0x0, pcie->parf + PCIE20_PARF_BDF_TO_SID_TABLE_N ++ + (4 * i)); ++ ++ return 0; ++} + static int qcom_pcie_link_up(struct dw_pcie *pci) + { + u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); +@@ -1467,6 +1592,15 @@ static const struct qcom_pcie_ops ops_1_9_0 = { + .config_sid = qcom_pcie_config_sid_sm8250, + }; + ++/* Qcom IP rev.: 2.9.0 Synopsys IP rev.: 5.00a */ ++static const struct qcom_pcie_ops ops_2_9_0 = { ++ .get_resources = qcom_pcie_get_resources_2_9_0, ++ .init = qcom_pcie_init_2_9_0, ++ .post_init = qcom_pcie_post_init_2_9_0, ++ .deinit = qcom_pcie_deinit_2_9_0, ++ .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, ++}; ++ + static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = qcom_pcie_link_up, + .start_link = qcom_pcie_start_link, +@@ -1566,6 +1700,7 @@ static const struct of_device_id qcom_pcie_match[] = { + { .compatible = "qcom,pcie-qcs404", .data = &ops_2_4_0 }, + { .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 }, + { .compatible = "qcom,pcie-sm8250", .data = &ops_1_9_0 }, ++ { .compatible = "qcom,pcie-ipq6018", .data = &ops_2_9_0 }, + { } + }; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0122-PCI-qcom-add-IPQ8074-Gen3-support.patch b/target/linux/ipq807x/patches-5.15/0122-PCI-qcom-add-IPQ8074-Gen3-support.patch new file mode 100644 index 00000000000000..19f5115881117b --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0122-PCI-qcom-add-IPQ8074-Gen3-support.patch @@ -0,0 +1,50 @@ +From 212a0fee94783c2c991740c6785badc6787d91ec Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 20 Dec 2021 15:01:36 +0100 +Subject: [PATCH 122/158] PCI: qcom: add IPQ8074 Gen3 support + +IPQ8074 has one Gen2 and one Gen3 port, Gen3 port is the same one as +in IPQ6018, so reuse the support but just add the missing clocks. + +Signed-off-by: Robert Marko +--- + drivers/pci/controller/dwc/pcie-qcom.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c +index 05359cfc0e34d..5556859eed18b 100644 +--- a/drivers/pci/controller/dwc/pcie-qcom.c ++++ b/drivers/pci/controller/dwc/pcie-qcom.c +@@ -184,7 +184,7 @@ struct qcom_pcie_resources_2_7_0 { + }; + + struct qcom_pcie_resources_2_9_0 { +- struct clk_bulk_data clks[5]; ++ struct clk_bulk_data clks[7]; + struct reset_control *rst; + }; + +@@ -1297,8 +1297,10 @@ static int qcom_pcie_get_resources_2_9_0(struct qcom_pcie *pcie) + res->clks[0].id = "iface"; + res->clks[1].id = "axi_m"; + res->clks[2].id = "axi_s"; +- res->clks[3].id = "axi_bridge"; +- res->clks[4].id = "rchng"; ++ res->clks[3].id = "ahb"; ++ res->clks[4].id = "aux"; ++ res->clks[5].id = "axi_bridge"; ++ res->clks[6].id = "rchng"; + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); + if (ret < 0) +@@ -1701,6 +1703,7 @@ static const struct of_device_id qcom_pcie_match[] = { + { .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 }, + { .compatible = "qcom,pcie-sm8250", .data = &ops_1_9_0 }, + { .compatible = "qcom,pcie-ipq6018", .data = &ops_2_9_0 }, ++ { .compatible = "qcom,pcie-ipq8074-gen3", .data = &ops_2_9_0 }, + { } + }; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0123-PCI-qcom-fix-IPQ8074-Gen2-support.patch b/target/linux/ipq807x/patches-5.15/0123-PCI-qcom-fix-IPQ8074-Gen2-support.patch new file mode 100644 index 00000000000000..118a9666a5895a --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0123-PCI-qcom-fix-IPQ8074-Gen2-support.patch @@ -0,0 +1,101 @@ +From 59b2f96bdb255ce7bc54683e7999e83e105c5936 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 20 Dec 2021 15:06:03 +0100 +Subject: [PATCH 123/158] PCI: qcom: fix IPQ8074 Gen2 support + +IPQ8074 has one Gen2 and one Gen3 port, currently the Gen2 port will +cause the system to hang as its using DBI registers in the .init +and those are only accesible after phy_power_on(). + +So solve this by splitting the DBI read/writes to .post_init. + +Fixes: a0fd361db8e5 ("PCI: dwc: Move "dbi", "dbi2", and "addr_space" resource setup into common code") +Signed-off-by: Robert Marko +--- + drivers/pci/controller/dwc/pcie-qcom.c | 48 +++++++++++++++----------- + 1 file changed, 28 insertions(+), 20 deletions(-) + +diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c +index 5556859eed18b..4ddc4431c9776 100644 +--- a/drivers/pci/controller/dwc/pcie-qcom.c ++++ b/drivers/pci/controller/dwc/pcie-qcom.c +@@ -1047,9 +1047,7 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) + struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; +- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + int i, ret; +- u32 val; + + for (i = 0; i < ARRAY_SIZE(res->rst); i++) { + ret = reset_control_assert(res->rst[i]); +@@ -1106,6 +1104,33 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) + goto err_clk_aux; + } + ++ return 0; ++ ++err_clk_aux: ++ clk_disable_unprepare(res->ahb_clk); ++err_clk_ahb: ++ clk_disable_unprepare(res->axi_s_clk); ++err_clk_axi_s: ++ clk_disable_unprepare(res->axi_m_clk); ++err_clk_axi_m: ++ clk_disable_unprepare(res->iface); ++err_clk_iface: ++ /* ++ * Not checking for failure, will anyway return ++ * the original failure in 'ret'. ++ */ ++ for (i = 0; i < ARRAY_SIZE(res->rst); i++) ++ reset_control_assert(res->rst[i]); ++ ++ return ret; ++} ++ ++static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) ++{ ++ struct dw_pcie *pci = pcie->pci; ++ u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); ++ u32 val; ++ + writel(SLV_ADDR_SPACE_SZ, + pcie->parf + PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE); + +@@ -1133,24 +1158,6 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) + PCI_EXP_DEVCTL2); + + return 0; +- +-err_clk_aux: +- clk_disable_unprepare(res->ahb_clk); +-err_clk_ahb: +- clk_disable_unprepare(res->axi_s_clk); +-err_clk_axi_s: +- clk_disable_unprepare(res->axi_m_clk); +-err_clk_axi_m: +- clk_disable_unprepare(res->iface); +-err_clk_iface: +- /* +- * Not checking for failure, will anyway return +- * the original failure in 'ret'. +- */ +- for (i = 0; i < ARRAY_SIZE(res->rst); i++) +- reset_control_assert(res->rst[i]); +- +- return ret; + } + + static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie) +@@ -1569,6 +1576,7 @@ static const struct qcom_pcie_ops ops_2_4_0 = { + static const struct qcom_pcie_ops ops_2_3_3 = { + .get_resources = qcom_pcie_get_resources_2_3_3, + .init = qcom_pcie_init_2_3_3, ++ .post_init = qcom_pcie_post_init_2_3_3, + .deinit = qcom_pcie_deinit_2_3_3, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, + }; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0124-arm64-dts-ipq8074-fix-PCI-related-DT-nodes.patch b/target/linux/ipq807x/patches-5.15/0124-arm64-dts-ipq8074-fix-PCI-related-DT-nodes.patch new file mode 100644 index 00000000000000..44b57e2c266bba --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0124-arm64-dts-ipq8074-fix-PCI-related-DT-nodes.patch @@ -0,0 +1,198 @@ +From 32408fcad174dedfcf65a41ca3efa1a022e4c399 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 20 Dec 2021 15:08:04 +0100 +Subject: [PATCH 124/158] arm64: dts: ipq8074: fix PCI related DT nodes + +Currently present PCI PHY and PCI controller nodes are not working +and are incorrect for the v2 of IPQ8074 which is the only version +supported upstream. + +So, correct the PCI related nodes. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 73 +++++++++++++++------------ + 1 file changed, 42 insertions(+), 31 deletions(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 56300f92251d2..6d4fc286782d5 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -228,9 +228,9 @@ qusb_phy_0: phy@79000 { + status = "disabled"; + }; + +- pcie_qmp0: phy@86000 { +- compatible = "qcom,ipq8074-qmp-pcie-phy"; +- reg = <0x00086000 0x1000>; ++ pcie_qmp0: phy@84000 { ++ compatible = "qcom,ipq8074-qmp-pcie-gen3-phy"; ++ reg = <0x00084000 0x1bc>; + #address-cells = <1>; + #size-cells = <1>; + ranges; +@@ -244,21 +244,22 @@ pcie_qmp0: phy@86000 { + "common"; + status = "disabled"; + +- pcie_phy0: phy@86200 { +- reg = <0x86200 0x16c>, +- <0x86400 0x200>, +- <0x86800 0x4f4>; ++ pcie_phy0: phy@84200 { ++ reg = <0x84200 0x16c>, ++ <0x84400 0x200>, ++ <0x84800 0x4f4>; + #phy-cells = <0>; + #clock-cells = <0>; + clocks = <&gcc GCC_PCIE0_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "pcie_0_pipe_clk"; ++ clock-output-rate = <250000000>; + }; + }; + + pcie_qmp1: phy@8e000 { + compatible = "qcom,ipq8074-qmp-pcie-phy"; +- reg = <0x0008e000 0x1000>; ++ reg = <0x0008e000 0x1c4>; + #address-cells = <1>; + #size-cells = <1>; + ranges; +@@ -273,14 +274,15 @@ pcie_qmp1: phy@8e000 { + status = "disabled"; + + pcie_phy1: phy@8e200 { +- reg = <0x8e200 0x16c>, ++ reg = <0x8e200 0x130>, + <0x8e400 0x200>, +- <0x8e800 0x4f4>; ++ <0x8e800 0x1f8>; + #phy-cells = <0>; + #clock-cells = <0>; + clocks = <&gcc GCC_PCIE1_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "pcie_1_pipe_clk"; ++ clock-output-rate = <125000000>; + }; + }; + +@@ -681,7 +683,7 @@ intc: interrupt-controller@b000000 { + reg = <0x0b000000 0x1000>, <0x0b002000 0x1000>; + ranges = <0 0xb00a000 0xffd>; + +- v2m@0 { ++ gic_v2m0: v2m@0 { + compatible = "arm,gic-v2m-frame"; + msi-controller; + reg = <0x0 0xffd>; +@@ -773,6 +775,7 @@ pcie1: pci@10000000 { + linux,pci-domain = <1>; + bus-range = <0x00 0xff>; + num-lanes = <1>; ++ max-link-speed = <2>; + #address-cells = <3>; + #size-cells = <2>; + +@@ -780,12 +783,12 @@ pcie1: pci@10000000 { + phy-names = "pciephy"; + + ranges = <0x81000000 0 0x10200000 0x10200000 +- 0 0x100000 /* downstream I/O */ +- 0x82000000 0 0x10300000 0x10300000 +- 0 0xd00000>; /* non-prefetchable memory */ ++ 0 0x10000>, /* downstream I/O */ ++ <0x82000000 0 0x10220000 0x10220000 ++ 0 0xfde0000>; /* non-prefetchable memory */ ++ ++ msi-parent = <&gic_v2m0>; + +- interrupts = ; +- interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 142 +@@ -825,16 +828,18 @@ IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + }; + + pcie0: pci@20000000 { +- compatible = "qcom,pcie-ipq8074"; +- reg = <0x20000000 0xf1d>, +- <0x20000f20 0xa8>, +- <0x00080000 0x2000>, +- <0x20100000 0x1000>; +- reg-names = "dbi", "elbi", "parf", "config"; ++ compatible = "qcom,pcie-ipq8074-gen3"; ++ reg = <0x20000000 0xf1d>, ++ <0x20000f20 0xa8>, ++ <0x20001000 0x1000>, ++ <0x00080000 0x4000>, ++ <0x20100000 0x1000>; ++ reg-names = "dbi", "elbi", "atu", "parf", "config"; + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; ++ max-link-speed = <3>; + #address-cells = <3>; + #size-cells = <2>; + +@@ -842,12 +847,12 @@ pcie0: pci@20000000 { + phy-names = "pciephy"; + + ranges = <0x81000000 0 0x20200000 0x20200000 +- 0 0x100000 /* downstream I/O */ +- 0x82000000 0 0x20300000 0x20300000 +- 0 0xd00000>; /* non-prefetchable memory */ ++ 0 0x10000>, /* downstream I/O */ ++ <0x82000000 0 0x20220000 0x20220000 ++ 0 0xfde0000>; /* non-prefetchable memory */ ++ ++ msi-parent = <&gic_v2m0>; + +- interrupts = ; +- interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 75 +@@ -863,27 +868,33 @@ IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <&gcc GCC_PCIE0_AXI_M_CLK>, + <&gcc GCC_PCIE0_AXI_S_CLK>, + <&gcc GCC_PCIE0_AHB_CLK>, +- <&gcc GCC_PCIE0_AUX_CLK>; ++ <&gcc GCC_PCIE0_AUX_CLK>, ++ <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>, ++ <&gcc GCC_PCIE0_RCHNG_CLK>; + + clock-names = "iface", + "axi_m", + "axi_s", + "ahb", +- "aux"; ++ "aux", ++ "axi_bridge", ++ "rchng"; + resets = <&gcc GCC_PCIE0_PIPE_ARES>, + <&gcc GCC_PCIE0_SLEEP_ARES>, + <&gcc GCC_PCIE0_CORE_STICKY_ARES>, + <&gcc GCC_PCIE0_AXI_MASTER_ARES>, + <&gcc GCC_PCIE0_AXI_SLAVE_ARES>, + <&gcc GCC_PCIE0_AHB_ARES>, +- <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>; ++ <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>, ++ <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>; + reset-names = "pipe", + "sleep", + "sticky", + "axi_m", + "axi_s", + "ahb", +- "axi_m_sticky"; ++ "axi_m_sticky", ++ "axi_s_sticky"; + status = "disabled"; + }; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0125-dt-bindings-thermal-tsens-Add-ipq8074-compatible.patch b/target/linux/ipq807x/patches-5.15/0125-dt-bindings-thermal-tsens-Add-ipq8074-compatible.patch new file mode 100644 index 00000000000000..251218706112b5 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0125-dt-bindings-thermal-tsens-Add-ipq8074-compatible.patch @@ -0,0 +1,150 @@ +From 75fecec71f84d49b9719e1d3573531eaa6adfba5 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 27 Apr 2022 23:12:16 +0200 +Subject: [PATCH 125/158] dt-bindings: thermal: tsens: Add ipq8074 compatible + +Qualcomm IPQ8074 has tsens v2.3.0 block, though unlike existing v2 IP it +only uses one IRQ, so tsens v2 compatible cannot be used as the fallback. + +We also have to make sure that correct interrupts are set according to +compatibles, so populate interrupt information per compatibles. + +Signed-off-by: Robert Marko +Reviewed-by: Krzysztof Kozlowski +--- +Changes in v4: +* Add the forgotten Reviewed-by tag from Krzysztof + +Changes in v3: +* Remove implied min/maxItem properties as pointed by Rob + +Changes in v2: +* No need for a list in compatible check +* Specify minItems and maxItems for interrupt and interrupt-names +--- + .../bindings/thermal/qcom-tsens.yaml | 76 ++++++++++++++++--- + 1 file changed, 65 insertions(+), 11 deletions(-) + +diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +index d3b9e9b600a28..26aa4d174b154 100644 +--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml ++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +@@ -55,6 +55,10 @@ properties: + - qcom,sm8350-tsens + - const: qcom,tsens-v2 + ++ - description: v2 of TSENS with combined interrupt ++ enum: ++ - qcom,ipq8074-tsens ++ + reg: + items: + - description: TM registers +@@ -62,15 +66,11 @@ properties: + + interrupts: + minItems: 1 +- items: +- - description: Combined interrupt if upper or lower threshold crossed +- - description: Interrupt if critical threshold crossed ++ maxItems: 2 + + interrupt-names: + minItems: 1 +- items: +- - const: uplow +- - const: critical ++ maxItems: 2 + + nvmem-cells: + minItems: 1 +@@ -123,22 +123,61 @@ allOf: + then: + properties: + interrupts: +- maxItems: 1 ++ items: ++ - description: Combined interrupt if upper or lower threshold crossed ++ interrupt-names: ++ items: ++ - const: uplow ++ ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - qcom,msm8953-tsens ++ - qcom,msm8996-tsens ++ - qcom,msm8998-tsens ++ - qcom,sc7180-tsens ++ - qcom,sc7280-tsens ++ - qcom,sc8180x-tsens ++ - qcom,sdm630-tsens ++ - qcom,sdm845-tsens ++ - qcom,sm8150-tsens ++ - qcom,sm8250-tsens ++ - qcom,sm8350-tsens ++ - qcom,tsens-v2 ++ then: ++ properties: ++ interrupts: ++ items: ++ - description: Combined interrupt if upper or lower threshold crossed ++ - description: Interrupt if critical threshold crossed + interrupt-names: +- maxItems: 1 ++ items: ++ - const: uplow ++ - const: critical + +- else: ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - qcom,ipq8074-tsens ++ then: + properties: + interrupts: +- minItems: 2 ++ items: ++ - description: Combined interrupt if upper, lower or critical thresholds crossed + interrupt-names: +- minItems: 2 ++ items: ++ - const: combined + + - if: + properties: + compatible: + contains: + enum: ++ - qcom,ipq8074-tsens + - qcom,tsens-v0_1 + - qcom,tsens-v1 + - qcom,tsens-v2 +@@ -221,4 +260,19 @@ examples: + #qcom,sensors = <13>; + #thermal-sensor-cells = <1>; + }; ++ ++ - | ++ #include ++ // Example 4 (for any IPQ8074 based SoC-s): ++ tsens4: thermal-sensor@4a9000 { ++ compatible = "qcom,ipq8074-tsens"; ++ reg = <0x4a9000 0x1000>, ++ <0x4a8000 0x1000>; ++ ++ interrupts = ; ++ interrupt-names = "combined"; ++ ++ #qcom,sensors = <16>; ++ #thermal-sensor-cells = <1>; ++ }; + ... +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0126-drivers-thermal-tsens-Add-support-for-combined-inter.patch b/target/linux/ipq807x/patches-5.15/0126-drivers-thermal-tsens-Add-support-for-combined-inter.patch new file mode 100644 index 00000000000000..9cebc4237243b0 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0126-drivers-thermal-tsens-Add-support-for-combined-inter.patch @@ -0,0 +1,146 @@ +From cc5e1348f697685ac0e7a5cc22b6212d03fc6d6d Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 28 Apr 2022 14:58:16 +0200 +Subject: [PATCH 126/158] drivers: thermal: tsens: Add support for combined + interrupt + +Despite using tsens v2.3 IP, IPQ8074 and IPQ6018 only have one IRQ for +signaling both up/low and critical trips. + +Signed-off-by: Robert Marko +--- + drivers/thermal/qcom/tsens-8960.c | 1 + + drivers/thermal/qcom/tsens-v0_1.c | 1 + + drivers/thermal/qcom/tsens-v1.c | 1 + + drivers/thermal/qcom/tsens-v2.c | 1 + + drivers/thermal/qcom/tsens.c | 37 ++++++++++++++++++++++++++----- + drivers/thermal/qcom/tsens.h | 2 ++ + 6 files changed, 37 insertions(+), 6 deletions(-) + +diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c +index 67c1748cdf734..ee584e5b07e56 100644 +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -269,6 +269,7 @@ static const struct tsens_ops ops_8960 = { + static struct tsens_features tsens_8960_feat = { + .ver_major = VER_0, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 0, + .max_sensors = 11, +diff --git a/drivers/thermal/qcom/tsens-v0_1.c b/drivers/thermal/qcom/tsens-v0_1.c +index f136cb3502384..6effb822bf3cc 100644 +--- a/drivers/thermal/qcom/tsens-v0_1.c ++++ b/drivers/thermal/qcom/tsens-v0_1.c +@@ -539,6 +539,7 @@ static int calibrate_9607(struct tsens_priv *priv) + static struct tsens_features tsens_v0_1_feat = { + .ver_major = VER_0_1, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c +index 573e261ccca74..a4f561a6e5821 100644 +--- a/drivers/thermal/qcom/tsens-v1.c ++++ b/drivers/thermal/qcom/tsens-v1.c +@@ -302,6 +302,7 @@ static int calibrate_8976(struct tsens_priv *priv) + static struct tsens_features tsens_v1_feat = { + .ver_major = VER_1_X, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c +index b293ed32174b5..129cdb247381c 100644 +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -31,6 +31,7 @@ + static struct tsens_features tsens_v2_feat = { + .ver_major = VER_2_X, + .crit_int = 1, ++ .combo_int = 0, + .adc = 0, + .srot_split = 1, + .max_sensors = 16, +diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c +index 99a8d9f3e03ca..238c38423d984 100644 +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -531,6 +531,26 @@ static irqreturn_t tsens_irq_thread(int irq, void *data) + return IRQ_HANDLED; + } + ++/** ++ * tsens_combined_irq_thread - Threaded interrupt handler for combined interrupts ++ * @irq: irq number ++ * @data: tsens controller private data ++ * ++ * Handle the combined interrupt as if it were 2 separate interrupts, so call the ++ * critical handler first and then the up/low one. ++ * ++ * Return: IRQ_HANDLED ++ */ ++static irqreturn_t tsens_combined_irq_thread(int irq, void *data) ++{ ++ irqreturn_t ret; ++ ++ ret = tsens_critical_irq_thread(irq, data); ++ ret = tsens_irq_thread(irq, data); ++ ++ return ret; ++} ++ + static int tsens_set_trips(void *_sensor, int low, int high) + { + struct tsens_sensor *s = _sensor; +@@ -1075,13 +1095,18 @@ static int tsens_register(struct tsens_priv *priv) + tsens_mC_to_hw(priv->sensor, 0)); + } + +- ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); +- if (ret < 0) +- return ret; ++ if (priv->feat->combo_int) { ++ ret = tsens_register_irq(priv, "combined", ++ tsens_combined_irq_thread); ++ } else { ++ ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); ++ if (ret < 0) ++ return ret; + +- if (priv->feat->crit_int) +- ret = tsens_register_irq(priv, "critical", +- tsens_critical_irq_thread); ++ if (priv->feat->crit_int) ++ ret = tsens_register_irq(priv, "critical", ++ tsens_critical_irq_thread); ++ } + + return ret; + } +diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h +index 1471a2c00f158..4614177944d6f 100644 +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -495,6 +495,7 @@ enum regfield_ids { + * struct tsens_features - Features supported by the IP + * @ver_major: Major number of IP version + * @crit_int: does the IP support critical interrupts? ++ * @combo_int: does the IP use one IRQ for up, low and critical thresholds? + * @adc: do the sensors only output adc code (instead of temperature)? + * @srot_split: does the IP neatly splits the register space into SROT and TM, + * with SROT only being available to secure boot firmware? +@@ -504,6 +505,7 @@ enum regfield_ids { + struct tsens_features { + unsigned int ver_major; + unsigned int crit_int:1; ++ unsigned int combo_int:1; + unsigned int adc:1; + unsigned int srot_split:1; + unsigned int has_watchdog:1; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0127-drivers-thermal-tsens-allow-configuring-min-and-max-.patch b/target/linux/ipq807x/patches-5.15/0127-drivers-thermal-tsens-allow-configuring-min-and-max-.patch new file mode 100644 index 00000000000000..39a2ed6106154e --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0127-drivers-thermal-tsens-allow-configuring-min-and-max-.patch @@ -0,0 +1,114 @@ +From 93d0e07b5565aeee993e5b0a8eba8ac8b644bc06 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 28 Apr 2022 19:06:29 +0200 +Subject: [PATCH 127/158] drivers: thermal: tsens: allow configuring min and + max trips + +IPQ8074 and IPQ6018 dont support negative trip temperatures and support +up to 204 degrees C as the max trip temperature. + +So, instead of always setting the -40 as min and 120 degrees C as max +allow it to be configured as part of the features. + +Signed-off-by: Robert Marko +--- + drivers/thermal/qcom/tsens-8960.c | 2 ++ + drivers/thermal/qcom/tsens-v0_1.c | 2 ++ + drivers/thermal/qcom/tsens-v1.c | 2 ++ + drivers/thermal/qcom/tsens-v2.c | 2 ++ + drivers/thermal/qcom/tsens.c | 4 ++-- + drivers/thermal/qcom/tsens.h | 4 ++++ + 6 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c +index ee584e5b07e56..4585904fb3800 100644 +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -273,6 +273,8 @@ static struct tsens_features tsens_8960_feat = { + .adc = 1, + .srot_split = 0, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + struct tsens_plat_data data_8960 = { +diff --git a/drivers/thermal/qcom/tsens-v0_1.c b/drivers/thermal/qcom/tsens-v0_1.c +index 6effb822bf3cc..2c203ff374e6e 100644 +--- a/drivers/thermal/qcom/tsens-v0_1.c ++++ b/drivers/thermal/qcom/tsens-v0_1.c +@@ -543,6 +543,8 @@ static struct tsens_features tsens_v0_1_feat = { + .adc = 1, + .srot_split = 1, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = { +diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c +index a4f561a6e5821..1d7f8a80bd13a 100644 +--- a/drivers/thermal/qcom/tsens-v1.c ++++ b/drivers/thermal/qcom/tsens-v1.c +@@ -306,6 +306,8 @@ static struct tsens_features tsens_v1_feat = { + .adc = 1, + .srot_split = 1, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = { +diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c +index 129cdb247381c..9babc69bfd22e 100644 +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -35,6 +35,8 @@ static struct tsens_features tsens_v2_feat = { + .adc = 0, + .srot_split = 1, + .max_sensors = 16, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { +diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c +index 238c38423d984..2e34561402c91 100644 +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -571,8 +571,8 @@ static int tsens_set_trips(void *_sensor, int low, int high) + dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", + hw_id, __func__, low, high); + +- cl_high = clamp_val(high, -40000, 120000); +- cl_low = clamp_val(low, -40000, 120000); ++ cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp); ++ cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp); + + high_val = tsens_mC_to_hw(s, cl_high); + low_val = tsens_mC_to_hw(s, cl_low); +diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h +index 4614177944d6f..747004476347c 100644 +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -501,6 +501,8 @@ enum regfield_ids { + * with SROT only being available to secure boot firmware? + * @has_watchdog: does this IP support watchdog functionality? + * @max_sensors: maximum sensors supported by this version of the IP ++ * @trip_min_temp: minimum trip temperature supported by this version of the IP ++ * @trip_max_temp: maximum trip temperature supported by this version of the IP + */ + struct tsens_features { + unsigned int ver_major; +@@ -510,6 +512,8 @@ struct tsens_features { + unsigned int srot_split:1; + unsigned int has_watchdog:1; + unsigned int max_sensors; ++ int trip_min_temp; ++ int trip_max_temp; + }; + + /** +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0128-drivers-thermal-tsens-add-IPQ8074-support.patch b/target/linux/ipq807x/patches-5.15/0128-drivers-thermal-tsens-add-IPQ8074-support.patch new file mode 100644 index 00000000000000..f0a3d621b65055 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0128-drivers-thermal-tsens-add-IPQ8074-support.patch @@ -0,0 +1,80 @@ +From f54c1d9bbf9e95934fdeec916985b7b88b6a7278 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 28 Apr 2022 20:26:13 +0200 +Subject: [PATCH 128/158] drivers: thermal: tsens: add IPQ8074 support + +Qualcomm IPQ8074 uses tsens v2.3 IP, however unlike other tsens v2 IP +it only has one IRQ, that is used for up/low as well as critical. +It also does not support negative trip temperatures. + +Signed-off-by: Robert Marko +--- + drivers/thermal/qcom/tsens-v2.c | 17 +++++++++++++++++ + drivers/thermal/qcom/tsens.c | 3 +++ + drivers/thermal/qcom/tsens.h | 2 +- + 3 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c +index 9babc69bfd22e..29a61d2d6ca31 100644 +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -39,6 +39,17 @@ static struct tsens_features tsens_v2_feat = { + .trip_max_temp = 120000, + }; + ++static struct tsens_features ipq8074_feat = { ++ .ver_major = VER_2_X, ++ .crit_int = 1, ++ .combo_int = 1, ++ .adc = 0, ++ .srot_split = 1, ++ .max_sensors = 16, ++ .trip_min_temp = 0, ++ .trip_max_temp = 204000, ++}; ++ + static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { + /* ----- SROT ------ */ + /* VERSION */ +@@ -104,6 +115,12 @@ struct tsens_plat_data data_tsens_v2 = { + .fields = tsens_v2_regfields, + }; + ++struct tsens_plat_data data_ipq8074 = { ++ .ops = &ops_generic_v2, ++ .feat = &ipq8074_feat, ++ .fields = tsens_v2_regfields, ++}; ++ + /* Kept around for backward compatibility with old msm8996.dtsi */ + struct tsens_plat_data data_8996 = { + .num_sensors = 13, +diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c +index 2e34561402c91..a128d41656d76 100644 +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -989,6 +989,9 @@ static const struct of_device_id tsens_table[] = { + { + .compatible = "qcom,ipq8064-tsens", + .data = &data_8960, ++ }, { ++ .compatible = "qcom,ipq8074-tsens", ++ .data = &data_ipq8074, + }, { + .compatible = "qcom,mdm9607-tsens", + .data = &data_9607, +diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h +index 747004476347c..8dd990d944ad7 100644 +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -599,6 +599,6 @@ extern struct tsens_plat_data data_8916, data_8939, data_8974, data_9607; + extern struct tsens_plat_data data_tsens_v1, data_8976; + + /* TSENS v2 targets */ +-extern struct tsens_plat_data data_8996, data_tsens_v2; ++extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; + + #endif /* __QCOM_TSENS_H__ */ +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0129-arm64-dts-ipq8074-add-thermal-nodes.patch b/target/linux/ipq807x/patches-5.15/0129-arm64-dts-ipq8074-add-thermal-nodes.patch new file mode 100644 index 00000000000000..fd50badd73338f --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0129-arm64-dts-ipq8074-add-thermal-nodes.patch @@ -0,0 +1,133 @@ +From 9484c0e4e39d7b009edc8eff11708eae6d37ec6c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 24 Dec 2021 20:33:59 +0100 +Subject: [PATCH 129/158] arm64: dts: ipq8074: add thermal nodes + +IPQ8074 has a tsens v2.3.0 peripheral which monitors +temperatures around the various subsystems on the +die. + +So lets add the tsens and thermal zone nodes, passive +CPU cooling will come in later patches after CPU frequency +scaling is supported. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 96 +++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 6d4fc286782d5..789fec7c6aa47 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -306,6 +306,16 @@ prng: rng@e3000 { + status = "disabled"; + }; + ++ tsens: thermal-sensor@4a9000 { ++ compatible = "qcom,ipq8074-tsens"; ++ reg = <0x4a9000 0x1000>, /* TM */ ++ <0x4a8000 0x1000>; /* SROT */ ++ interrupts = ; ++ interrupt-names = "combined"; ++ #qcom,sensors = <16>; ++ #thermal-sensor-cells = <1>; ++ }; ++ + cryptobam: dma-controller@704000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0x00704000 0x20000>; +@@ -906,4 +916,90 @@ apcs_glb: mailbox@b111000 { + #mbox-cells = <1>; + }; + }; ++ ++ thermal-zones { ++ nss-top-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 4>; ++ }; ++ ++ nss0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 5>; ++ }; ++ ++ nss1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 6>; ++ }; ++ ++ wcss-phya0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 7>; ++ }; ++ ++ wcss-phya1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 8>; ++ }; ++ ++ cpu0_thermal: cpu0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 9>; ++ }; ++ ++ cpu1_thermal: cpu1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 10>; ++ }; ++ ++ cpu2_thermal: cpu2-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 11>; ++ }; ++ ++ cpu3_thermal: cpu3-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 12>; ++ }; ++ ++ cluster_thermal: cluster-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 13>; ++ }; ++ ++ wcss-phyb0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 14>; ++ }; ++ ++ wcss-phyb1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 15>; ++ }; ++ }; + }; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0130-regulator-qcom_spmi-add-support-for-HT_P150.patch b/target/linux/ipq807x/patches-5.15/0130-regulator-qcom_spmi-add-support-for-HT_P150.patch new file mode 100644 index 00000000000000..d52bfdc1c6204d --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0130-regulator-qcom_spmi-add-support-for-HT_P150.patch @@ -0,0 +1,61 @@ +From 9e47db6a810adff5fe39d316eaa4d36883e5a557 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 11 May 2022 23:21:53 +0200 +Subject: [PATCH 130/158] regulator: qcom_spmi: add support for HT_P150 + +HT_P150 is a LDO PMOS regulator based on LV P150 using HFS430 layout +found in PMP8074 and PMS405 PMIC-s. + +Both PMP8074 and PMS405 define the programmable range as 1.616V to 3.304V +but the actual MAX output voltage depends on the exact LDO in each of +the PMIC-s. + +It has a max current of 150mA, voltage step of 8mV. + +Signed-off-by: Robert Marko +--- + drivers/regulator/qcom_spmi-regulator.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c +index 41424a3366d0e..49e5fe168391f 100644 +--- a/drivers/regulator/qcom_spmi-regulator.c ++++ b/drivers/regulator/qcom_spmi-regulator.c +@@ -164,6 +164,7 @@ enum spmi_regulator_subtype { + SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3 = 0x0f, + SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4 = 0x10, + SPMI_REGULATOR_SUBTYPE_HFS430 = 0x0a, ++ SPMI_REGULATOR_SUBTYPE_HT_P150 = 0x35, + }; + + enum spmi_common_regulator_registers { +@@ -544,6 +545,10 @@ static struct spmi_voltage_range hfs430_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 320000, 320000, 2040000, 2040000, 8000), + }; + ++static struct spmi_voltage_range ht_p150_ranges[] = { ++ SPMI_VOLTAGE_RANGE(0, 1616000, 1616000, 3304000, 3304000, 8000), ++}; ++ + static DEFINE_SPMI_SET_POINTS(pldo); + static DEFINE_SPMI_SET_POINTS(nldo1); + static DEFINE_SPMI_SET_POINTS(nldo2); +@@ -564,6 +569,7 @@ static DEFINE_SPMI_SET_POINTS(nldo660); + static DEFINE_SPMI_SET_POINTS(ht_lvpldo); + static DEFINE_SPMI_SET_POINTS(ht_nldo); + static DEFINE_SPMI_SET_POINTS(hfs430); ++static DEFINE_SPMI_SET_POINTS(ht_p150); + + static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf, + int len) +@@ -1458,6 +1464,7 @@ static const struct regulator_ops spmi_hfs430_ops = { + + static const struct spmi_regulator_mapping supported_regulators[] = { + /* type subtype dig_min dig_max ltype ops setpoints hpm_min */ ++ SPMI_VREG(LDO, HT_P150, 0, INF, HFS430, hfs430, ht_p150, 10000), + SPMI_VREG(BUCK, GP_CTL, 0, INF, SMPS, smps, smps, 100000), + SPMI_VREG(BUCK, HFS430, 0, INF, HFS430, hfs430, hfs430, 10000), + SPMI_VREG(LDO, N300, 0, INF, LDO, ldo, nldo1, 10000), +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0131-regulator-qcom_spmi-add-support-for-HT_P600.patch b/target/linux/ipq807x/patches-5.15/0131-regulator-qcom_spmi-add-support-for-HT_P600.patch new file mode 100644 index 00000000000000..b0ad0b2ab12154 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0131-regulator-qcom_spmi-add-support-for-HT_P600.patch @@ -0,0 +1,62 @@ +From c02ecb07c870e43856ff148e548487d200fdd821 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 18 May 2022 20:19:02 +0200 +Subject: [PATCH 131/158] regulator: qcom_spmi: add support for HT_P600 + +HT_P600 is a LDO PMOS regulator based on LV P600 using HFS430 layout +found in PMP8074 and PMS405 PMIC-s. + +Both PMP8074 and PMS405 define the programmable range as 1.704 to 1.896V +but the actual MAX output voltage depends on the exact LDO in each of +the PMIC-s. +Their usual voltage that they are used is 1.8V. + +It has a max current of 600mA, voltage step of 8mV. + +Signed-off-by: Robert Marko +--- + drivers/regulator/qcom_spmi-regulator.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c +index 49e5fe168391f..828e97b873dfb 100644 +--- a/drivers/regulator/qcom_spmi-regulator.c ++++ b/drivers/regulator/qcom_spmi-regulator.c +@@ -165,6 +165,7 @@ enum spmi_regulator_subtype { + SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4 = 0x10, + SPMI_REGULATOR_SUBTYPE_HFS430 = 0x0a, + SPMI_REGULATOR_SUBTYPE_HT_P150 = 0x35, ++ SPMI_REGULATOR_SUBTYPE_HT_P600 = 0x3d, + }; + + enum spmi_common_regulator_registers { +@@ -549,6 +550,10 @@ static struct spmi_voltage_range ht_p150_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 1616000, 1616000, 3304000, 3304000, 8000), + }; + ++static struct spmi_voltage_range ht_p600_ranges[] = { ++ SPMI_VOLTAGE_RANGE(0, 1704000, 1704000, 1896000, 1896000, 8000), ++}; ++ + static DEFINE_SPMI_SET_POINTS(pldo); + static DEFINE_SPMI_SET_POINTS(nldo1); + static DEFINE_SPMI_SET_POINTS(nldo2); +@@ -570,6 +575,7 @@ static DEFINE_SPMI_SET_POINTS(ht_lvpldo); + static DEFINE_SPMI_SET_POINTS(ht_nldo); + static DEFINE_SPMI_SET_POINTS(hfs430); + static DEFINE_SPMI_SET_POINTS(ht_p150); ++static DEFINE_SPMI_SET_POINTS(ht_p600); + + static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf, + int len) +@@ -1464,6 +1470,7 @@ static const struct regulator_ops spmi_hfs430_ops = { + + static const struct spmi_regulator_mapping supported_regulators[] = { + /* type subtype dig_min dig_max ltype ops setpoints hpm_min */ ++ SPMI_VREG(LDO, HT_P600, 0, INF, HFS430, hfs430, ht_p600, 10000), + SPMI_VREG(LDO, HT_P150, 0, INF, HFS430, hfs430, ht_p150, 10000), + SPMI_VREG(BUCK, GP_CTL, 0, INF, SMPS, smps, smps, 100000), + SPMI_VREG(BUCK, HFS430, 0, INF, HFS430, hfs430, hfs430, 10000), +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0132-regulator-qcom_spmi-add-support-for-PMP8074-regulato.patch b/target/linux/ipq807x/patches-5.15/0132-regulator-qcom_spmi-add-support-for-PMP8074-regulato.patch new file mode 100644 index 00000000000000..5090a10c66734e --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0132-regulator-qcom_spmi-add-support-for-PMP8074-regulato.patch @@ -0,0 +1,76 @@ +From 09c913b3cdf44231baa7a5479ff4ca585f7248f8 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 10 May 2022 22:41:36 +0200 +Subject: [PATCH 132/158] regulator: qcom_spmi: add support for PMP8074 + regulators + +PMP8074 is a companion PMIC for the Qualcomm IPQ8074 WiSoC-s. + +It features 5 HF-SMPS and 13 LDO regulators. + +HF-SMPS regulators are Buck HFS430 regulators. +L1, L2 and L3 are HT_N1200_ST subtype LDO regulators. +L4 is HT_N300_ST subtype LDO regulator. +L5 and L6 are HT_P600 subtype LDO regulators. +L7, L11, L12 and L13 are HT_P150 subtype LDO regulators. +L10 is HT_P50 subtype LDO regulator. + +This commit adds support for all of the buck regulators and LDO-s except +for L10 as I dont have documentation on its output voltage range. + +S3 is the CPU cluster voltage supply, S4 supplies the UBI32 NPU cores +and L11 is the SDIO/eMMC I/O voltage regulator required for high speeds. + +Signed-off-by: Robert Marko +--- +Changes in v4: +* Add remaining regulators, minus L11 +* Sort structure and compatible +--- + drivers/regulator/qcom_spmi-regulator.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c +index 828e97b873dfb..13521d47d5892 100644 +--- a/drivers/regulator/qcom_spmi-regulator.c ++++ b/drivers/regulator/qcom_spmi-regulator.c +@@ -2101,6 +2101,28 @@ static const struct spmi_regulator_data pm8005_regulators[] = { + { } + }; + ++static const struct spmi_regulator_data pmp8074_regulators[] = { ++ { "s1", 0x1400, "vdd_s1"}, ++ { "s2", 0x1700, "vdd_s2"}, ++ { "s3", 0x1a00, "vdd_s3"}, ++ { "s4", 0x1d00, "vdd_s4"}, ++ { "s5", 0x2000, "vdd_s5"}, ++ { "l1", 0x4000, "vdd_l1_l2"}, ++ { "l2", 0x4100, "vdd_l1_l2"}, ++ { "l3", 0x4200, "vdd_l3_l8"}, ++ { "l4", 0x4300, "vdd_l4"}, ++ { "l5", 0x4400, "vdd_l5_l6_l15"}, ++ { "l6", 0x4500, "vdd_l5_l6_l15"}, ++ { "l7", 0x4600, "vdd_l7"}, ++ { "l8", 0x4700, "vdd_l3_l8"}, ++ { "l9", 0x4800, "vdd_l9"}, ++ /* l10 is currently unsupported HT_P50 */ ++ { "l11", 0x4a00, "vdd_l10_l11_l12_l13"}, ++ { "l12", 0x4b00, "vdd_l10_l11_l12_l13"}, ++ { "l13", 0x4c00, "vdd_l10_l11_l12_l13"}, ++ { } ++}; ++ + static const struct spmi_regulator_data pms405_regulators[] = { + { "s3", 0x1a00, "vdd_s3"}, + { } +@@ -2117,6 +2139,7 @@ static const struct of_device_id qcom_spmi_regulator_match[] = { + { .compatible = "qcom,pmi8994-regulators", .data = &pmi8994_regulators }, + { .compatible = "qcom,pm660-regulators", .data = &pm660_regulators }, + { .compatible = "qcom,pm660l-regulators", .data = &pm660l_regulators }, ++ { .compatible = "qcom,pmp8074-regulators", .data = &pmp8074_regulators }, + { .compatible = "qcom,pms405-regulators", .data = &pms405_regulators }, + { } + }; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0133-arm64-dts-qcom-add-PMP8074-DTSI.patch b/target/linux/ipq807x/patches-5.15/0133-arm64-dts-qcom-add-PMP8074-DTSI.patch new file mode 100644 index 00000000000000..02b9f7e600fae8 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0133-arm64-dts-qcom-add-PMP8074-DTSI.patch @@ -0,0 +1,66 @@ +From 3df1bd091d125bb4fbe8ddb6fdb5adec53ca3ea0 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 18 May 2022 16:36:42 +0200 +Subject: [PATCH 133/158] arm64: dts: qcom: add PMP8074 DTSI + +PMP8074 is a companion PMIC to the Qualcomm IPQ8074 series that is +controlled via SPMI. + +Since we now have support for the regulators inside of it add DTSI +for it. + +Signed-off-by: Robert Marko +--- +Changes in v5: +* Remove #address-cells and #size-cells as they are not required for +regulator subnodes +--- + arch/arm64/boot/dts/qcom/pmp8074.dtsi | 36 +++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 arch/arm64/boot/dts/qcom/pmp8074.dtsi + +diff --git a/arch/arm64/boot/dts/qcom/pmp8074.dtsi b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +new file mode 100644 +index 0000000000000..7b3c9fe705aa3 +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +@@ -0,0 +1,36 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++ ++&spmi_bus { ++ pmic@1 { ++ compatible = "qcom,spmi-pmic"; ++ reg = <0x1 SPMI_USID>; ++ ++ regulators { ++ compatible = "qcom,pmp8074-regulators"; ++ ++ s3: s3 { ++ regulator-name = "vdd_s3"; ++ regulator-min-microvolt = <592000>; ++ regulator-max-microvolt = <1064000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ s4: s4 { ++ regulator-name = "vdd_s4"; ++ regulator-min-microvolt = <712000>; ++ regulator-max-microvolt = <992000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ l11: l11 { ++ regulator-name = "l11"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; ++ }; ++}; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0134-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch b/target/linux/ipq807x/patches-5.15/0134-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch new file mode 100644 index 00000000000000..baf9445c93d37d --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0134-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch @@ -0,0 +1,40 @@ +From fe01deb8dd23ca54ff6dc026186def3187bac140 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 19 May 2022 13:34:03 +0200 +Subject: [PATCH 134/158] arm64: dts: qcom: ipq8074-hk01: add VQMMC supply + +Since now we have control over the PMP8074 PMIC providing various system +voltages including L11 which provides the SDIO/eMMC I/O voltage set it as +the SDHCI VQMMC supply. + +This allows SDHCI controller to switch to 1.8V I/O mode and support high +speed modes like HS200 and HS400. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +index cc08dc4eb56a5..40b626d69c403 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +@@ -3,6 +3,7 @@ + /* Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + #include "ipq8074.dtsi" ++#include "pmp8074.dtsi" + + / { + #address-cells = <0x2>; +@@ -85,6 +86,7 @@ nand@0 { + + &sdhc_1 { + status = "okay"; ++ vqmmc-supply = <&l11>; + }; + + &qusb_phy_0 { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0135-mfd-qcom-spmi-pmic-Sort-compatibles-in-the-driver.patch b/target/linux/ipq807x/patches-5.15/0135-mfd-qcom-spmi-pmic-Sort-compatibles-in-the-driver.patch new file mode 100644 index 00000000000000..edb60f26b4bedc --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0135-mfd-qcom-spmi-pmic-Sort-compatibles-in-the-driver.patch @@ -0,0 +1,65 @@ +From d51a9bfef0eae54163f90bb7dfc931038830276e Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson +Date: Sun, 17 Oct 2021 09:12:16 -0700 +Subject: [PATCH 135/158] mfd: qcom-spmi-pmic: Sort compatibles in the driver + +Sort the compatibles in the driver, to make it easier to validate that +the DT binding and driver are in sync. + +Signed-off-by: Bjorn Andersson +Signed-off-by: Lee Jones +Link: https://lore.kernel.org/r/20211017161218.2378176-2-bjorn.andersson@linaro.org +--- + drivers/mfd/qcom-spmi-pmic.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c +index a35d5cf16faa5..8be07102a4684 100644 +--- a/drivers/mfd/qcom-spmi-pmic.c ++++ b/drivers/mfd/qcom-spmi-pmic.c +@@ -40,27 +40,27 @@ + #define PM660_SUBTYPE 0x1B + + static const struct of_device_id pmic_spmi_id_table[] = { +- { .compatible = "qcom,spmi-pmic", .data = (void *)COMMON_SUBTYPE }, +- { .compatible = "qcom,pm8941", .data = (void *)PM8941_SUBTYPE }, +- { .compatible = "qcom,pm8841", .data = (void *)PM8841_SUBTYPE }, ++ { .compatible = "qcom,pm660", .data = (void *)PM660_SUBTYPE }, ++ { .compatible = "qcom,pm660l", .data = (void *)PM660L_SUBTYPE }, ++ { .compatible = "qcom,pm8004", .data = (void *)PM8004_SUBTYPE }, ++ { .compatible = "qcom,pm8005", .data = (void *)PM8005_SUBTYPE }, + { .compatible = "qcom,pm8019", .data = (void *)PM8019_SUBTYPE }, +- { .compatible = "qcom,pm8226", .data = (void *)PM8226_SUBTYPE }, + { .compatible = "qcom,pm8110", .data = (void *)PM8110_SUBTYPE }, +- { .compatible = "qcom,pma8084", .data = (void *)PMA8084_SUBTYPE }, +- { .compatible = "qcom,pmi8962", .data = (void *)PMI8962_SUBTYPE }, +- { .compatible = "qcom,pmd9635", .data = (void *)PMD9635_SUBTYPE }, +- { .compatible = "qcom,pm8994", .data = (void *)PM8994_SUBTYPE }, +- { .compatible = "qcom,pmi8994", .data = (void *)PMI8994_SUBTYPE }, +- { .compatible = "qcom,pm8916", .data = (void *)PM8916_SUBTYPE }, +- { .compatible = "qcom,pm8004", .data = (void *)PM8004_SUBTYPE }, ++ { .compatible = "qcom,pm8226", .data = (void *)PM8226_SUBTYPE }, ++ { .compatible = "qcom,pm8841", .data = (void *)PM8841_SUBTYPE }, + { .compatible = "qcom,pm8909", .data = (void *)PM8909_SUBTYPE }, ++ { .compatible = "qcom,pm8916", .data = (void *)PM8916_SUBTYPE }, ++ { .compatible = "qcom,pm8941", .data = (void *)PM8941_SUBTYPE }, + { .compatible = "qcom,pm8950", .data = (void *)PM8950_SUBTYPE }, +- { .compatible = "qcom,pmi8950", .data = (void *)PMI8950_SUBTYPE }, ++ { .compatible = "qcom,pm8994", .data = (void *)PM8994_SUBTYPE }, + { .compatible = "qcom,pm8998", .data = (void *)PM8998_SUBTYPE }, ++ { .compatible = "qcom,pma8084", .data = (void *)PMA8084_SUBTYPE }, ++ { .compatible = "qcom,pmd9635", .data = (void *)PMD9635_SUBTYPE }, ++ { .compatible = "qcom,pmi8950", .data = (void *)PMI8950_SUBTYPE }, ++ { .compatible = "qcom,pmi8962", .data = (void *)PMI8962_SUBTYPE }, ++ { .compatible = "qcom,pmi8994", .data = (void *)PMI8994_SUBTYPE }, + { .compatible = "qcom,pmi8998", .data = (void *)PMI8998_SUBTYPE }, +- { .compatible = "qcom,pm8005", .data = (void *)PM8005_SUBTYPE }, +- { .compatible = "qcom,pm660l", .data = (void *)PM660L_SUBTYPE }, +- { .compatible = "qcom,pm660", .data = (void *)PM660_SUBTYPE }, ++ { .compatible = "qcom,spmi-pmic", .data = (void *)COMMON_SUBTYPE }, + { } + }; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0136-mfd-qcom-spmi-pmic-Add-missing-PMICs-supported-by-so.patch b/target/linux/ipq807x/patches-5.15/0136-mfd-qcom-spmi-pmic-Add-missing-PMICs-supported-by-so.patch new file mode 100644 index 00000000000000..42b2bca7d97f9d --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0136-mfd-qcom-spmi-pmic-Add-missing-PMICs-supported-by-so.patch @@ -0,0 +1,71 @@ +From ca0bd14c8b893aad5116278fb09ee041afc95d24 Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson +Date: Sun, 17 Oct 2021 09:12:18 -0700 +Subject: [PATCH 136/158] mfd: qcom-spmi-pmic: Add missing PMICs supported by + socinfo + +The Qualcomm socinfo driver has eight more PMICs described, add these to +the SPMI PMIC driver as well. + +Signed-off-by: Bjorn Andersson +Signed-off-by: Lee Jones +Link: https://lore.kernel.org/r/20211017161218.2378176-4-bjorn.andersson@linaro.org +--- + drivers/mfd/qcom-spmi-pmic.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c +index 8be07102a4684..1cacc00aa6c95 100644 +--- a/drivers/mfd/qcom-spmi-pmic.c ++++ b/drivers/mfd/qcom-spmi-pmic.c +@@ -31,6 +31,8 @@ + #define PM8916_SUBTYPE 0x0b + #define PM8004_SUBTYPE 0x0c + #define PM8909_SUBTYPE 0x0d ++#define PM8028_SUBTYPE 0x0e ++#define PM8901_SUBTYPE 0x0f + #define PM8950_SUBTYPE 0x10 + #define PMI8950_SUBTYPE 0x11 + #define PM8998_SUBTYPE 0x14 +@@ -38,6 +40,13 @@ + #define PM8005_SUBTYPE 0x18 + #define PM660L_SUBTYPE 0x1A + #define PM660_SUBTYPE 0x1B ++#define PM8150_SUBTYPE 0x1E ++#define PM8150L_SUBTYPE 0x1f ++#define PM8150B_SUBTYPE 0x20 ++#define PMK8002_SUBTYPE 0x21 ++#define PM8009_SUBTYPE 0x24 ++#define PM8150C_SUBTYPE 0x26 ++#define SMB2351_SUBTYPE 0x29 + + static const struct of_device_id pmic_spmi_id_table[] = { + { .compatible = "qcom,pm660", .data = (void *)PM660_SUBTYPE }, +@@ -45,9 +54,15 @@ static const struct of_device_id pmic_spmi_id_table[] = { + { .compatible = "qcom,pm8004", .data = (void *)PM8004_SUBTYPE }, + { .compatible = "qcom,pm8005", .data = (void *)PM8005_SUBTYPE }, + { .compatible = "qcom,pm8019", .data = (void *)PM8019_SUBTYPE }, ++ { .compatible = "qcom,pm8028", .data = (void *)PM8028_SUBTYPE }, + { .compatible = "qcom,pm8110", .data = (void *)PM8110_SUBTYPE }, ++ { .compatible = "qcom,pm8150", .data = (void *)PM8150_SUBTYPE }, ++ { .compatible = "qcom,pm8150b", .data = (void *)PM8150B_SUBTYPE }, ++ { .compatible = "qcom,pm8150c", .data = (void *)PM8150C_SUBTYPE }, ++ { .compatible = "qcom,pm8150l", .data = (void *)PM8150L_SUBTYPE }, + { .compatible = "qcom,pm8226", .data = (void *)PM8226_SUBTYPE }, + { .compatible = "qcom,pm8841", .data = (void *)PM8841_SUBTYPE }, ++ { .compatible = "qcom,pm8901", .data = (void *)PM8901_SUBTYPE }, + { .compatible = "qcom,pm8909", .data = (void *)PM8909_SUBTYPE }, + { .compatible = "qcom,pm8916", .data = (void *)PM8916_SUBTYPE }, + { .compatible = "qcom,pm8941", .data = (void *)PM8941_SUBTYPE }, +@@ -60,6 +75,8 @@ static const struct of_device_id pmic_spmi_id_table[] = { + { .compatible = "qcom,pmi8962", .data = (void *)PMI8962_SUBTYPE }, + { .compatible = "qcom,pmi8994", .data = (void *)PMI8994_SUBTYPE }, + { .compatible = "qcom,pmi8998", .data = (void *)PMI8998_SUBTYPE }, ++ { .compatible = "qcom,pmk8002", .data = (void *)PMK8002_SUBTYPE }, ++ { .compatible = "qcom,smb2351", .data = (void *)SMB2351_SUBTYPE }, + { .compatible = "qcom,spmi-pmic", .data = (void *)COMMON_SUBTYPE }, + { } + }; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0137-mfd-qcom-spmi-pmic-add-support-for-PMP8074.patch b/target/linux/ipq807x/patches-5.15/0137-mfd-qcom-spmi-pmic-add-support-for-PMP8074.patch new file mode 100644 index 00000000000000..e69610f13e0468 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0137-mfd-qcom-spmi-pmic-add-support-for-PMP8074.patch @@ -0,0 +1,30 @@ +From 58529ca9090192ea84557f101a5643b939dd6986 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 19 May 2022 14:51:53 +0200 +Subject: [PATCH 137/158] mfd: qcom-spmi-pmic: add support for PMP8074 + +Add support for PMP8074 PMIC which is a companion PMIC for the Qualcomm +IPQ8074 SoC-s. + +It shares the same subtype identifier as PM8901. + +Signed-off-by: Robert Marko +--- + drivers/mfd/qcom-spmi-pmic.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c +index 1cacc00aa6c95..664de43df6900 100644 +--- a/drivers/mfd/qcom-spmi-pmic.c ++++ b/drivers/mfd/qcom-spmi-pmic.c +@@ -76,6 +76,7 @@ static const struct of_device_id pmic_spmi_id_table[] = { + { .compatible = "qcom,pmi8994", .data = (void *)PMI8994_SUBTYPE }, + { .compatible = "qcom,pmi8998", .data = (void *)PMI8998_SUBTYPE }, + { .compatible = "qcom,pmk8002", .data = (void *)PMK8002_SUBTYPE }, ++ { .compatible = "qcom,pmp8074", .data = (void *)PM8901_SUBTYPE }, + { .compatible = "qcom,smb2351", .data = (void *)SMB2351_SUBTYPE }, + { .compatible = "qcom,spmi-pmic", .data = (void *)COMMON_SUBTYPE }, + { } +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0138-arm64-dts-pmp8074-add-PMP8074-compatible.patch b/target/linux/ipq807x/patches-5.15/0138-arm64-dts-pmp8074-add-PMP8074-compatible.patch new file mode 100644 index 00000000000000..7d170b32a4ddd9 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0138-arm64-dts-pmp8074-add-PMP8074-compatible.patch @@ -0,0 +1,31 @@ +From a12e8e9de91b6314a1595c362ab976074df02f9b Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 19 May 2022 14:54:13 +0200 +Subject: [PATCH 138/158] arm64: dts: pmp8074: add PMP8074 compatible + +According to the bindings, each SPMI PMIC should have both the IC specifc +as well as the generic SPMI PMIC compatible. + +So add the one specific to PMP8074. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/pmp8074.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/qcom/pmp8074.dtsi b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +index 7b3c9fe705aa3..a266092a59613 100644 +--- a/arch/arm64/boot/dts/qcom/pmp8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +@@ -4,7 +4,7 @@ + + &spmi_bus { + pmic@1 { +- compatible = "qcom,spmi-pmic"; ++ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; + reg = <0x1 SPMI_USID>; + + regulators { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0139-pinctrl-qcom-pmic-gpio-add-support-for-PMP8074.patch b/target/linux/ipq807x/patches-5.15/0139-pinctrl-qcom-pmic-gpio-add-support-for-PMP8074.patch new file mode 100644 index 00000000000000..5639aeec49fa73 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0139-pinctrl-qcom-pmic-gpio-add-support-for-PMP8074.patch @@ -0,0 +1,28 @@ +From bb477ff10b0560c237b692f76889ca6deb7530b8 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 19 May 2022 15:12:00 +0200 +Subject: [PATCH 139/158] pinctrl: qcom-pmic-gpio: add support for PMP8074 + +PMP8074 has 12 GPIO-s with holes on GPIO1 and GPIO12. + +Signed-off-by: Robert Marko +--- + drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +index b2562e8931397..fe1d3b260e271 100644 +--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c ++++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +@@ -1167,6 +1167,8 @@ static const struct of_device_id pmic_gpio_of_match[] = { + { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, + { .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pmm8155au-gpio", .data = (void *) 10 }, ++ /* pmp8074 has 12 GPIOs with holes on 1 and 12 */ ++ { .compatible = "qcom,pmp8074-gpio", .data = (void *) 12 }, + { .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 }, + /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0140-arm64-dts-pmp8074-add-GPIO-node.patch b/target/linux/ipq807x/patches-5.15/0140-arm64-dts-pmp8074-add-GPIO-node.patch new file mode 100644 index 00000000000000..7e21a4724b0817 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0140-arm64-dts-pmp8074-add-GPIO-node.patch @@ -0,0 +1,54 @@ +From 752b6ba0016927816cce11629131eafd629c0b7f Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 19 May 2022 15:13:07 +0200 +Subject: [PATCH 140/158] arm64: dts: pmp8074: add GPIO node + +PMP8074 provides 12 GPIO-s with holes at GPIO1 and GPIO12, so add the +required DT node for it. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/pmp8074.dtsi | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pmp8074.dtsi b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +index a266092a59613..20122d614dd1b 100644 +--- a/arch/arm64/boot/dts/qcom/pmp8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +@@ -3,6 +3,33 @@ + #include + + &spmi_bus { ++ pmic@0 { ++ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; ++ reg = <0x0 SPMI_USID>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pmp8074_gpios: gpio@c000 { ++ compatible = "qcom,pmp8074-gpio", "qcom,spmi-gpio"; ++ reg = <0xc000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmp8074_gpios 0 0 12>; ++ interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, ++ <0 0xc1 0 IRQ_TYPE_NONE>, ++ <0 0xc2 0 IRQ_TYPE_NONE>, ++ <0 0xc3 0 IRQ_TYPE_NONE>, ++ <0 0xc4 0 IRQ_TYPE_NONE>, ++ <0 0xc5 0 IRQ_TYPE_NONE>, ++ <0 0xc6 0 IRQ_TYPE_NONE>, ++ <0 0xc7 0 IRQ_TYPE_NONE>, ++ <0 0xc8 0 IRQ_TYPE_NONE>, ++ <0 0xc9 0 IRQ_TYPE_NONE>, ++ <0 0xca 0 IRQ_TYPE_NONE>, ++ <0 0xcb 0 IRQ_TYPE_NONE>; ++ }; ++ }; ++ + pmic@1 { + compatible = "qcom,pmp8074", "qcom,spmi-pmic"; + reg = <0x1 SPMI_USID>; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0141-power-Add-Qualcomm-APM.patch b/target/linux/ipq807x/patches-5.15/0141-power-Add-Qualcomm-APM.patch new file mode 100644 index 00000000000000..b1d16638a88558 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0141-power-Add-Qualcomm-APM.patch @@ -0,0 +1,1066 @@ +From 3dcc58ea838be376d31f485192ccae0dc47943c5 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 11 Apr 2022 14:38:08 +0200 +Subject: [PATCH 141/158] power: Add Qualcomm APM + +Allow building Qualcomm APM. + +Signed-off-by: Robert Marko +--- + drivers/power/Kconfig | 1 + + drivers/power/Makefile | 1 + + drivers/power/qcom/Kconfig | 7 + + drivers/power/qcom/Makefile | 1 + + drivers/power/qcom/apm.c | 944 +++++++++++++++++++++++++++++++++ + include/linux/power/qcom/apm.h | 48 ++ + 6 files changed, 1002 insertions(+) + create mode 100644 drivers/power/qcom/Kconfig + create mode 100644 drivers/power/qcom/Makefile + create mode 100644 drivers/power/qcom/apm.c + create mode 100644 include/linux/power/qcom/apm.h + +diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig +index 696bf77a70420..235f3cbf2a548 100644 +--- a/drivers/power/Kconfig ++++ b/drivers/power/Kconfig +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + source "drivers/power/reset/Kconfig" + source "drivers/power/supply/Kconfig" ++source "drivers/power/qcom/Kconfig" +diff --git a/drivers/power/Makefile b/drivers/power/Makefile +index effbf0377f321..cd239e730b2bf 100644 +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_POWER_RESET) += reset/ + obj-$(CONFIG_POWER_SUPPLY) += supply/ ++obj-$(CONFIG_QCOM_APM) += qcom/ +diff --git a/drivers/power/qcom/Kconfig b/drivers/power/qcom/Kconfig +new file mode 100644 +index 0000000000000..01993a6179d16 +--- /dev/null ++++ b/drivers/power/qcom/Kconfig +@@ -0,0 +1,7 @@ ++config QCOM_APM ++ bool "Qualcomm Technologies Inc platform specific APM driver" ++ help ++ Platform specific driver to manage the power source of ++ memory arrays. Interfaces with regulator drivers to ensure ++ SRAM Vmin requirements are met across different performance ++ levels. +diff --git a/drivers/power/qcom/Makefile b/drivers/power/qcom/Makefile +new file mode 100644 +index 0000000000000..43a288e62ef16 +--- /dev/null ++++ b/drivers/power/qcom/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_QCOM_APM) += apm.o +diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c +new file mode 100644 +index 0000000000000..bd93358e3d34c +--- /dev/null ++++ b/drivers/power/qcom/apm.c +@@ -0,0 +1,944 @@ ++/* ++ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * VDD_APCC ++ * ============================================================= ++ * | VDD_MX | | ++ * | ==========================|============= | ++ * ___|___ ___|___ ___|___ ___|___ ___|___ ___|___ ++ * | | | | | | | | | | | | ++ * | APCC | | MX HS | | MX HS | | APCC | | MX HS | | APCC | ++ * | HS | | | | | | HS | | | | HS | ++ * |_______| |_______| |_______| |_______| |_______| |_______| ++ * |_________| |_________| |__________| ++ * | | | ++ * ______|_____ ______|_____ _______|_____ ++ * | | | | | | ++ * | | | | | | ++ * | CPU MEM | | L2 MEM | | L3 MEM | ++ * | Arrays | | Arrays | | Arrays | ++ * | | | | | | ++ * |____________| |____________| |_____________| ++ * ++ */ ++ ++/* Register value definitions */ ++#define APCS_GFMUXA_SEL_VAL 0x13 ++#define APCS_GFMUXA_DESEL_VAL 0x03 ++#define MSM_APM_MX_MODE_VAL 0x00 ++#define MSM_APM_APCC_MODE_VAL 0x10 ++#define MSM_APM_MX_DONE_VAL 0x00 ++#define MSM_APM_APCC_DONE_VAL 0x03 ++#define MSM_APM_OVERRIDE_SEL_VAL 0xb0 ++#define MSM_APM_SEC_CLK_SEL_VAL 0x30 ++#define SPM_EVENT_SET_VAL 0x01 ++#define SPM_EVENT_CLEAR_VAL 0x00 ++ ++/* Register bit mask definitions */ ++#define MSM_APM_CTL_STS_MASK 0x0f ++ ++/* Register offset definitions */ ++#define APCC_APM_MODE 0x00000098 ++#define APCC_APM_CTL_STS 0x000000a8 ++#define APCS_SPARE 0x00000068 ++#define APCS_VERSION 0x00000fd0 ++ ++#define HMSS_VERSION_1P2 0x10020000 ++ ++#define MSM_APM_SWITCH_TIMEOUT_US 10 ++#define SPM_WAKEUP_DELAY_US 2 ++#define SPM_EVENT_NUM 6 ++ ++#define MSM_APM_DRIVER_NAME "qcom,msm-apm" ++ ++enum { ++ MSM8996_ID, ++ MSM8953_ID, ++ IPQ807x_ID, ++}; ++ ++struct msm_apm_ctrl_dev { ++ struct list_head list; ++ struct device *dev; ++ enum msm_apm_supply supply; ++ spinlock_t lock; ++ void __iomem *reg_base; ++ void __iomem *apcs_csr_base; ++ void __iomem **apcs_spm_events_addr; ++ void __iomem *apc0_pll_ctl_addr; ++ void __iomem *apc1_pll_ctl_addr; ++ u32 version; ++ struct dentry *debugfs; ++ u32 msm_id; ++}; ++ ++#if defined(CONFIG_DEBUG_FS) ++static struct dentry *apm_debugfs_base; ++#endif ++ ++static DEFINE_MUTEX(apm_ctrl_list_mutex); ++static LIST_HEAD(apm_ctrl_list); ++ ++/* ++ * Get the resources associated with the APM controller from device tree ++ * and remap all I/O addresses that are relevant to this HW revision. ++ */ ++static int msm_apm_ctrl_devm_ioremap(struct platform_device *pdev, ++ struct msm_apm_ctrl_dev *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ static const char *res_name[SPM_EVENT_NUM] = { ++ "apc0-l2-spm", ++ "apc1-l2-spm", ++ "apc0-cpu0-spm", ++ "apc0-cpu1-spm", ++ "apc1-cpu0-spm", ++ "apc1-cpu1-spm" ++ }; ++ int i, ret = 0; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb"); ++ if (!res) { ++ dev_err(dev, "Missing PM APCC Global register physical address"); ++ return -EINVAL; ++ } ++ ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!ctrl->reg_base) { ++ dev_err(dev, "Failed to map PM APCC Global registers\n"); ++ return -ENOMEM; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs-csr"); ++ if (!res) { ++ dev_err(dev, "Missing APCS CSR physical base address"); ++ return -EINVAL; ++ } ++ ctrl->apcs_csr_base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!ctrl->apcs_csr_base) { ++ dev_err(dev, "Failed to map APCS CSR registers\n"); ++ return -ENOMEM; ++ } ++ ++ ctrl->version = readl_relaxed(ctrl->apcs_csr_base + APCS_VERSION); ++ ++ if (ctrl->version >= HMSS_VERSION_1P2) ++ return ret; ++ ++ ctrl->apcs_spm_events_addr = devm_kzalloc(&pdev->dev, ++ SPM_EVENT_NUM ++ * sizeof(void __iomem *), ++ GFP_KERNEL); ++ if (!ctrl->apcs_spm_events_addr) { ++ dev_err(dev, "Failed to allocate memory for APCS SPM event registers\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < SPM_EVENT_NUM; i++) { ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ res_name[i]); ++ if (!res) { ++ dev_err(dev, "Missing address for %s\n", res_name[i]); ++ ret = -EINVAL; ++ goto free_events; ++ } ++ ++ ctrl->apcs_spm_events_addr[i] = devm_ioremap(dev, res->start, ++ resource_size(res)); ++ if (!ctrl->apcs_spm_events_addr[i]) { ++ dev_err(dev, "Failed to map %s\n", res_name[i]); ++ ret = -ENOMEM; ++ goto free_events; ++ } ++ ++ dev_dbg(dev, "%s event phys: %pa virt:0x%p\n", res_name[i], ++ &res->start, ctrl->apcs_spm_events_addr[i]); ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "apc0-pll-ctl"); ++ if (!res) { ++ dev_err(dev, "Missing APC0 PLL CTL physical address\n"); ++ ret = -EINVAL; ++ goto free_events; ++ } ++ ++ ctrl->apc0_pll_ctl_addr = devm_ioremap(dev, ++ res->start, ++ resource_size(res)); ++ if (!ctrl->apc0_pll_ctl_addr) { ++ dev_err(dev, "Failed to map APC0 PLL CTL register\n"); ++ ret = -ENOMEM; ++ goto free_events; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "apc1-pll-ctl"); ++ if (!res) { ++ dev_err(dev, "Missing APC1 PLL CTL physical address\n"); ++ ret = -EINVAL; ++ goto free_events; ++ } ++ ++ ctrl->apc1_pll_ctl_addr = devm_ioremap(dev, ++ res->start, ++ resource_size(res)); ++ if (!ctrl->apc1_pll_ctl_addr) { ++ dev_err(dev, "Failed to map APC1 PLL CTL register\n"); ++ ret = -ENOMEM; ++ goto free_events; ++ } ++ ++ return ret; ++ ++free_events: ++ devm_kfree(dev, ctrl->apcs_spm_events_addr); ++ return ret; ++} ++ ++/* 8953 register offset definition */ ++#define MSM8953_APM_DLY_CNTR 0x2ac ++ ++/* Register field shift definitions */ ++#define APM_CTL_SEL_SWITCH_DLY_SHIFT 0 ++#define APM_CTL_RESUME_CLK_DLY_SHIFT 8 ++#define APM_CTL_HALT_CLK_DLY_SHIFT 16 ++#define APM_CTL_POST_HALT_DLY_SHIFT 24 ++ ++/* Register field mask definitions */ ++#define APM_CTL_SEL_SWITCH_DLY_MASK GENMASK(7, 0) ++#define APM_CTL_RESUME_CLK_DLY_MASK GENMASK(15, 8) ++#define APM_CTL_HALT_CLK_DLY_MASK GENMASK(23, 16) ++#define APM_CTL_POST_HALT_DLY_MASK GENMASK(31, 24) ++ ++/* ++ * Get the resources associated with the msm8953 APM controller from ++ * device tree, remap all I/O addresses, and program the initial ++ * register configuration required for the 8953 APM controller device. ++ */ ++static int msm8953_apm_ctrl_init(struct platform_device *pdev, ++ struct msm_apm_ctrl_dev *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ u32 delay_counter, val = 0, regval = 0; ++ int rc = 0; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb"); ++ if (!res) { ++ dev_err(dev, "Missing PM APCC Global register physical address\n"); ++ return -ENODEV; ++ } ++ ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!ctrl->reg_base) { ++ dev_err(dev, "Failed to map PM APCC Global registers\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * Initial APM register configuration required before starting ++ * APM HW controller. ++ */ ++ regval = readl_relaxed(ctrl->reg_base + MSM8953_APM_DLY_CNTR); ++ val = regval; ++ ++ if (of_find_property(dev->of_node, "qcom,apm-post-halt-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-post-halt-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-post-halt-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_POST_HALT_DLY_MASK; ++ val |= (delay_counter << APM_CTL_POST_HALT_DLY_SHIFT) ++ & APM_CTL_POST_HALT_DLY_MASK; ++ } ++ ++ if (of_find_property(dev->of_node, "qcom,apm-halt-clk-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-halt-clk-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-halt-clk-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_HALT_CLK_DLY_MASK; ++ val |= (delay_counter << APM_CTL_HALT_CLK_DLY_SHIFT) ++ & APM_CTL_HALT_CLK_DLY_MASK; ++ } ++ ++ if (of_find_property(dev->of_node, "qcom,apm-resume-clk-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-resume-clk-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-resume-clk-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_RESUME_CLK_DLY_MASK; ++ val |= (delay_counter << APM_CTL_RESUME_CLK_DLY_SHIFT) ++ & APM_CTL_RESUME_CLK_DLY_MASK; ++ } ++ ++ if (of_find_property(dev->of_node, "qcom,apm-sel-switch-delay", NULL)) { ++ rc = of_property_read_u32(dev->of_node, ++ "qcom,apm-sel-switch-delay", &delay_counter); ++ if (rc < 0) { ++ dev_err(dev, "apm-sel-switch-delay read failed, rc = %d", ++ rc); ++ return rc; ++ } ++ ++ val &= ~APM_CTL_SEL_SWITCH_DLY_MASK; ++ val |= (delay_counter << APM_CTL_SEL_SWITCH_DLY_SHIFT) ++ & APM_CTL_SEL_SWITCH_DLY_MASK; ++ } ++ ++ if (val != regval) { ++ writel_relaxed(val, ctrl->reg_base + MSM8953_APM_DLY_CNTR); ++ /* make sure write completes before return */ ++ mb(); ++ } ++ ++ return rc; ++} ++ ++static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int i, timeout = MSM_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Clear SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_CLEAR_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ ++ udelay(SPM_WAKEUP_DELAY_US); ++ ++ /* Switch APC/CBF to GPLL0 clock */ ++ writel_relaxed(APCS_GFMUXA_SEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ ++ /* Ensure writes complete before proceeding */ ++ mb(); ++ } ++ ++ /* Switch arrays to MX supply and wait for its completion */ ++ writel_relaxed(MSM_APM_MX_MODE_VAL, ctrl_dev->reg_base + ++ APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS); ++ if ((regval & MSM_APM_CTL_STS_MASK) == ++ MSM_APM_MX_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Switch APC/CBF clocks to original source */ ++ writel_relaxed(APCS_GFMUXA_DESEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ ++ /* Complete clock source switch before SPM event sequence */ ++ mb(); ++ ++ /* Set SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_SET_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ } ++ ++ if (!ret) { ++ ctrl_dev->supply = MSM_APM_SUPPLY_MX; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++static int msm8996_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int i, timeout = MSM_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Clear SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_CLEAR_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ ++ udelay(SPM_WAKEUP_DELAY_US); ++ ++ /* Switch APC/CBF to GPLL0 clock */ ++ writel_relaxed(APCS_GFMUXA_SEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ ++ /* Ensure previous writes complete before proceeding */ ++ mb(); ++ } ++ ++ /* Switch arrays to APCC supply and wait for its completion */ ++ writel_relaxed(MSM_APM_APCC_MODE_VAL, ctrl_dev->reg_base + ++ APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS); ++ if ((regval & MSM_APM_CTL_STS_MASK) == ++ MSM_APM_APCC_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } ++ ++ /* Perform revision-specific programming steps */ ++ if (ctrl_dev->version < HMSS_VERSION_1P2) { ++ /* Set SPM events */ ++ for (i = 0; i < SPM_EVENT_NUM; i++) ++ writel_relaxed(SPM_EVENT_SET_VAL, ++ ctrl_dev->apcs_spm_events_addr[i]); ++ ++ /* Complete SPM event sequence before clock source switch */ ++ mb(); ++ ++ /* Switch APC/CBF clocks to original source */ ++ writel_relaxed(APCS_GFMUXA_DESEL_VAL, ++ ctrl_dev->apcs_csr_base + APCS_SPARE); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc0_pll_ctl_addr); ++ ndelay(200); ++ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, ++ ctrl_dev->apc1_pll_ctl_addr); ++ } ++ ++ if (!ret) { ++ ctrl_dev->supply = MSM_APM_SUPPLY_APCC; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++/* 8953 register value definitions */ ++#define MSM8953_APM_MX_MODE_VAL 0x00 ++#define MSM8953_APM_APCC_MODE_VAL 0x02 ++#define MSM8953_APM_MX_DONE_VAL 0x00 ++#define MSM8953_APM_APCC_DONE_VAL 0x03 ++ ++/* 8953 register offset definitions */ ++#define MSM8953_APCC_APM_MODE 0x000002a8 ++#define MSM8953_APCC_APM_CTL_STS 0x000002b0 ++ ++/* 8953 constants */ ++#define MSM8953_APM_SWITCH_TIMEOUT_US 500 ++ ++/* Register bit mask definitions */ ++#define MSM8953_APM_CTL_STS_MASK 0x1f ++ ++static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Switch arrays to MX supply and wait for its completion */ ++ writel_relaxed(MSM8953_APM_MX_MODE_VAL, ctrl_dev->reg_base + ++ MSM8953_APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + ++ MSM8953_APCC_APM_CTL_STS); ++ if ((regval & MSM8953_APM_CTL_STS_MASK) == ++ MSM8953_APM_MX_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } else { ++ ctrl_dev->supply = MSM_APM_SUPPLY_MX; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; ++ u32 regval; ++ int ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctrl_dev->lock, flags); ++ ++ /* Switch arrays to APCC supply and wait for its completion */ ++ writel_relaxed(MSM8953_APM_APCC_MODE_VAL, ctrl_dev->reg_base + ++ MSM8953_APCC_APM_MODE); ++ ++ /* Ensure write above completes before delaying */ ++ mb(); ++ ++ while (timeout > 0) { ++ regval = readl_relaxed(ctrl_dev->reg_base + ++ MSM8953_APCC_APM_CTL_STS); ++ if ((regval & MSM8953_APM_CTL_STS_MASK) == ++ MSM8953_APM_APCC_DONE_VAL) ++ break; ++ ++ udelay(1); ++ timeout--; ++ } ++ ++ if (timeout == 0) { ++ ret = -ETIMEDOUT; ++ dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n", ++ regval); ++ } else { ++ ctrl_dev->supply = MSM_APM_SUPPLY_APCC; ++ dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n"); ++ } ++ ++ spin_unlock_irqrestore(&ctrl_dev->lock, flags); ++ ++ return ret; ++} ++ ++static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int ret = 0; ++ ++ switch (ctrl_dev->msm_id) { ++ case MSM8996_ID: ++ ret = msm8996_apm_switch_to_mx(ctrl_dev); ++ break; ++ case MSM8953_ID: ++ case IPQ807x_ID: ++ ret = msm8953_apm_switch_to_mx(ctrl_dev); ++ break; ++ } ++ ++ return ret; ++} ++ ++static int msm_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ int ret = 0; ++ ++ switch (ctrl_dev->msm_id) { ++ case MSM8996_ID: ++ ret = msm8996_apm_switch_to_apcc(ctrl_dev); ++ break; ++ case MSM8953_ID: ++ case IPQ807x_ID: ++ ret = msm8953_apm_switch_to_apcc(ctrl_dev); ++ break; ++ } ++ ++ return ret; ++} ++ ++/** ++ * msm_apm_get_supply() - Returns the supply that is currently ++ * powering the memory arrays ++ * @ctrl_dev: Pointer to an MSM APM controller device ++ * ++ * Returns the supply currently selected by the APM. ++ */ ++int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ return ctrl_dev->supply; ++} ++EXPORT_SYMBOL(msm_apm_get_supply); ++ ++/** ++ * msm_apm_set_supply() - Perform the necessary steps to switch the voltage ++ * source of the memory arrays to a given supply ++ * @ctrl_dev: Pointer to an MSM APM controller device ++ * @supply: Power rail to use as supply for the memory ++ * arrays ++ * ++ * Returns 0 on success, -ETIMEDOUT on APM switch timeout, or -EPERM if ++ * the supply is not supported. ++ */ ++int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, ++ enum msm_apm_supply supply) ++{ ++ int ret; ++ ++ switch (supply) { ++ case MSM_APM_SUPPLY_APCC: ++ ret = msm_apm_switch_to_apcc(ctrl_dev); ++ break; ++ case MSM_APM_SUPPLY_MX: ++ ret = msm_apm_switch_to_mx(ctrl_dev); ++ break; ++ default: ++ ret = -EPERM; ++ break; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(msm_apm_set_supply); ++ ++/** ++ * msm_apm_ctrl_dev_get() - get a handle to the MSM APM controller linked to ++ * the device in device tree ++ * @dev: Pointer to the device ++ * ++ * The device must specify "qcom,apm-ctrl" property in its device tree ++ * node which points to an MSM APM controller device node. ++ * ++ * Returns an MSM APM controller handle if successful or ERR_PTR on any error. ++ * If the APM controller device hasn't probed yet, ERR_PTR(-EPROBE_DEFER) is ++ * returned. ++ */ ++struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev) ++{ ++ struct msm_apm_ctrl_dev *ctrl_dev = NULL; ++ struct msm_apm_ctrl_dev *dev_found = ERR_PTR(-EPROBE_DEFER); ++ struct device_node *ctrl_node; ++ ++ if (!dev || !dev->of_node) { ++ pr_err("Invalid device node\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ ctrl_node = of_parse_phandle(dev->of_node, "qcom,apm-ctrl", 0); ++ if (!ctrl_node) { ++ pr_err("Could not find qcom,apm-ctrl property in %s\n", ++ dev->of_node->full_name); ++ return ERR_PTR(-ENXIO); ++ } ++ ++ mutex_lock(&apm_ctrl_list_mutex); ++ list_for_each_entry(ctrl_dev, &apm_ctrl_list, list) { ++ if (ctrl_dev->dev && ctrl_dev->dev->of_node == ctrl_node) { ++ dev_found = ctrl_dev; ++ break; ++ } ++ } ++ mutex_unlock(&apm_ctrl_list_mutex); ++ ++ of_node_put(ctrl_node); ++ return dev_found; ++} ++EXPORT_SYMBOL(msm_apm_ctrl_dev_get); ++ ++#if defined(CONFIG_DEBUG_FS) ++ ++static int apm_supply_dbg_open(struct inode *inode, struct file *filep) ++{ ++ filep->private_data = inode->i_private; ++ ++ return 0; ++} ++ ++static ssize_t apm_supply_dbg_read(struct file *filep, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct msm_apm_ctrl_dev *ctrl_dev = filep->private_data; ++ char buf[10]; ++ int len; ++ ++ if (!ctrl_dev) { ++ pr_err("invalid apm ctrl handle\n"); ++ return -ENODEV; ++ } ++ ++ if (ctrl_dev->supply == MSM_APM_SUPPLY_APCC) ++ len = snprintf(buf, sizeof(buf), "APCC\n"); ++ else if (ctrl_dev->supply == MSM_APM_SUPPLY_MX) ++ len = snprintf(buf, sizeof(buf), "MX\n"); ++ else ++ len = snprintf(buf, sizeof(buf), "ERR\n"); ++ ++ return simple_read_from_buffer(ubuf, count, ppos, buf, len); ++} ++ ++static const struct file_operations apm_supply_fops = { ++ .open = apm_supply_dbg_open, ++ .read = apm_supply_dbg_read, ++}; ++ ++static void apm_debugfs_base_init(void) ++{ ++ apm_debugfs_base = debugfs_create_dir("msm-apm", NULL); ++ ++ if (IS_ERR_OR_NULL(apm_debugfs_base)) ++ pr_err("msm-apm debugfs base directory creation failed\n"); ++} ++ ++static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ struct dentry *temp; ++ ++ if (IS_ERR_OR_NULL(apm_debugfs_base)) { ++ pr_err("Base directory missing, cannot create apm debugfs nodes\n"); ++ return; ++ } ++ ++ ctrl_dev->debugfs = debugfs_create_dir(dev_name(ctrl_dev->dev), ++ apm_debugfs_base); ++ if (IS_ERR_OR_NULL(ctrl_dev->debugfs)) { ++ pr_err("%s debugfs directory creation failed\n", ++ dev_name(ctrl_dev->dev)); ++ return; ++ } ++ ++ temp = debugfs_create_file("supply", S_IRUGO, ctrl_dev->debugfs, ++ ctrl_dev, &apm_supply_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ pr_err("supply mode creation failed\n"); ++ return; ++ } ++} ++ ++static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev) ++{ ++ if (!IS_ERR_OR_NULL(ctrl_dev->debugfs)) ++ debugfs_remove_recursive(ctrl_dev->debugfs); ++} ++ ++static void apm_debugfs_base_remove(void) ++{ ++ debugfs_remove_recursive(apm_debugfs_base); ++} ++#else ++ ++static void apm_debugfs_base_init(void) ++{} ++ ++static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev) ++{} ++ ++static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev) ++{} ++ ++static void apm_debugfs_base_remove(void) ++{} ++ ++#endif ++ ++static struct of_device_id msm_apm_match_table[] = { ++ { ++ .compatible = "qcom,msm-apm", ++ .data = (void *)(uintptr_t)MSM8996_ID, ++ }, ++ { ++ .compatible = "qcom,msm8953-apm", ++ .data = (void *)(uintptr_t)MSM8953_ID, ++ }, ++ { ++ .compatible = "qcom,ipq807x-apm", ++ .data = (void *)(uintptr_t)IPQ807x_ID, ++ }, ++ {} ++}; ++ ++static int msm_apm_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct msm_apm_ctrl_dev *ctrl; ++ const struct of_device_id *match; ++ int ret = 0; ++ ++ dev_dbg(dev, "probing MSM Array Power Mux driver\n"); ++ ++ if (!dev->of_node) { ++ dev_err(dev, "Device tree node is missing\n"); ++ return -ENODEV; ++ } ++ ++ match = of_match_device(msm_apm_match_table, dev); ++ if (!match) ++ return -ENODEV; ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) { ++ dev_err(dev, "MSM APM controller memory allocation failed\n"); ++ return -ENOMEM; ++ } ++ ++ INIT_LIST_HEAD(&ctrl->list); ++ spin_lock_init(&ctrl->lock); ++ ctrl->dev = dev; ++ ctrl->msm_id = (uintptr_t)match->data; ++ platform_set_drvdata(pdev, ctrl); ++ ++ switch (ctrl->msm_id) { ++ case MSM8996_ID: ++ ret = msm_apm_ctrl_devm_ioremap(pdev, ctrl); ++ if (ret) { ++ dev_err(dev, "Failed to add APM controller device\n"); ++ return ret; ++ } ++ break; ++ case MSM8953_ID: ++ case IPQ807x_ID: ++ ret = msm8953_apm_ctrl_init(pdev, ctrl); ++ if (ret) { ++ dev_err(dev, "Failed to initialize APM controller device: ret=%d\n", ++ ret); ++ return ret; ++ } ++ break; ++ default: ++ dev_err(dev, "unable to add APM controller device for msm_id:%d\n", ++ ctrl->msm_id); ++ return -ENODEV; ++ } ++ ++ apm_debugfs_init(ctrl); ++ mutex_lock(&apm_ctrl_list_mutex); ++ list_add_tail(&ctrl->list, &apm_ctrl_list); ++ mutex_unlock(&apm_ctrl_list_mutex); ++ ++ dev_dbg(dev, "MSM Array Power Mux driver probe successful"); ++ ++ return ret; ++} ++ ++static int msm_apm_remove(struct platform_device *pdev) ++{ ++ struct msm_apm_ctrl_dev *ctrl_dev; ++ ++ ctrl_dev = platform_get_drvdata(pdev); ++ if (ctrl_dev) { ++ mutex_lock(&apm_ctrl_list_mutex); ++ list_del(&ctrl_dev->list); ++ mutex_unlock(&apm_ctrl_list_mutex); ++ apm_debugfs_deinit(ctrl_dev); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver msm_apm_driver = { ++ .driver = { ++ .name = MSM_APM_DRIVER_NAME, ++ .of_match_table = msm_apm_match_table, ++ .owner = THIS_MODULE, ++ }, ++ .probe = msm_apm_probe, ++ .remove = msm_apm_remove, ++}; ++ ++static int __init msm_apm_init(void) ++{ ++ apm_debugfs_base_init(); ++ return platform_driver_register(&msm_apm_driver); ++} ++ ++static void __exit msm_apm_exit(void) ++{ ++ platform_driver_unregister(&msm_apm_driver); ++ apm_debugfs_base_remove(); ++} ++ ++arch_initcall(msm_apm_init); ++module_exit(msm_apm_exit); ++ ++MODULE_DESCRIPTION("MSM Array Power Mux driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/power/qcom/apm.h b/include/linux/power/qcom/apm.h +new file mode 100644 +index 0000000000000..432683578b76a +--- /dev/null ++++ b/include/linux/power/qcom/apm.h +@@ -0,0 +1,48 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __LINUX_POWER_QCOM_APM_H__ ++#define __LINUX_POWER_QCOM_APM_H__ ++ ++#include ++#include ++ ++/** ++ * enum msm_apm_supply - supported power rails to supply memory arrays ++ * %MSM_APM_SUPPLY_APCC: to enable selection of VDD_APCC rail as supply ++ * %MSM_APM_SUPPLY_MX: to enable selection of VDD_MX rail as supply ++ */ ++enum msm_apm_supply { ++ MSM_APM_SUPPLY_APCC, ++ MSM_APM_SUPPLY_MX, ++}; ++ ++/* Handle used to identify an APM controller device */ ++struct msm_apm_ctrl_dev; ++ ++#ifdef CONFIG_QCOM_APM ++struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev); ++int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, ++ enum msm_apm_supply supply); ++int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev); ++ ++#else ++static inline struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev) ++{ return ERR_PTR(-EPERM); } ++static inline int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, ++ enum msm_apm_supply supply) ++{ return -EPERM; } ++static inline int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev) ++{ return -EPERM; } ++#endif ++#endif +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0142-regulator-add-Qualcomm-CPR-regulators.patch b/target/linux/ipq807x/patches-5.15/0142-regulator-add-Qualcomm-CPR-regulators.patch new file mode 100644 index 00000000000000..bcc0e5f0f23a54 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0142-regulator-add-Qualcomm-CPR-regulators.patch @@ -0,0 +1,12171 @@ +From 5b8bfc38fe6ec012171ba83afd94f0cd6a540e56 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 11 Apr 2022 14:35:36 +0200 +Subject: [PATCH 142/158] regulator: add Qualcomm CPR regulators + +Allow building Qualcomm CPR regulators. + +Signed-off-by: Robert Marko +--- + drivers/regulator/Kconfig | 33 + + drivers/regulator/Makefile | 3 + + drivers/regulator/cpr3-npu-regulator.c | 695 +++ + drivers/regulator/cpr3-regulator.c | 5112 +++++++++++++++++++++++ + drivers/regulator/cpr3-regulator.h | 1211 ++++++ + drivers/regulator/cpr3-util.c | 2750 ++++++++++++ + drivers/regulator/cpr4-apss-regulator.c | 1819 ++++++++ + include/soc/qcom/socinfo.h | 463 ++ + 8 files changed, 12086 insertions(+) + create mode 100644 drivers/regulator/cpr3-npu-regulator.c + create mode 100644 drivers/regulator/cpr3-regulator.c + create mode 100644 drivers/regulator/cpr3-regulator.h + create mode 100644 drivers/regulator/cpr3-util.c + create mode 100644 drivers/regulator/cpr4-apss-regulator.c + create mode 100644 include/soc/qcom/socinfo.h + +diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig +index 4fd13b06231f2..92164c80cbc42 100644 +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -1423,5 +1423,38 @@ config REGULATOR_QCOM_LABIBB + boost regulator and IBB can be used as a negative boost regulator + for LCD display panel. + ++config REGULATOR_CPR3 ++ bool "QCOM CPR3 regulator core support" ++ help ++ This driver supports Core Power Reduction (CPR) version 3 controllers ++ which are used by some Qualcomm Technologies, Inc. SoCs to ++ manage important voltage regulators. CPR3 controllers are capable of ++ monitoring several ring oscillator sensing loops simultaneously. The ++ CPR3 controller informs software when the silicon conditions require ++ the supply voltage to be increased or decreased. On certain supply ++ rails, the CPR3 controller is able to propagate the voltage increase ++ or decrease requests all the way to the PMIC without software ++ involvement. ++ ++config REGULATOR_CPR3_NPU ++ bool "QCOM CPR3 regulator for NPU" ++ depends on OF && REGULATOR_CPR3 ++ help ++ This driver supports Qualcomm Technologies, Inc. NPU CPR3 ++ regulator Which will always operate in open loop. ++ ++config REGULATOR_CPR4_APSS ++ bool "QCOM CPR4 regulator for APSS" ++ depends on OF && REGULATOR_CPR3 ++ help ++ This driver supports Qualcomm Technologies, Inc. APSS application ++ processor specific features including memory array power mux (APM) ++ switching, one CPR4 thread which monitor the two APSS clusters that ++ are both powered by a shared supply, hardware closed-loop auto ++ voltage stepping, voltage adjustments based on online core count, ++ voltage adjustments based on temperature readings, and voltage ++ adjustments for performance boost mode. This driver reads both initial ++ voltage and CPR target quotient values out of hardware fuses. ++ + endif + +diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile +index 9e382b50a5ef0..2e521cb39e7e5 100644 +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -105,6 +105,9 @@ obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o + obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o + obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o + obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o ++obj-$(CONFIG_REGULATOR_CPR3) += cpr3-regulator.o cpr3-util.o ++obj-$(CONFIG_REGULATOR_CPR3_NPU) += cpr3-npu-regulator.o ++obj-$(CONFIG_REGULATOR_CPR4_APSS) += cpr4-apss-regulator.o + obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o + obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o + obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o +diff --git a/drivers/regulator/cpr3-npu-regulator.c b/drivers/regulator/cpr3-npu-regulator.c +new file mode 100644 +index 0000000000000..e808073c08500 +--- /dev/null ++++ b/drivers/regulator/cpr3-npu-regulator.c +@@ -0,0 +1,695 @@ ++/* ++ * Copyright (c) 2017, The Linux Foundation. All rights reserved. ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpr3-regulator.h" ++ ++#define IPQ807x_NPU_FUSE_CORNERS 2 ++#define IPQ817x_NPU_FUSE_CORNERS 1 ++#define IPQ807x_NPU_FUSE_STEP_VOLT 8000 ++#define IPQ807x_NPU_VOLTAGE_FUSE_SIZE 6 ++#define IPQ807x_NPU_CPR_CLOCK_RATE 19200000 ++ ++#define IPQ807x_NPU_CPR_TCSR_START 6 ++#define IPQ807x_NPU_CPR_TCSR_END 7 ++ ++#define NPU_TSENS 5 ++ ++u32 g_valid_npu_fuse_count = IPQ807x_NPU_FUSE_CORNERS; ++/** ++ * struct cpr3_ipq807x_npu_fuses - NPU specific fuse data for IPQ807x ++ * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value ++ * for each fuse corner (raw, not converted to a voltage) ++ * This struct holds the values for all of the fuses read from memory. ++ */ ++struct cpr3_ipq807x_npu_fuses { ++ u64 init_voltage[IPQ807x_NPU_FUSE_CORNERS]; ++}; ++ ++/* ++ * Constants which define the name of each fuse corner. ++ */ ++enum cpr3_ipq807x_npu_fuse_corner { ++ CPR3_IPQ807x_NPU_FUSE_CORNER_NOM = 0, ++ CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO = 1, ++}; ++ ++static const char * const cpr3_ipq807x_npu_fuse_corner_name[] = { ++ [CPR3_IPQ807x_NPU_FUSE_CORNER_NOM] = "NOM", ++ [CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO] = "TURBO", ++}; ++ ++/* ++ * IPQ807x NPU fuse parameter locations: ++ * ++ * Structs are organized with the following dimensions: ++ * Outer: 0 to 1 for fuse corners from lowest to highest corner ++ * Inner: large enough to hold the longest set of parameter segments which ++ * fully defines a fuse parameter, +1 (for NULL termination). ++ * Each segment corresponds to a contiguous group of bits from a ++ * single fuse row. These segments are concatentated together in ++ * order to form the full fuse parameter value. The segments for ++ * a given parameter may correspond to different fuse rows. ++ */ ++static struct cpr3_fuse_param ++ipq807x_npu_init_voltage_param[IPQ807x_NPU_FUSE_CORNERS][2] = { ++ {{73, 22, 27}, {} }, ++ {{73, 16, 21}, {} }, ++}; ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ807x ++ */ ++static int ++ipq807x_npu_fuse_ref_volt [IPQ807x_NPU_FUSE_CORNERS] = { ++ 912000, ++ 992000, ++}; ++ ++/* ++ * IPQ9574 (Few parameters are changed, remaining are same as IPQ807x) ++ */ ++#define IPQ9574_NPU_FUSE_CORNERS 2 ++#define IPQ9574_NPU_FUSE_STEP_VOLT 10000 ++#define IPQ9574_NPU_CPR_CLOCK_RATE 24000000 ++ ++/* ++ * fues parameters for IPQ9574 ++ */ ++static struct cpr3_fuse_param ++ipq9574_npu_init_voltage_param[IPQ9574_NPU_FUSE_CORNERS][2] = { ++ {{105, 12, 17}, {} }, ++ {{105, 6, 11}, {} }, ++}; ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ9574 ++ */ ++static int ++ipq9574_npu_fuse_ref_volt [IPQ9574_NPU_FUSE_CORNERS] = { ++ 862500, ++ 987500, ++}; ++ ++struct cpr3_controller *g_ctrl; ++ ++void cpr3_npu_temp_notify(int sensor, int temp, int low_notif) ++{ ++ u32 prev_sensor_state; ++ ++ if (sensor != NPU_TSENS) ++ return; ++ ++ prev_sensor_state = g_ctrl->cur_sensor_state; ++ if (low_notif) ++ g_ctrl->cur_sensor_state |= BIT(sensor); ++ else ++ g_ctrl->cur_sensor_state &= ~BIT(sensor); ++ ++ if (!prev_sensor_state && g_ctrl->cur_sensor_state) ++ cpr3_handle_temp_open_loop_adjustment(g_ctrl, true); ++ else if (prev_sensor_state && !g_ctrl->cur_sensor_state) ++ cpr3_handle_temp_open_loop_adjustment(g_ctrl, false); ++} ++ ++/** ++ * cpr3_ipq807x_npu_read_fuse_data() - load NPU specific fuse parameter values ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function allocates a cpr3_ipq807x_npu_fuses struct, fills it with ++ * values read out of hardware fuses, and finally copies common fuse values ++ * into the CPR3 regulator struct. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_ipq807x_npu_read_fuse_data(struct cpr3_regulator *vreg) ++{ ++ void __iomem *base = vreg->thread->ctrl->fuse_base; ++ struct cpr3_ipq807x_npu_fuses *fuse; ++ int i, rc; ++ ++ fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL); ++ if (!fuse) ++ return -ENOMEM; ++ ++ for (i = 0; i < g_valid_npu_fuse_count; i++) { ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr3_regulator_data->init_voltage_param[i], ++ &fuse->init_voltage[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ } ++ ++ vreg->fuse_corner_count = g_valid_npu_fuse_count; ++ vreg->platform_fuses = fuse; ++ ++ return 0; ++} ++ ++/** ++ * cpr3_npu_parse_corner_data() - parse NPU corner data from device tree ++ * properties of the CPR3 regulator's device node ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_parse_corner_data(struct cpr3_regulator *vreg) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "error reading corner data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_ipq807x_npu_calculate_open_loop_voltages() - calculate the open-loop ++ * voltage for each corner of a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * @temp_correction: Temperature based correction ++ * ++ * If open-loop voltage interpolation is allowed in device tree, then ++ * this function calculates the open-loop voltage for a given corner using ++ * linear interpolation. This interpolation is performed using the processor ++ * frequencies of the lower and higher Fmax corners along with their fused ++ * open-loop voltages. ++ * ++ * If open-loop voltage interpolation is not allowed, then this function uses ++ * the Fmax fused open-loop voltage for all of the corners associated with a ++ * given fuse corner. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_ipq807x_npu_calculate_open_loop_voltages( ++ struct cpr3_regulator *vreg, bool temp_correction) ++{ ++ struct cpr3_ipq807x_npu_fuses *fuse = vreg->platform_fuses; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int i, j, rc = 0; ++ u64 freq_low, volt_low, freq_high, volt_high; ++ int *fuse_volt; ++ int *fmax_corner; ++ ++ fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt), ++ GFP_KERNEL); ++ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), ++ GFP_KERNEL); ++ if (!fuse_volt || !fmax_corner) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (ctrl->cpr_global_setting == CPR_DISABLED) ++ fuse_volt[i] = vreg->cpr3_regulator_data->fuse_ref_volt[i]; ++ else ++ fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse( ++ vreg->cpr3_regulator_data->fuse_ref_volt[i], ++ vreg->cpr3_regulator_data->fuse_step_volt, ++ fuse->init_voltage[i], ++ IPQ807x_NPU_VOLTAGE_FUSE_SIZE); ++ ++ /* Log fused open-loop voltage values for debugging purposes. */ ++ cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", ++ cpr3_ipq807x_npu_fuse_corner_name[i], ++ fuse_volt[i]); ++ } ++ ++ rc = cpr3_determine_part_type(vreg, ++ fuse_volt[CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO]); ++ if (rc) { ++ cpr3_err(vreg, ++ "fused part type detection failed failed, rc=%d\n", rc); ++ goto done; ++ } ++ ++ rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt); ++ if (rc) { ++ cpr3_err(vreg, ++ "fused open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ if (temp_correction) { ++ rc = cpr3_determine_temp_base_open_loop_correction(vreg, ++ fuse_volt); ++ if (rc) { ++ cpr3_err(vreg, ++ "temp open-loop voltage adj. failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ if (fuse_volt[i] < fuse_volt[i - 1]) { ++ cpr3_info(vreg, ++ "fuse corner %d voltage=%d uV < fuse corner %d \ ++ voltage=%d uV; overriding: fuse corner %d \ ++ voltage=%d\n", ++ i, fuse_volt[i], i - 1, fuse_volt[i - 1], ++ i, fuse_volt[i - 1]); ++ fuse_volt[i] = fuse_volt[i - 1]; ++ } ++ } ++ ++ /* Determine highest corner mapped to each fuse corner */ ++ j = vreg->fuse_corner_count - 1; ++ for (i = vreg->corner_count - 1; i >= 0; i--) { ++ if (vreg->corner[i].cpr_fuse_corner == j) { ++ fmax_corner[j] = i; ++ j--; ++ } ++ } ++ ++ if (j >= 0) { ++ cpr3_err(vreg, "invalid fuse corner mapping\n"); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ /* ++ * Interpolation is not possible for corners mapped to the lowest fuse ++ * corner so use the fuse corner value directly. ++ */ ++ for (i = 0; i <= fmax_corner[0]; i++) ++ vreg->corner[i].open_loop_volt = fuse_volt[0]; ++ ++ /* Interpolate voltages for the higher fuse corners. */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; ++ volt_low = fuse_volt[i - 1]; ++ freq_high = vreg->corner[fmax_corner[i]].proc_freq; ++ volt_high = fuse_volt[i]; ++ ++ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) ++ vreg->corner[j].open_loop_volt = cpr3_interpolate( ++ freq_low, volt_low, freq_high, volt_high, ++ vreg->corner[j].proc_freq); ++ } ++ ++done: ++ if (rc == 0) { ++ cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n"); ++ for (i = 0; i < vreg->corner_count; i++) ++ cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i, ++ vreg->corner[i].open_loop_volt); ++ ++ rc = cpr3_adjust_open_loop_voltages(vreg); ++ if (rc) ++ cpr3_err(vreg, ++ "open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ } ++ ++ kfree(fuse_volt); ++ kfree(fmax_corner); ++ return rc; ++} ++ ++/** ++ * cpr3_npu_print_settings() - print out NPU CPR configuration settings into ++ * the kernel log for debugging purposes ++ * @vreg: Pointer to the CPR3 regulator ++ */ ++static void cpr3_npu_print_settings(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_corner *corner; ++ int i; ++ ++ cpr3_debug(vreg, ++ "Corner: Frequency (Hz), Fuse Corner, Floor (uV), \ ++ Open-Loop (uV), Ceiling (uV)\n"); ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n", ++ i, corner->proc_freq, corner->cpr_fuse_corner, ++ corner->floor_volt, corner->open_loop_volt, ++ corner->ceiling_volt); ++ } ++ ++ if (vreg->thread->ctrl->apm) ++ cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n", ++ vreg->thread->ctrl->apm_threshold_volt, ++ vreg->thread->ctrl->apm_adj_volt); ++} ++ ++/** ++ * cpr3_ipq807x_npu_calc_temp_based_ol_voltages() - Calculate the open loop ++ * voltages based on temperature based correction margins ++ * @vreg: Pointer to the CPR3 regulator ++ */ ++ ++static int ++cpr3_ipq807x_npu_calc_temp_based_ol_voltages(struct cpr3_regulator *vreg, ++ bool temp_correction) ++{ ++ int rc, i; ++ ++ rc = cpr3_ipq807x_npu_calculate_open_loop_voltages(vreg, ++ temp_correction); ++ if (rc) { ++ cpr3_err(vreg, ++ "unable to calculate open-loop voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = cpr3_limit_open_loop_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ cpr3_open_loop_voltage_as_ceiling(vreg); ++ ++ rc = cpr3_limit_floor_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (temp_correction) ++ vreg->corner[i].cold_temp_open_loop_volt = ++ vreg->corner[i].open_loop_volt; ++ else ++ vreg->corner[i].normal_temp_open_loop_volt = ++ vreg->corner[i].open_loop_volt; ++ } ++ ++ cpr3_npu_print_settings(vreg); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_npu_init_thread() - perform steps necessary to initialize the ++ * configuration data for a CPR3 thread ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_init_thread(struct cpr3_thread *thread) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_thread_data(thread); ++ if (rc) { ++ cpr3_err(thread->ctrl, ++ "thread %u CPR thread data from DT- failed, rc=%d\n", ++ thread->thread_id, rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_npu_init_regulator() - perform all steps necessary to initialize the ++ * configuration data for a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_init_regulator(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_ipq807x_npu_fuses *fuse; ++ int rc, cold_temp = 0; ++ bool can_adj_cold_temp = cpr3_can_adjust_cold_temp(vreg); ++ ++ rc = cpr3_ipq807x_npu_read_fuse_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ fuse = vreg->platform_fuses; ++ ++ rc = cpr3_npu_parse_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, ++ "Cannot read CPR corner data from DT, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = cpr3_mem_acc_init(vreg); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(vreg, ++ "Cannot initialize mem-acc regulator settings, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (can_adj_cold_temp) { ++ rc = cpr3_ipq807x_npu_calc_temp_based_ol_voltages(vreg, true); ++ if (rc) { ++ cpr3_err(vreg, ++ "unable to calculate open-loop voltages, rc=%d\n", rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_ipq807x_npu_calc_temp_based_ol_voltages(vreg, false); ++ if (rc) { ++ cpr3_err(vreg, ++ "unable to calculate open-loop voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (can_adj_cold_temp) { ++ cpr3_info(vreg, ++ "Normal and Cold condition init done. Default to normal.\n"); ++ ++ rc = cpr3_get_cold_temp_threshold(vreg, &cold_temp); ++ if (rc) { ++ cpr3_err(vreg, ++ "Get cold temperature threshold failed, rc=%d\n", rc); ++ return rc; ++ } ++ register_low_temp_notif(NPU_TSENS, cold_temp, ++ cpr3_npu_temp_notify); ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_npu_init_controller() - perform NPU CPR3 controller specific ++ * initializations ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_npu_init_controller(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = cpr3_parse_open_loop_common_ctrl_data(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ ctrl->ctrl_type = CPR_CTRL_TYPE_CPR3; ++ ctrl->supports_hw_closed_loop = false; ++ ++ return 0; ++} ++ ++static const struct cpr3_reg_data ipq807x_cpr_npu = { ++ .cpr_valid_fuse_count = IPQ807x_NPU_FUSE_CORNERS, ++ .init_voltage_param = ipq807x_npu_init_voltage_param, ++ .fuse_ref_volt = ipq807x_npu_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_NPU_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_NPU_CPR_CLOCK_RATE, ++}; ++ ++static const struct cpr3_reg_data ipq817x_cpr_npu = { ++ .cpr_valid_fuse_count = IPQ817x_NPU_FUSE_CORNERS, ++ .init_voltage_param = ipq807x_npu_init_voltage_param, ++ .fuse_ref_volt = ipq807x_npu_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_NPU_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_NPU_CPR_CLOCK_RATE, ++}; ++ ++static const struct cpr3_reg_data ipq9574_cpr_npu = { ++ .cpr_valid_fuse_count = IPQ9574_NPU_FUSE_CORNERS, ++ .init_voltage_param = ipq9574_npu_init_voltage_param, ++ .fuse_ref_volt = ipq9574_npu_fuse_ref_volt, ++ .fuse_step_volt = IPQ9574_NPU_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ9574_NPU_CPR_CLOCK_RATE, ++}; ++ ++static struct of_device_id cpr3_regulator_match_table[] = { ++ { ++ .compatible = "qcom,cpr3-ipq807x-npu-regulator", ++ .data = &ipq807x_cpr_npu ++ }, ++ { ++ .compatible = "qcom,cpr3-ipq817x-npu-regulator", ++ .data = &ipq817x_cpr_npu ++ }, ++ { ++ .compatible = "qcom,cpr3-ipq9574-npu-regulator", ++ .data = &ipq9574_cpr_npu ++ }, ++ {} ++}; ++ ++static int cpr3_npu_regulator_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct cpr3_controller *ctrl; ++ int i, rc; ++ const struct of_device_id *match; ++ struct cpr3_reg_data *cpr_data; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "Device tree node is missing\n"); ++ return -EINVAL; ++ } ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ g_ctrl = ctrl; ++ ++ match = of_match_device(cpr3_regulator_match_table, &pdev->dev); ++ if (!match) ++ return -ENODEV; ++ ++ cpr_data = (struct cpr3_reg_data *)match->data; ++ g_valid_npu_fuse_count = cpr_data->cpr_valid_fuse_count; ++ dev_info(dev, "NPU CPR valid fuse count: %d\n", g_valid_npu_fuse_count); ++ ctrl->cpr_clock_rate = cpr_data->cpr_clk_rate; ++ ++ ctrl->dev = dev; ++ /* Set to false later if anything precludes CPR operation. */ ++ ctrl->cpr_allowed_hw = true; ++ ++ rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name", ++ &ctrl->name); ++ if (rc) { ++ cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_map_fuse_base(ctrl, pdev); ++ if (rc) { ++ cpr3_err(ctrl, "could not map fuse base address\n"); ++ return rc; ++ } ++ ++ rc = cpr3_read_tcsr_setting(ctrl, pdev, IPQ807x_NPU_CPR_TCSR_START, ++ IPQ807x_NPU_CPR_TCSR_END); ++ if (rc) { ++ cpr3_err(ctrl, "could not read CPR tcsr rsetting\n"); ++ return rc; ++ } ++ ++ rc = cpr3_allocate_threads(ctrl, 0, 0); ++ if (rc) { ++ cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->thread_count != 1) { ++ cpr3_err(ctrl, "expected 1 thread but found %d\n", ++ ctrl->thread_count); ++ return -EINVAL; ++ } ++ ++ rc = cpr3_npu_init_controller(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_npu_init_thread(&ctrl->thread[0]); ++ if (rc) { ++ cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread[0].vreg_count; i++) { ++ ctrl->thread[0].vreg[i].cpr3_regulator_data = cpr_data; ++ rc = cpr3_npu_init_regulator(&ctrl->thread[0].vreg[i]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[0].vreg[i], "regulator initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ platform_set_drvdata(pdev, ctrl); ++ ++ return cpr3_open_loop_regulator_register(pdev, ctrl); ++} ++ ++static int cpr3_npu_regulator_remove(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_open_loop_regulator_unregister(ctrl); ++} ++ ++static struct platform_driver cpr3_npu_regulator_driver = { ++ .driver = { ++ .name = "qcom,cpr3-npu-regulator", ++ .of_match_table = cpr3_regulator_match_table, ++ .owner = THIS_MODULE, ++ }, ++ .probe = cpr3_npu_regulator_probe, ++ .remove = cpr3_npu_regulator_remove, ++}; ++ ++static int cpr3_regulator_init(void) ++{ ++ return platform_driver_register(&cpr3_npu_regulator_driver); ++} ++arch_initcall(cpr3_regulator_init); ++ ++static void cpr3_regulator_exit(void) ++{ ++ platform_driver_unregister(&cpr3_npu_regulator_driver); ++} ++module_exit(cpr3_regulator_exit); ++ ++MODULE_DESCRIPTION("QCOM CPR3 NPU regulator driver"); ++MODULE_LICENSE("Dual BSD/GPLv2"); ++MODULE_ALIAS("platform:npu-ipq807x"); +diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c +new file mode 100644 +index 0000000000000..d5d8a1a0b0619 +--- /dev/null ++++ b/drivers/regulator/cpr3-regulator.c +@@ -0,0 +1,5112 @@ ++/* ++ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpr3-regulator.h" ++ ++#define CPR3_REGULATOR_CORNER_INVALID (-1) ++#define CPR3_RO_MASK GENMASK(CPR3_RO_COUNT - 1, 0) ++ ++/* CPR3 registers */ ++#define CPR3_REG_CPR_CTL 0x4 ++#define CPR3_CPR_CTL_LOOP_EN_MASK BIT(0) ++#define CPR3_CPR_CTL_LOOP_ENABLE BIT(0) ++#define CPR3_CPR_CTL_LOOP_DISABLE 0 ++#define CPR3_CPR_CTL_IDLE_CLOCKS_MASK GENMASK(5, 1) ++#define CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT 1 ++#define CPR3_CPR_CTL_COUNT_MODE_MASK GENMASK(7, 6) ++#define CPR3_CPR_CTL_COUNT_MODE_SHIFT 6 ++#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN 0 ++#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MAX 1 ++#define CPR3_CPR_CTL_COUNT_MODE_STAGGERED 2 ++#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE 3 ++#define CPR3_CPR_CTL_COUNT_REPEAT_MASK GENMASK(31, 9) ++#define CPR3_CPR_CTL_COUNT_REPEAT_SHIFT 9 ++ ++#define CPR3_REG_CPR_STATUS 0x8 ++#define CPR3_CPR_STATUS_BUSY_MASK BIT(0) ++#define CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK BIT(1) ++ ++/* ++ * This register is not present on controllers that support HW closed-loop ++ * except CPR4 APSS controller. ++ */ ++#define CPR3_REG_CPR_TIMER_AUTO_CONT 0xC ++ ++#define CPR3_REG_CPR_STEP_QUOT 0x14 ++#define CPR3_CPR_STEP_QUOT_MIN_MASK GENMASK(5, 0) ++#define CPR3_CPR_STEP_QUOT_MIN_SHIFT 0 ++#define CPR3_CPR_STEP_QUOT_MAX_MASK GENMASK(11, 6) ++#define CPR3_CPR_STEP_QUOT_MAX_SHIFT 6 ++ ++#define CPR3_REG_GCNT(ro) (0xA0 + 0x4 * (ro)) ++ ++#define CPR3_REG_SENSOR_BYPASS_WRITE(sensor) (0xE0 + 0x4 * ((sensor) / 32)) ++#define CPR3_REG_SENSOR_BYPASS_WRITE_BANK(bank) (0xE0 + 0x4 * (bank)) ++ ++#define CPR3_REG_SENSOR_MASK_WRITE(sensor) (0x120 + 0x4 * ((sensor) / 32)) ++#define CPR3_REG_SENSOR_MASK_WRITE_BANK(bank) (0x120 + 0x4 * (bank)) ++#define CPR3_REG_SENSOR_MASK_READ(sensor) (0x140 + 0x4 * ((sensor) / 32)) ++ ++#define CPR3_REG_SENSOR_OWNER(sensor) (0x200 + 0x4 * (sensor)) ++ ++#define CPR3_REG_CONT_CMD 0x800 ++#define CPR3_CONT_CMD_ACK 0x1 ++#define CPR3_CONT_CMD_NACK 0x0 ++ ++#define CPR3_REG_THRESH(thread) (0x808 + 0x440 * (thread)) ++#define CPR3_THRESH_CONS_DOWN_MASK GENMASK(3, 0) ++#define CPR3_THRESH_CONS_DOWN_SHIFT 0 ++#define CPR3_THRESH_CONS_UP_MASK GENMASK(7, 4) ++#define CPR3_THRESH_CONS_UP_SHIFT 4 ++#define CPR3_THRESH_DOWN_THRESH_MASK GENMASK(12, 8) ++#define CPR3_THRESH_DOWN_THRESH_SHIFT 8 ++#define CPR3_THRESH_UP_THRESH_MASK GENMASK(17, 13) ++#define CPR3_THRESH_UP_THRESH_SHIFT 13 ++ ++#define CPR3_REG_RO_MASK(thread) (0x80C + 0x440 * (thread)) ++ ++#define CPR3_REG_RESULT0(thread) (0x810 + 0x440 * (thread)) ++#define CPR3_RESULT0_BUSY_MASK BIT(0) ++#define CPR3_RESULT0_STEP_DN_MASK BIT(1) ++#define CPR3_RESULT0_STEP_UP_MASK BIT(2) ++#define CPR3_RESULT0_ERROR_STEPS_MASK GENMASK(7, 3) ++#define CPR3_RESULT0_ERROR_STEPS_SHIFT 3 ++#define CPR3_RESULT0_ERROR_MASK GENMASK(19, 8) ++#define CPR3_RESULT0_ERROR_SHIFT 8 ++#define CPR3_RESULT0_NEGATIVE_MASK BIT(20) ++ ++#define CPR3_REG_RESULT1(thread) (0x814 + 0x440 * (thread)) ++#define CPR3_RESULT1_QUOT_MIN_MASK GENMASK(11, 0) ++#define CPR3_RESULT1_QUOT_MIN_SHIFT 0 ++#define CPR3_RESULT1_QUOT_MAX_MASK GENMASK(23, 12) ++#define CPR3_RESULT1_QUOT_MAX_SHIFT 12 ++#define CPR3_RESULT1_RO_MIN_MASK GENMASK(27, 24) ++#define CPR3_RESULT1_RO_MIN_SHIFT 24 ++#define CPR3_RESULT1_RO_MAX_MASK GENMASK(31, 28) ++#define CPR3_RESULT1_RO_MAX_SHIFT 28 ++ ++#define CPR3_REG_RESULT2(thread) (0x818 + 0x440 * (thread)) ++#define CPR3_RESULT2_STEP_QUOT_MIN_MASK GENMASK(5, 0) ++#define CPR3_RESULT2_STEP_QUOT_MIN_SHIFT 0 ++#define CPR3_RESULT2_STEP_QUOT_MAX_MASK GENMASK(11, 6) ++#define CPR3_RESULT2_STEP_QUOT_MAX_SHIFT 6 ++#define CPR3_RESULT2_SENSOR_MIN_MASK GENMASK(23, 16) ++#define CPR3_RESULT2_SENSOR_MIN_SHIFT 16 ++#define CPR3_RESULT2_SENSOR_MAX_MASK GENMASK(31, 24) ++#define CPR3_RESULT2_SENSOR_MAX_SHIFT 24 ++ ++#define CPR3_REG_IRQ_EN 0x81C ++#define CPR3_REG_IRQ_CLEAR 0x820 ++#define CPR3_REG_IRQ_STATUS 0x824 ++#define CPR3_IRQ_UP BIT(3) ++#define CPR3_IRQ_MID BIT(2) ++#define CPR3_IRQ_DOWN BIT(1) ++ ++#define CPR3_REG_TARGET_QUOT(thread, ro) \ ++ (0x840 + 0x440 * (thread) + 0x4 * (ro)) ++ ++/* Registers found only on controllers that support HW closed-loop. */ ++#define CPR3_REG_PD_THROTTLE 0xE8 ++#define CPR3_PD_THROTTLE_DISABLE 0x0 ++ ++#define CPR3_REG_HW_CLOSED_LOOP 0x3000 ++#define CPR3_HW_CLOSED_LOOP_ENABLE 0x0 ++#define CPR3_HW_CLOSED_LOOP_DISABLE 0x1 ++ ++#define CPR3_REG_CPR_TIMER_MID_CONT 0x3004 ++#define CPR3_REG_CPR_TIMER_UP_DN_CONT 0x3008 ++ ++#define CPR3_REG_LAST_MEASUREMENT 0x7F8 ++#define CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT 0 ++#define CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT 4 ++#define CPR3_LAST_MEASUREMENT_THREAD_DN(thread) \ ++ (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT) ++#define CPR3_LAST_MEASUREMENT_THREAD_UP(thread) \ ++ (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT) ++#define CPR3_LAST_MEASUREMENT_AGGR_DN BIT(8) ++#define CPR3_LAST_MEASUREMENT_AGGR_MID BIT(9) ++#define CPR3_LAST_MEASUREMENT_AGGR_UP BIT(10) ++#define CPR3_LAST_MEASUREMENT_VALID BIT(11) ++#define CPR3_LAST_MEASUREMENT_SAW_ERROR BIT(12) ++#define CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK GENMASK(23, 16) ++#define CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT 16 ++ ++/* CPR4 controller specific registers and bit definitions */ ++#define CPR4_REG_CPR_TIMER_CLAMP 0x10 ++#define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN BIT(27) ++ ++#define CPR4_REG_MISC 0x700 ++#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK GENMASK(23, 20) ++#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT 20 ++#define CPR4_MISC_TEMP_SENSOR_ID_START_MASK GENMASK(27, 24) ++#define CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT 24 ++#define CPR4_MISC_TEMP_SENSOR_ID_END_MASK GENMASK(31, 28) ++#define CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT 28 ++ ++#define CPR4_REG_SAW_ERROR_STEP_LIMIT 0x7A4 ++#define CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK GENMASK(4, 0) ++#define CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT 0 ++#define CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK GENMASK(9, 5) ++#define CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT 5 ++ ++#define CPR4_REG_MARGIN_TEMP_CORE_TIMERS 0x7A8 ++#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK GENMASK(28, 18) ++#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT 18 ++ ++#define CPR4_REG_MARGIN_TEMP_CORE(core) (0x7AC + 0x4 * (core)) ++#define CPR4_MARGIN_TEMP_CORE_ADJ_MASK GENMASK(7, 0) ++#define CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT 8 ++ ++#define CPR4_REG_MARGIN_TEMP_POINT0N1 0x7F0 ++#define CPR4_MARGIN_TEMP_POINT0_MASK GENMASK(11, 0) ++#define CPR4_MARGIN_TEMP_POINT0_SHIFT 0 ++#define CPR4_MARGIN_TEMP_POINT1_MASK GENMASK(23, 12) ++#define CPR4_MARGIN_TEMP_POINT1_SHIFT 12 ++#define CPR4_REG_MARGIN_TEMP_POINT2 0x7F4 ++#define CPR4_MARGIN_TEMP_POINT2_MASK GENMASK(11, 0) ++#define CPR4_MARGIN_TEMP_POINT2_SHIFT 0 ++ ++#define CPR4_REG_MARGIN_ADJ_CTL 0x7F8 ++#define CPR4_MARGIN_ADJ_CTL_BOOST_EN BIT(0) ++#define CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN BIT(1) ++#define CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN BIT(2) ++#define CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN BIT(3) ++#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK BIT(4) ++#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE BIT(4) ++#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE 0 ++#define CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN BIT(7) ++#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN BIT(8) ++#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK GENMASK(16, 12) ++#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT 12 ++#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK GENMASK(21, 19) ++#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT 19 ++#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK GENMASK(25, 22) ++#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT 22 ++#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK GENMASK(31, 26) ++#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT 26 ++ ++#define CPR4_REG_CPR_MASK_THREAD(thread) (0x80C + 0x440 * (thread)) ++#define CPR4_CPR_MASK_THREAD_DISABLE_THREAD BIT(31) ++#define CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK GENMASK(15, 0) ++ ++/* ++ * The amount of time to wait for the CPR controller to become idle when ++ * performing an aging measurement. ++ */ ++#define CPR3_AGING_MEASUREMENT_TIMEOUT_NS 5000000 ++ ++/* ++ * The number of individual aging measurements to perform which are then ++ * averaged together in order to determine the final aging adjustment value. ++ */ ++#define CPR3_AGING_MEASUREMENT_ITERATIONS 16 ++ ++/* ++ * Aging measurements for the aged and unaged ring oscillators take place a few ++ * microseconds apart. If the vdd-supply voltage fluctuates between the two ++ * measurements, then the difference between them will be incorrect. The ++ * difference could end up too high or too low. This constant defines the ++ * number of lowest and highest measurements to ignore when averaging. ++ */ ++#define CPR3_AGING_MEASUREMENT_FILTER 3 ++ ++/* ++ * The number of times to attempt the full aging measurement sequence before ++ * declaring a measurement failure. ++ */ ++#define CPR3_AGING_RETRY_COUNT 5 ++ ++/* ++ * The maximum time to wait in microseconds for a CPR register write to ++ * complete. ++ */ ++#define CPR3_REGISTER_WRITE_DELAY_US 200 ++ ++static DEFINE_MUTEX(cpr3_controller_list_mutex); ++static LIST_HEAD(cpr3_controller_list); ++static struct dentry *cpr3_debugfs_base; ++ ++/** ++ * cpr3_read() - read four bytes from the memory address specified ++ * @ctrl: Pointer to the CPR3 controller ++ * @offset: Offset in bytes from the CPR3 controller's base address ++ * ++ * Return: memory address value ++ */ ++static inline u32 cpr3_read(struct cpr3_controller *ctrl, u32 offset) ++{ ++ if (!ctrl->cpr_enabled) { ++ cpr3_err(ctrl, "CPR register reads are not possible when CPR clocks are disabled\n"); ++ return 0; ++ } ++ ++ return readl_relaxed(ctrl->cpr_ctrl_base + offset); ++} ++ ++/** ++ * cpr3_write() - write four bytes to the memory address specified ++ * @ctrl: Pointer to the CPR3 controller ++ * @offset: Offset in bytes from the CPR3 controller's base address ++ * @value: Value to write to the memory address ++ * ++ * Return: none ++ */ ++static inline void cpr3_write(struct cpr3_controller *ctrl, u32 offset, ++ u32 value) ++{ ++ if (!ctrl->cpr_enabled) { ++ cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n"); ++ return; ++ } ++ ++ writel_relaxed(value, ctrl->cpr_ctrl_base + offset); ++} ++ ++/** ++ * cpr3_masked_write() - perform a read-modify-write sequence so that only ++ * masked bits are modified ++ * @ctrl: Pointer to the CPR3 controller ++ * @offset: Offset in bytes from the CPR3 controller's base address ++ * @mask: Mask identifying the bits that should be modified ++ * @value: Value to write to the memory address ++ * ++ * Return: none ++ */ ++static inline void cpr3_masked_write(struct cpr3_controller *ctrl, u32 offset, ++ u32 mask, u32 value) ++{ ++ u32 reg_val, orig_val; ++ ++ if (!ctrl->cpr_enabled) { ++ cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n"); ++ return; ++ } ++ ++ reg_val = orig_val = readl_relaxed(ctrl->cpr_ctrl_base + offset); ++ reg_val &= ~mask; ++ reg_val |= value & mask; ++ ++ if (reg_val != orig_val) ++ writel_relaxed(reg_val, ctrl->cpr_ctrl_base + offset); ++} ++ ++/** ++ * cpr3_ctrl_loop_enable() - enable the CPR sensing loop for a given controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static inline void cpr3_ctrl_loop_enable(struct cpr3_controller *ctrl) ++{ ++ if (ctrl->cpr_enabled && !(ctrl->aggr_corner.sdelta ++ && ctrl->aggr_corner.sdelta->allow_boost)) ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_ENABLE); ++} ++ ++/** ++ * cpr3_ctrl_loop_disable() - disable the CPR sensing loop for a given ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static inline void cpr3_ctrl_loop_disable(struct cpr3_controller *ctrl) ++{ ++ if (ctrl->cpr_enabled) ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_DISABLE); ++} ++ ++/** ++ * cpr3_clock_enable() - prepare and enable all clocks used by this CPR3 ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_clock_enable(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = clk_prepare_enable(ctrl->bus_clk); ++ if (rc) { ++ cpr3_err(ctrl, "failed to enable bus clock, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = clk_prepare_enable(ctrl->iface_clk); ++ if (rc) { ++ cpr3_err(ctrl, "failed to enable interface clock, rc=%d\n", rc); ++ clk_disable_unprepare(ctrl->bus_clk); ++ return rc; ++ } ++ ++ rc = clk_prepare_enable(ctrl->core_clk); ++ if (rc) { ++ cpr3_err(ctrl, "failed to enable core clock, rc=%d\n", rc); ++ clk_disable_unprepare(ctrl->iface_clk); ++ clk_disable_unprepare(ctrl->bus_clk); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_clock_disable() - disable and unprepare all clocks used by this CPR3 ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static void cpr3_clock_disable(struct cpr3_controller *ctrl) ++{ ++ clk_disable_unprepare(ctrl->core_clk); ++ clk_disable_unprepare(ctrl->iface_clk); ++ clk_disable_unprepare(ctrl->bus_clk); ++} ++ ++/** ++ * cpr3_ctrl_clear_cpr4_config() - clear the CPR4 register configuration ++ * programmed for current aggregated corner of a given controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static inline int cpr3_ctrl_clear_cpr4_config(struct cpr3_controller *ctrl) ++{ ++ struct cpr4_sdelta *aggr_sdelta = ctrl->aggr_corner.sdelta; ++ bool cpr_enabled = ctrl->cpr_enabled; ++ int i, rc = 0; ++ ++ if (!aggr_sdelta || !(aggr_sdelta->allow_core_count_adj ++ || aggr_sdelta->allow_temp_adj || aggr_sdelta->allow_boost)) ++ /* cpr4 features are not enabled */ ++ return 0; ++ ++ /* Ensure that CPR clocks are enabled before writing to registers. */ ++ if (!cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ return rc; ++ } ++ ctrl->cpr_enabled = true; ++ } ++ ++ /* ++ * Clear feature enable configuration made for current ++ * aggregated corner. ++ */ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK ++ | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_BOOST_EN ++ | CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, 0); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK, ++ 0 << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT); ++ ++ for (i = 0; i <= aggr_sdelta->max_core_count; i++) { ++ /* Clear voltage margin adjustments programmed in TEMP_COREi */ ++ cpr3_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE(i), 0); ++ } ++ ++ /* Turn off CPR clocks if they were off before this function call. */ ++ if (!cpr_enabled) { ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_closed_loop_enable() - enable logical CPR closed-loop operation ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_closed_loop_enable(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ if (!ctrl->cpr_allowed_hw || !ctrl->cpr_allowed_sw) { ++ cpr3_err(ctrl, "cannot enable closed-loop CPR operation because it is disallowed\n"); ++ return -EPERM; ++ } else if (ctrl->cpr_enabled) { ++ /* Already enabled */ ++ return 0; ++ } else if (ctrl->cpr_suspended) { ++ /* ++ * CPR must remain disabled as the system is entering suspend. ++ */ ++ return 0; ++ } ++ ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "unable to enable CPR clocks, rc=%d\n", rc); ++ return rc; ++ } ++ ++ ctrl->cpr_enabled = true; ++ cpr3_debug(ctrl, "CPR closed-loop operation enabled\n"); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_closed_loop_disable() - disable logical CPR closed-loop operation ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static inline int cpr3_closed_loop_disable(struct cpr3_controller *ctrl) ++{ ++ if (!ctrl->cpr_enabled) { ++ /* Already disabled */ ++ return 0; ++ } ++ ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ cpr3_debug(ctrl, "CPR closed-loop operation disabled\n"); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_get_gcnt() - returns the GCNT register value corresponding ++ * to the clock rate and sensor time of the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: GCNT value ++ */ ++static u32 cpr3_regulator_get_gcnt(struct cpr3_controller *ctrl) ++{ ++ u64 temp; ++ unsigned int remainder; ++ u32 gcnt; ++ ++ temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->sensor_time; ++ remainder = do_div(temp, 1000000000); ++ if (remainder) ++ temp++; ++ /* ++ * GCNT == 0 corresponds to a single ref clock measurement interval so ++ * offset GCNT values by 1. ++ */ ++ gcnt = temp - 1; ++ ++ return gcnt; ++} ++ ++/** ++ * cpr3_regulator_init_thread() - performs hardware initialization of CPR ++ * thread registers ++ * @thread: Pointer to the CPR3 thread ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_thread(struct cpr3_thread *thread) ++{ ++ u32 reg; ++ ++ reg = (thread->consecutive_up << CPR3_THRESH_CONS_UP_SHIFT) ++ & CPR3_THRESH_CONS_UP_MASK; ++ reg |= (thread->consecutive_down << CPR3_THRESH_CONS_DOWN_SHIFT) ++ & CPR3_THRESH_CONS_DOWN_MASK; ++ reg |= (thread->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT) ++ & CPR3_THRESH_UP_THRESH_MASK; ++ reg |= (thread->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT) ++ & CPR3_THRESH_DOWN_THRESH_MASK; ++ ++ cpr3_write(thread->ctrl, CPR3_REG_THRESH(thread->thread_id), reg); ++ ++ /* ++ * Mask all RO's initially so that unused thread doesn't contribute ++ * to closed-loop voltage. ++ */ ++ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), ++ CPR3_RO_MASK); ++ ++ return 0; ++} ++ ++/** ++ * cpr4_regulator_init_temp_points() - performs hardware initialization of CPR4 ++ * registers to track tsen temperature data and also specify the ++ * temperature band range values to apply different voltage margins ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_regulator_init_temp_points(struct cpr3_controller *ctrl) ++{ ++ if (!ctrl->allow_temp_adj) ++ return 0; ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_TEMP_SENSOR_ID_START_MASK, ++ ctrl->temp_sensor_id_start ++ << CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_TEMP_SENSOR_ID_END_MASK, ++ ctrl->temp_sensor_id_end ++ << CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT2, ++ CPR4_MARGIN_TEMP_POINT2_MASK, ++ (ctrl->temp_band_count == 4 ? ctrl->temp_points[2] : 0x7FF) ++ << CPR4_MARGIN_TEMP_POINT2_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1, ++ CPR4_MARGIN_TEMP_POINT1_MASK, ++ (ctrl->temp_band_count >= 3 ? ctrl->temp_points[1] : 0x7FF) ++ << CPR4_MARGIN_TEMP_POINT1_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1, ++ CPR4_MARGIN_TEMP_POINT0_MASK, ++ (ctrl->temp_band_count >= 2 ? ctrl->temp_points[0] : 0x7FF) ++ << CPR4_MARGIN_TEMP_POINT0_SHIFT); ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_init_cpr4() - performs hardware initialization at the ++ * controller and thread level required for CPR4 operation. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * This function allocates sdelta structures and sdelta tables for aggregated ++ * corners of the controller and its threads. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_thread *thread; ++ struct cpr3_regulator *vreg; ++ struct cpr4_sdelta *sdelta; ++ int i, j, ctrl_max_core_count, thread_max_core_count, rc = 0; ++ bool ctrl_valid_sdelta, thread_valid_sdelta; ++ u32 pmic_step_size = 1; ++ int thread_id = 0; ++ u64 temp; ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->saw_use_unit_mV) ++ pmic_step_size = ctrl->step_volt / 1000; ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK, ++ (pmic_step_size ++ << CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT)); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT, ++ CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK, ++ (ctrl->down_error_step_limit ++ << CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT)); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT, ++ CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK, ++ (ctrl->up_error_step_limit ++ << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT)); ++ ++ /* ++ * Enable thread aggregation regardless of which threads are ++ * enabled or disabled. ++ */ ++ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, ++ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, ++ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN); ++ ++ switch (ctrl->thread_count) { ++ case 0: ++ /* Disable both threads */ ++ cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(0), ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(1), ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); ++ break; ++ case 1: ++ /* Disable unused thread */ ++ thread_id = ctrl->thread[0].thread_id ? 0 : 1; ++ cpr3_masked_write(ctrl, ++ CPR4_REG_CPR_MASK_THREAD(thread_id), ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, ++ CPR4_CPR_MASK_THREAD_DISABLE_THREAD ++ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); ++ break; ++ } ++ } ++ ++ if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj ++ && !ctrl->allow_boost) { ++ /* ++ * Skip below configuration as none of the features ++ * are enabled. ++ */ ++ return rc; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN, ++ CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK, ++ ctrl->step_quot_fixed ++ << CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN, ++ (ctrl->use_dynamic_step_quot ++ ? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0)); ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK, ++ ctrl->initial_temp_band ++ << CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT); ++ ++ rc = cpr4_regulator_init_temp_points(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "initialize temp points failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (ctrl->voltage_settling_time) { ++ /* ++ * Configure the settling timer used to account for ++ * one VDD supply step. ++ */ ++ temp = (u64)ctrl->cpr_clock_rate ++ * (u64)ctrl->voltage_settling_time; ++ do_div(temp, 1000000000); ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE_TIMERS, ++ CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK, ++ temp ++ << CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT); ++ } ++ ++ /* ++ * Allocate memory for cpr4_sdelta structure and sdelta table for ++ * controller aggregated corner by finding the maximum core count ++ * used by any cpr3 regulators. ++ */ ++ ctrl_max_core_count = 1; ++ ctrl_valid_sdelta = false; ++ for (i = 0; i < ctrl->thread_count; i++) { ++ thread = &ctrl->thread[i]; ++ ++ /* ++ * Allocate memory for cpr4_sdelta structure and sdelta table ++ * for thread aggregated corner by finding the maximum core ++ * count used by any cpr3 regulators of the thread. ++ */ ++ thread_max_core_count = 1; ++ thread_valid_sdelta = false; ++ for (j = 0; j < thread->vreg_count; j++) { ++ vreg = &thread->vreg[j]; ++ thread_max_core_count = max(thread_max_core_count, ++ vreg->max_core_count); ++ thread_valid_sdelta |= (vreg->allow_core_count_adj ++ | vreg->allow_temp_adj ++ | vreg->allow_boost); ++ } ++ if (thread_valid_sdelta) { ++ sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), ++ GFP_KERNEL); ++ if (!sdelta) ++ return -ENOMEM; ++ ++ sdelta->table = devm_kcalloc(ctrl->dev, ++ thread_max_core_count ++ * ctrl->temp_band_count, ++ sizeof(*sdelta->table), ++ GFP_KERNEL); ++ if (!sdelta->table) ++ return -ENOMEM; ++ ++ sdelta->boost_table = devm_kcalloc(ctrl->dev, ++ ctrl->temp_band_count, ++ sizeof(*sdelta->boost_table), ++ GFP_KERNEL); ++ if (!sdelta->boost_table) ++ return -ENOMEM; ++ ++ thread->aggr_corner.sdelta = sdelta; ++ } ++ ++ ctrl_valid_sdelta |= thread_valid_sdelta; ++ ctrl_max_core_count = max(ctrl_max_core_count, ++ thread_max_core_count); ++ } ++ ++ if (ctrl_valid_sdelta) { ++ sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), GFP_KERNEL); ++ if (!sdelta) ++ return -ENOMEM; ++ ++ sdelta->table = devm_kcalloc(ctrl->dev, ctrl_max_core_count ++ * ctrl->temp_band_count, ++ sizeof(*sdelta->table), GFP_KERNEL); ++ if (!sdelta->table) ++ return -ENOMEM; ++ ++ sdelta->boost_table = devm_kcalloc(ctrl->dev, ++ ctrl->temp_band_count, ++ sizeof(*sdelta->boost_table), ++ GFP_KERNEL); ++ if (!sdelta->boost_table) ++ return -ENOMEM; ++ ++ ctrl->aggr_corner.sdelta = sdelta; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_write_temp_core_margin() - programs hardware SDELTA registers with ++ * the voltage margin adjustments that need to be applied for ++ * different online core-count and temperature bands. ++ * @ctrl: Pointer to the CPR3 controller ++ * @addr: SDELTA register address ++ * @temp_core_adj: Array of voltage margin values for different temperature ++ * bands. ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: none ++ */ ++static void cpr3_write_temp_core_margin(struct cpr3_controller *ctrl, ++ int addr, int *temp_core_adj) ++{ ++ int i, margin_steps; ++ u32 reg = 0; ++ ++ for (i = 0; i < ctrl->temp_band_count; i++) { ++ margin_steps = max(min(temp_core_adj[i], 127), -128); ++ reg |= (margin_steps & CPR4_MARGIN_TEMP_CORE_ADJ_MASK) << ++ (i * CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT); ++ } ++ ++ cpr3_write(ctrl, addr, reg); ++ cpr3_debug(ctrl, "sdelta offset=0x%08x, val=0x%08x\n", addr, reg); ++} ++ ++/** ++ * cpr3_controller_program_sdelta() - programs hardware SDELTA registers with ++ * the voltage margin adjustments that need to be applied at ++ * different online core-count and temperature bands. Also, ++ * programs hardware register configuration for per-online-core ++ * and per-temperature based adjustments. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * CPR interface/bus clocks must be enabled before calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_corner *corner = &ctrl->aggr_corner; ++ struct cpr4_sdelta *sdelta = corner->sdelta; ++ int i, index, max_core_count, rc = 0; ++ bool cpr_enabled = ctrl->cpr_enabled; ++ ++ if (!sdelta) ++ /* cpr4_sdelta not defined for current aggregated corner */ ++ return 0; ++ ++ if (ctrl->supports_hw_closed_loop && ctrl->cpr_enabled) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ (ctrl->use_hw_closed_loop && !sdelta->allow_boost) ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0); ++ } ++ ++ if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj ++ && !sdelta->allow_boost) { ++ /* ++ * Per-online-core, per-temperature and voltage boost ++ * adjustments are disabled for this aggregation corner. ++ */ ++ return 0; ++ } ++ ++ /* Ensure that CPR clocks are enabled before writing to registers. */ ++ if (!cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ return rc; ++ } ++ ctrl->cpr_enabled = true; ++ } ++ ++ max_core_count = sdelta->max_core_count; ++ ++ if (sdelta->allow_core_count_adj || sdelta->allow_temp_adj) { ++ if (sdelta->allow_core_count_adj) { ++ /* Program TEMP_CORE0 to same margins as TEMP_CORE1 */ ++ cpr3_write_temp_core_margin(ctrl, ++ CPR4_REG_MARGIN_TEMP_CORE(0), ++ &sdelta->table[0]); ++ } ++ ++ for (i = 0; i < max_core_count; i++) { ++ index = i * sdelta->temp_band_count; ++ /* ++ * Program TEMP_COREi with voltage margin adjustments ++ * that need to be applied when the number of cores ++ * becomes i. ++ */ ++ cpr3_write_temp_core_margin(ctrl, ++ CPR4_REG_MARGIN_TEMP_CORE( ++ sdelta->allow_core_count_adj ++ ? i + 1 : max_core_count), ++ &sdelta->table[index]); ++ } ++ } ++ ++ if (sdelta->allow_boost) { ++ /* Program only boost_num_cores row of SDELTA */ ++ cpr3_write_temp_core_margin(ctrl, ++ CPR4_REG_MARGIN_TEMP_CORE(sdelta->boost_num_cores), ++ &sdelta->boost_table[0]); ++ } ++ ++ if (!sdelta->allow_core_count_adj && !sdelta->allow_boost) { ++ cpr3_masked_write(ctrl, CPR4_REG_MISC, ++ CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK, ++ max_core_count ++ << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT); ++ } ++ ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK ++ | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN ++ | CPR4_MARGIN_ADJ_CTL_BOOST_EN, ++ max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT ++ | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ++ ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) ++ | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) ++ ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) ++ | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost) ++ || !ctrl->supports_hw_closed_loop) ++ ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0) ++ | (sdelta->allow_boost ++ ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0)); ++ ++ /* ++ * Ensure that all previous CPR register writes have completed before ++ * continuing. ++ */ ++ mb(); ++ ++ /* Turn off CPR clocks if they were off before this function call. */ ++ if (!cpr_enabled) { ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_init_ctrl() - performs hardware initialization of CPR ++ * controller registers ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) ++{ ++ int i, j, k, m, rc; ++ u32 ro_used = 0; ++ u32 gcnt, cont_dly, up_down_dly, val; ++ u64 temp; ++ char *mode; ++ ++ if (ctrl->core_clk) { ++ rc = clk_set_rate(ctrl->core_clk, ctrl->cpr_clock_rate); ++ if (rc) { ++ cpr3_err(ctrl, "clk_set_rate(core_clk, %u) failed, rc=%d\n", ++ ctrl->cpr_clock_rate, rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ return rc; ++ } ++ ctrl->cpr_enabled = true; ++ ++ /* Find all RO's used by any corner of any regulator. */ ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ for (k = 0; k < ctrl->thread[i].vreg[j].corner_count; ++ k++) ++ for (m = 0; m < CPR3_RO_COUNT; m++) ++ if (ctrl->thread[i].vreg[j].corner[k]. ++ target_quot[m]) ++ ro_used |= BIT(m); ++ ++ /* Configure the GCNT of the RO's that will be used */ ++ gcnt = cpr3_regulator_get_gcnt(ctrl); ++ for (i = 0; i < CPR3_RO_COUNT; i++) ++ if (ro_used & BIT(i)) ++ cpr3_write(ctrl, CPR3_REG_GCNT(i), gcnt); ++ ++ /* Configure the loop delay time */ ++ temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->loop_time; ++ do_div(temp, 1000000000); ++ cont_dly = temp; ++ if (ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly); ++ else ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, cont_dly); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ temp = (u64)ctrl->cpr_clock_rate * ++ (u64)ctrl->up_down_delay_time; ++ do_div(temp, 1000000000); ++ up_down_dly = temp; ++ if (ctrl->supports_hw_closed_loop) ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, ++ up_down_dly); ++ cpr3_debug(ctrl, "up_down_dly=%u, up_down_delay_time=%u ns\n", ++ up_down_dly, ctrl->up_down_delay_time); ++ } ++ ++ cpr3_debug(ctrl, "cpr_clock_rate=%u HZ, sensor_time=%u ns, loop_time=%u ns, gcnt=%u, cont_dly=%u\n", ++ ctrl->cpr_clock_rate, ctrl->sensor_time, ctrl->loop_time, ++ gcnt, cont_dly); ++ ++ /* Configure CPR sensor operation */ ++ val = (ctrl->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT) ++ & CPR3_CPR_CTL_IDLE_CLOCKS_MASK; ++ val |= (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT) ++ & CPR3_CPR_CTL_COUNT_MODE_MASK; ++ val |= (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT) ++ & CPR3_CPR_CTL_COUNT_REPEAT_MASK; ++ cpr3_write(ctrl, CPR3_REG_CPR_CTL, val); ++ ++ cpr3_debug(ctrl, "idle_clocks=%u, count_mode=%u, count_repeat=%u; CPR_CTL=0x%08X\n", ++ ctrl->idle_clocks, ctrl->count_mode, ctrl->count_repeat, val); ++ ++ /* Configure CPR default step quotients */ ++ val = (ctrl->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT) ++ & CPR3_CPR_STEP_QUOT_MIN_MASK; ++ val |= (ctrl->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT) ++ & CPR3_CPR_STEP_QUOT_MAX_MASK; ++ cpr3_write(ctrl, CPR3_REG_CPR_STEP_QUOT, val); ++ ++ cpr3_debug(ctrl, "step_quot_min=%u, step_quot_max=%u; STEP_QUOT=0x%08X\n", ++ ctrl->step_quot_init_min, ctrl->step_quot_init_max, val); ++ ++ /* Configure the CPR sensor ownership */ ++ for (i = 0; i < ctrl->sensor_count; i++) ++ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(i), ++ ctrl->sensor_owner[i]); ++ ++ /* Configure per-thread registers */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ rc = cpr3_regulator_init_thread(&ctrl->thread[i]); ++ if (rc) { ++ cpr3_err(ctrl, "CPR thread register initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ ctrl->use_hw_closed_loop ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE ++ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ ctrl->use_hw_closed_loop ++ ? CPR3_HW_CLOSED_LOOP_ENABLE ++ : CPR3_HW_CLOSED_LOOP_DISABLE); ++ ++ cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n", ++ ctrl->proc_clock_throttle); ++ } ++ ++ if ((ctrl->use_hw_closed_loop || ++ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && ++ ctrl->vdd_limit_regulator) { ++ rc = regulator_enable(ctrl->vdd_limit_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_regulator_init_cpr4(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR4-specific controller initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ /* Ensure that all register writes complete before disabling clocks. */ ++ wmb(); ++ ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ ++ if (!ctrl->cpr_allowed_sw || !ctrl->cpr_allowed_hw) ++ mode = "open-loop"; ++ else if (ctrl->supports_hw_closed_loop) ++ mode = ctrl->use_hw_closed_loop ++ ? "HW closed-loop" : "SW closed-loop"; ++ else ++ mode = "closed-loop"; ++ ++ cpr3_info(ctrl, "Default CPR mode = %s", mode); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_set_target_quot() - configure the target quotient for each ++ * RO of the CPR3 thread and set the RO mask ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_set_target_quot(struct cpr3_thread *thread) ++{ ++ u32 new_quot, last_quot; ++ int i; ++ ++ if (thread->aggr_corner.ro_mask == CPR3_RO_MASK ++ && thread->last_closed_loop_aggr_corner.ro_mask == CPR3_RO_MASK) { ++ /* Avoid writing target quotients since all RO's are masked. */ ++ return; ++ } else if (thread->aggr_corner.ro_mask == CPR3_RO_MASK) { ++ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), ++ CPR3_RO_MASK); ++ thread->last_closed_loop_aggr_corner.ro_mask = CPR3_RO_MASK; ++ /* ++ * Only the RO_MASK register needs to be written since all ++ * RO's are masked. ++ */ ++ return; ++ } else if (thread->aggr_corner.ro_mask ++ != thread->last_closed_loop_aggr_corner.ro_mask) { ++ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), ++ thread->aggr_corner.ro_mask); ++ } ++ ++ for (i = 0; i < CPR3_RO_COUNT; i++) { ++ new_quot = thread->aggr_corner.target_quot[i]; ++ last_quot = thread->last_closed_loop_aggr_corner.target_quot[i]; ++ if (new_quot != last_quot) ++ cpr3_write(thread->ctrl, ++ CPR3_REG_TARGET_QUOT(thread->thread_id, i), ++ new_quot); ++ } ++ ++ thread->last_closed_loop_aggr_corner = thread->aggr_corner; ++ ++ return; ++} ++ ++/** ++ * cpr3_update_vreg_closed_loop_volt() - update the last known settled ++ * closed loop voltage for a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * @vdd_volt: Last known settled voltage in microvolts for the ++ * VDD supply ++ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register ++ * ++ * Return: none ++ */ ++static void cpr3_update_vreg_closed_loop_volt(struct cpr3_regulator *vreg, ++ int vdd_volt, u32 reg_last_measurement) ++{ ++ bool step_dn, step_up, aggr_step_up, aggr_step_dn, aggr_step_mid; ++ bool valid, pd_valid, saw_error; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr3_corner *corner; ++ u32 id; ++ ++ if (vreg->last_closed_loop_corner == CPR3_REGULATOR_CORNER_INVALID) ++ return; ++ else ++ corner = &vreg->corner[vreg->last_closed_loop_corner]; ++ ++ if (vreg->thread->last_closed_loop_aggr_corner.ro_mask ++ == CPR3_RO_MASK || !vreg->aggregated) { ++ return; ++ } else if (!ctrl->cpr_enabled || !ctrl->last_corner_was_closed_loop) { ++ return; ++ } else if (ctrl->thread_count == 1 ++ && vdd_volt >= corner->floor_volt ++ && vdd_volt <= corner->ceiling_volt) { ++ corner->last_volt = vdd_volt; ++ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, ++ corner->ceiling_volt, ++ vreg->last_closed_loop_corner, ++ corner->floor_volt); ++ return; ++ } else if (!ctrl->supports_hw_closed_loop) { ++ return; ++ } else if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPR3) { ++ corner->last_volt = vdd_volt; ++ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, ++ corner->ceiling_volt, ++ vreg->last_closed_loop_corner, ++ corner->floor_volt); ++ return; ++ } ++ ++ /* CPR clocks are on and HW closed loop is supported */ ++ valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID); ++ if (!valid) { ++ cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X valid bit not set\n", ++ reg_last_measurement); ++ return; ++ } ++ ++ id = vreg->thread->thread_id; ++ ++ step_dn ++ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_DN(id)); ++ step_up ++ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_UP(id)); ++ aggr_step_dn = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_DN); ++ aggr_step_mid ++ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_MID); ++ aggr_step_up = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_UP); ++ saw_error = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_SAW_ERROR); ++ pd_valid ++ = !((((reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK) ++ >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT) ++ & vreg->pd_bypass_mask) == vreg->pd_bypass_mask); ++ ++ if (!pd_valid) { ++ cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X, all power domains bypassed\n", ++ reg_last_measurement); ++ return; ++ } else if (step_dn && step_up) { ++ cpr3_err(vreg, "both up and down status bits set, CPR_LAST_VALID_MEASUREMENT=0x%X\n", ++ reg_last_measurement); ++ return; ++ } else if (aggr_step_dn && step_dn && vdd_volt < corner->last_volt ++ && vdd_volt >= corner->floor_volt) { ++ corner->last_volt = vdd_volt; ++ } else if (aggr_step_up && step_up && vdd_volt > corner->last_volt ++ && vdd_volt <= corner->ceiling_volt) { ++ corner->last_volt = vdd_volt; ++ } else if (aggr_step_mid ++ && vdd_volt >= corner->floor_volt ++ && vdd_volt <= corner->ceiling_volt) { ++ corner->last_volt = vdd_volt; ++ } else if (saw_error && (vdd_volt == corner->ceiling_volt ++ || vdd_volt == corner->floor_volt)) { ++ corner->last_volt = vdd_volt; ++ } else { ++ cpr3_debug(vreg, "last_volt not updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, vdd_volt=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, ++ corner->ceiling_volt, ++ vreg->last_closed_loop_corner, corner->floor_volt, ++ vdd_volt, reg_last_measurement); ++ return; ++ } ++ ++ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n", ++ vreg->last_closed_loop_corner, corner->last_volt, ++ vreg->last_closed_loop_corner, corner->ceiling_volt, ++ vreg->last_closed_loop_corner, corner->floor_volt, ++ reg_last_measurement); ++} ++ ++/** ++ * cpr3_regulator_mem_acc_bhs_used() - determines if mem-acc regulators powered ++ * through a BHS are associated with the CPR3 controller or any of ++ * the CPR3 regulators it controls. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * This function determines if the CPR3 controller or any of its CPR3 regulators ++ * need to manage mem-acc regulators that are currently powered through a BHS ++ * and whose corner selection is based upon a particular voltage threshold. ++ * ++ * Return: true or false ++ */ ++static bool cpr3_regulator_mem_acc_bhs_used(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_regulator *vreg; ++ int i, j; ++ ++ if (!ctrl->mem_acc_threshold_volt) ++ return false; ++ ++ if (ctrl->mem_acc_regulator) ++ return true; ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (vreg->mem_acc_regulator) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/** ++ * cpr3_regulator_config_bhs_mem_acc() - configure the mem-acc regulator ++ * settings for hardware blocks currently powered through the BHS. ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to ++ * end up at ++ * @last_volt: Pointer to the last known voltage in microvolts for the ++ * VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function programs the mem-acc regulator corners for CPR3 regulators ++ * whose LDO regulators are in bypassed state. The function also handles ++ * CPR3 controllers which utilize mem-acc regulators that operate independently ++ * from the LDO hardware and that must be programmed when the VDD supply ++ * crosses a particular voltage threshold. ++ * ++ * Return: 0 on success, errno on failure. If the VDD supply voltage is ++ * modified, last_volt is updated to reflect the new voltage setpoint. ++ */ ++static int cpr3_regulator_config_bhs_mem_acc(struct cpr3_controller *ctrl, ++ int new_volt, int *last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ struct cpr3_regulator *vreg; ++ int i, j, rc, mem_acc_corn, safe_volt; ++ int mem_acc_volt = ctrl->mem_acc_threshold_volt; ++ int ref_volt; ++ ++ if (!cpr3_regulator_mem_acc_bhs_used(ctrl)) ++ return 0; ++ ++ ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt : ++ new_volt; ++ ++ if (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) || ++ (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt))) { ++ if (ref_volt < *last_volt) ++ safe_volt = max(mem_acc_volt, aggr_corner->last_volt); ++ else ++ safe_volt = max(mem_acc_volt, *last_volt); ++ ++ rc = regulator_set_voltage(ctrl->vdd_regulator, safe_volt, ++ new_volt < *last_volt ? ++ ctrl->aggr_corner.ceiling_volt : ++ new_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", ++ safe_volt, rc); ++ return rc; ++ } ++ ++ *last_volt = safe_volt; ++ ++ mem_acc_corn = ref_volt < mem_acc_volt ? ++ ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER] : ++ ctrl->mem_acc_corner_map[CPR3_MEM_ACC_HIGH_CORNER]; ++ ++ if (ctrl->mem_acc_regulator) { ++ rc = regulator_set_voltage(ctrl->mem_acc_regulator, ++ mem_acc_corn, mem_acc_corn); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", ++ mem_acc_corn, rc); ++ return rc; ++ } ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (!vreg->mem_acc_regulator) ++ continue; ++ ++ rc = regulator_set_voltage( ++ vreg->mem_acc_regulator, mem_acc_corn, ++ mem_acc_corn); ++ if (rc) { ++ cpr3_err(vreg, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", ++ mem_acc_corn, rc); ++ return rc; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_switch_apm_mode() - switch the mode of the APM controller ++ * associated with a given CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to ++ * end up at ++ * @last_volt: Pointer to the last known voltage in microvolts for the ++ * VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function requests a switch of the APM mode while guaranteeing ++ * any LDO regulator hardware requirements are satisfied. The function must ++ * be called once it is known a new VDD supply setpoint crosses the APM ++ * voltage threshold. ++ * ++ * Return: 0 on success, errno on failure. If the VDD supply voltage is ++ * modified, last_volt is updated to reflect the new voltage setpoint. ++ */ ++static int cpr3_regulator_switch_apm_mode(struct cpr3_controller *ctrl, ++ int new_volt, int *last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ struct regulator *vdd = ctrl->vdd_regulator; ++ int apm_volt = ctrl->apm_threshold_volt; ++ int orig_last_volt = *last_volt; ++ int rc; ++ ++ rc = regulator_set_voltage(vdd, apm_volt, apm_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", ++ apm_volt, rc); ++ return rc; ++ } ++ ++ *last_volt = apm_volt; ++ ++ rc = msm_apm_set_supply(ctrl->apm, new_volt >= apm_volt ++ ? ctrl->apm_high_supply : ctrl->apm_low_supply); ++ if (rc) { ++ cpr3_err(ctrl, "APM switch failed, rc=%d\n", rc); ++ /* Roll back the voltage. */ ++ regulator_set_voltage(vdd, orig_last_volt, INT_MAX); ++ *last_volt = orig_last_volt; ++ return rc; ++ } ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_config_voltage_crossings() - configure APM and mem-acc ++ * settings depending upon a new VDD supply setpoint ++ * ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to ++ * end up at ++ * @last_volt: Pointer to the last known voltage in microvolts for the ++ * VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function handles the APM and mem-acc regulator reconfiguration if ++ * the new VDD supply voltage will result in crossing their respective voltage ++ * thresholds. ++ * ++ * Return: 0 on success, errno on failure. If the VDD supply voltage is ++ * modified, last_volt is updated to reflect the new voltage setpoint. ++ */ ++static int cpr3_regulator_config_voltage_crossings(struct cpr3_controller *ctrl, ++ int new_volt, int *last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ bool apm_crossing = false, mem_acc_crossing = false; ++ bool mem_acc_bhs_used; ++ int apm_volt = ctrl->apm_threshold_volt; ++ int mem_acc_volt = ctrl->mem_acc_threshold_volt; ++ int ref_volt, rc; ++ ++ if (ctrl->apm && apm_volt > 0 ++ && ((*last_volt < apm_volt && apm_volt <= new_volt) ++ || (*last_volt >= apm_volt && apm_volt > new_volt))) ++ apm_crossing = true; ++ ++ mem_acc_bhs_used = cpr3_regulator_mem_acc_bhs_used(ctrl); ++ ++ ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt : ++ new_volt; ++ ++ if (mem_acc_bhs_used && ++ (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) || ++ (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt)))) ++ mem_acc_crossing = true; ++ ++ if (apm_crossing && mem_acc_crossing) { ++ if ((new_volt < *last_volt && apm_volt >= mem_acc_volt) || ++ (new_volt >= *last_volt && apm_volt < mem_acc_volt)) { ++ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, ++ last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to switch APM mode\n"); ++ return rc; ++ } ++ ++ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, ++ last_volt, aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); ++ return rc; ++ } ++ } else { ++ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, ++ last_volt, aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); ++ return rc; ++ } ++ ++ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, ++ last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to switch APM mode\n"); ++ return rc; ++ } ++ } ++ } else if (apm_crossing) { ++ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to switch APM mode\n"); ++ return rc; ++ } ++ } else if (mem_acc_crossing) { ++ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, ++ last_volt, aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_config_mem_acc() - configure the corner of the mem-acc ++ * regulator associated with the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_config_mem_acc(struct cpr3_controller *ctrl, ++ struct cpr3_corner *aggr_corner) ++{ ++ int rc; ++ ++ if (ctrl->mem_acc_regulator && aggr_corner->mem_acc_volt) { ++ rc = regulator_set_voltage(ctrl->mem_acc_regulator, ++ aggr_corner->mem_acc_volt, ++ aggr_corner->mem_acc_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", ++ aggr_corner->mem_acc_volt, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_scale_vdd_voltage() - scale the CPR controlled VDD supply ++ * voltage to the new level while satisfying any other hardware ++ * requirements ++ * @ctrl: Pointer to the CPR3 controller ++ * @new_volt: New voltage in microvolts that VDD supply needs to end ++ * up at ++ * @last_volt: Last known voltage in microvolts for the VDD supply ++ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max ++ * corner aggregated from all CPR3 threads managed by the ++ * CPR3 controller ++ * ++ * This function scales the CPR controlled VDD supply voltage from its ++ * current level to the new voltage that is specified. If the supply is ++ * configured to use the APM and the APM threshold is crossed as a result of ++ * the voltage scaling, then this function also stops at the APM threshold, ++ * switches the APM source, and finally sets the final new voltage. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl, ++ int new_volt, int last_volt, ++ struct cpr3_corner *aggr_corner) ++{ ++ struct regulator *vdd = ctrl->vdd_regulator; ++ int rc; ++ ++ if (new_volt < last_volt) { ++ rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); ++ if (rc) ++ return rc; ++ } else { ++ /* Increasing VDD voltage */ ++ if (ctrl->system_regulator) { ++ rc = regulator_set_voltage(ctrl->system_regulator, ++ aggr_corner->system_volt, INT_MAX); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n", ++ aggr_corner->system_volt, rc); ++ return rc; ++ } ++ } ++ } ++ ++ rc = cpr3_regulator_config_voltage_crossings(ctrl, new_volt, &last_volt, ++ aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "unable to handle voltage threshold crossing configurations, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* ++ * Subtract a small amount from the min_uV parameter so that the ++ * set voltage request is not dropped by the framework due to being ++ * duplicate. This is needed in order to switch from hardware ++ * closed-loop to open-loop successfully. ++ */ ++ rc = regulator_set_voltage(vdd, new_volt - (ctrl->cpr_enabled ? 0 : 1), ++ aggr_corner->ceiling_volt); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", ++ new_volt, rc); ++ return rc; ++ } ++ ++ if (new_volt == last_volt && ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ /* ++ * CPR4 features enforce voltage reprogramming when the last ++ * set voltage and new set voltage are same. This way, we can ++ * ensure that SAW PMIC STATUS register is updated with newly ++ * programmed voltage. ++ */ ++ rc = regulator_sync_voltage(vdd); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_sync_voltage(vdd) == %d failed, rc=%d\n", ++ new_volt, rc); ++ return rc; ++ } ++ } ++ ++ if (new_volt >= last_volt) { ++ rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); ++ if (rc) ++ return rc; ++ } else { ++ /* Decreasing VDD voltage */ ++ if (ctrl->system_regulator) { ++ rc = regulator_set_voltage(ctrl->system_regulator, ++ aggr_corner->system_volt, INT_MAX); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n", ++ aggr_corner->system_volt, rc); ++ return rc; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_get_dynamic_floor_volt() - returns the current dynamic floor ++ * voltage based upon static configurations and the state of all ++ * power domains during the last CPR measurement ++ * @ctrl: Pointer to the CPR3 controller ++ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register ++ * ++ * When using HW closed-loop, the dynamic floor voltage is always returned ++ * regardless of the current state of the power domains. ++ * ++ * Return: dynamic floor voltage in microvolts or 0 if dynamic floor is not ++ * currently required ++ */ ++static int cpr3_regulator_get_dynamic_floor_volt(struct cpr3_controller *ctrl, ++ u32 reg_last_measurement) ++{ ++ int dynamic_floor_volt = 0; ++ struct cpr3_regulator *vreg; ++ bool valid, pd_valid; ++ u32 bypass_bits; ++ int i, j; ++ ++ if (!ctrl->supports_hw_closed_loop) ++ return 0; ++ ++ if (likely(!ctrl->use_hw_closed_loop)) { ++ valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID); ++ bypass_bits ++ = (reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK) ++ >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT; ++ } else { ++ /* ++ * Ensure that the dynamic floor voltage is always used for ++ * HW closed-loop since the conditions below cannot be evaluated ++ * after each CPR measurement. ++ */ ++ valid = false; ++ bypass_bits = 0; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (!vreg->uses_dynamic_floor) ++ continue; ++ ++ pd_valid = !((bypass_bits & vreg->pd_bypass_mask) ++ == vreg->pd_bypass_mask); ++ ++ if (!valid || !pd_valid) ++ dynamic_floor_volt = max(dynamic_floor_volt, ++ vreg->corner[ ++ vreg->dynamic_floor_corner].last_volt); ++ } ++ } ++ ++ return dynamic_floor_volt; ++} ++ ++/** ++ * cpr3_regulator_max_sdelta_diff() - returns the maximum voltage difference in ++ * microvolts that can result from different operating conditions ++ * for the specified sdelta struct ++ * @sdelta: Pointer to the sdelta structure ++ * @step_volt: Step size in microvolts between available set ++ * points of the VDD supply. ++ * ++ * Return: voltage difference between the highest and lowest adjustments if ++ * sdelta and sdelta->table are valid, else 0. ++ */ ++static int cpr3_regulator_max_sdelta_diff(const struct cpr4_sdelta *sdelta, ++ int step_volt) ++{ ++ int i, j, index, sdelta_min = INT_MAX, sdelta_max = INT_MIN; ++ ++ if (!sdelta || !sdelta->table) ++ return 0; ++ ++ for (i = 0; i < sdelta->max_core_count; i++) { ++ for (j = 0; j < sdelta->temp_band_count; j++) { ++ index = i * sdelta->temp_band_count + j; ++ sdelta_min = min(sdelta_min, sdelta->table[index]); ++ sdelta_max = max(sdelta_max, sdelta->table[index]); ++ } ++ } ++ ++ return (sdelta_max - sdelta_min) * step_volt; ++} ++ ++/** ++ * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current ++ * aggregated corner and current corner of a given regulator ++ * and adjust the sdelta strucuture data of aggregate corner. ++ * @aggr_corner: Pointer to accumulated aggregated corner which ++ * is both an input and an output ++ * @corner: Pointer to the corner to be aggregated with ++ * aggr_corner ++ * @step_volt: Step size in microvolts between available set ++ * points of the VDD supply. ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_aggregate_sdelta( ++ struct cpr3_corner *aggr_corner, ++ const struct cpr3_corner *corner, int step_volt) ++{ ++ struct cpr4_sdelta *aggr_sdelta, *sdelta; ++ int aggr_core_count, core_count, temp_band_count; ++ u32 aggr_index, index; ++ int i, j, sdelta_size, cap_steps, adjust_sdelta; ++ ++ aggr_sdelta = aggr_corner->sdelta; ++ sdelta = corner->sdelta; ++ ++ if (aggr_corner->open_loop_volt < corner->open_loop_volt) { ++ /* ++ * Found the new dominant regulator as its open-loop requirement ++ * is higher than previous dominant regulator. Calculate cap ++ * voltage to limit the SDELTA values to make sure the runtime ++ * (Core-count/temp) adjustments do not violate other ++ * regulators' voltage requirements. Use cpr4_sdelta values of ++ * new dominant regulator. ++ */ ++ aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt, ++ (corner->open_loop_volt - ++ aggr_corner->open_loop_volt)); ++ ++ /* Clear old data in the sdelta table */ ++ sdelta_size = aggr_sdelta->max_core_count ++ * aggr_sdelta->temp_band_count; ++ ++ if (aggr_sdelta->allow_core_count_adj ++ || aggr_sdelta->allow_temp_adj) ++ memset(aggr_sdelta->table, 0, sdelta_size ++ * sizeof(*aggr_sdelta->table)); ++ ++ if (sdelta->allow_temp_adj || sdelta->allow_core_count_adj) { ++ /* Copy new data in sdelta table */ ++ sdelta_size = sdelta->max_core_count ++ * sdelta->temp_band_count; ++ if (sdelta->table) ++ memcpy(aggr_sdelta->table, sdelta->table, ++ sdelta_size * sizeof(*sdelta->table)); ++ } ++ ++ if (sdelta->allow_boost) { ++ memcpy(aggr_sdelta->boost_table, sdelta->boost_table, ++ sdelta->temp_band_count ++ * sizeof(*sdelta->boost_table)); ++ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; ++ } else if (aggr_sdelta->allow_boost) { ++ for (i = 0; i < aggr_sdelta->temp_band_count; i++) { ++ adjust_sdelta = (corner->open_loop_volt ++ - aggr_corner->open_loop_volt) ++ / step_volt; ++ aggr_sdelta->boost_table[i] += adjust_sdelta; ++ aggr_sdelta->boost_table[i] ++ = min(aggr_sdelta->boost_table[i], 0); ++ } ++ } ++ ++ aggr_corner->open_loop_volt = corner->open_loop_volt; ++ aggr_sdelta->allow_temp_adj = sdelta->allow_temp_adj; ++ aggr_sdelta->allow_core_count_adj ++ = sdelta->allow_core_count_adj; ++ aggr_sdelta->max_core_count = sdelta->max_core_count; ++ aggr_sdelta->temp_band_count = sdelta->temp_band_count; ++ } else if (aggr_corner->open_loop_volt > corner->open_loop_volt) { ++ /* ++ * Adjust the cap voltage if the open-loop requirement of new ++ * regulator is the next highest. ++ */ ++ aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt, ++ (aggr_corner->open_loop_volt ++ - corner->open_loop_volt)); ++ ++ if (sdelta->allow_boost) { ++ for (i = 0; i < aggr_sdelta->temp_band_count; i++) { ++ adjust_sdelta = (aggr_corner->open_loop_volt ++ - corner->open_loop_volt) ++ / step_volt; ++ aggr_sdelta->boost_table[i] = ++ sdelta->boost_table[i] + adjust_sdelta; ++ aggr_sdelta->boost_table[i] ++ = min(aggr_sdelta->boost_table[i], 0); ++ } ++ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; ++ } ++ } else { ++ /* ++ * Found another dominant regulator with same open-loop ++ * requirement. Make cap voltage to '0'. Disable core-count ++ * adjustments as we couldn't support for both regulators. ++ * Keep enable temp based adjustments if enabled for both ++ * regulators and choose mininum margin adjustment values ++ * between them. ++ */ ++ aggr_sdelta->cap_volt = 0; ++ aggr_sdelta->allow_core_count_adj = false; ++ ++ if (aggr_sdelta->allow_temp_adj ++ && sdelta->allow_temp_adj) { ++ aggr_core_count = aggr_sdelta->max_core_count - 1; ++ core_count = sdelta->max_core_count - 1; ++ temp_band_count = sdelta->temp_band_count; ++ for (j = 0; j < temp_band_count; j++) { ++ aggr_index = aggr_core_count * temp_band_count ++ + j; ++ index = core_count * temp_band_count + j; ++ aggr_sdelta->table[aggr_index] = ++ min(aggr_sdelta->table[aggr_index], ++ sdelta->table[index]); ++ } ++ } else { ++ aggr_sdelta->allow_temp_adj = false; ++ } ++ ++ if (sdelta->allow_boost) { ++ memcpy(aggr_sdelta->boost_table, sdelta->boost_table, ++ sdelta->temp_band_count ++ * sizeof(*sdelta->boost_table)); ++ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; ++ } ++ } ++ ++ /* Keep non-dominant clients boost enable state */ ++ aggr_sdelta->allow_boost |= sdelta->allow_boost; ++ if (aggr_sdelta->allow_boost) ++ aggr_sdelta->allow_core_count_adj = false; ++ ++ if (aggr_sdelta->cap_volt && !(aggr_sdelta->cap_volt == INT_MAX)) { ++ core_count = aggr_sdelta->max_core_count; ++ temp_band_count = aggr_sdelta->temp_band_count; ++ /* ++ * Convert cap voltage from uV to PMIC steps and use to limit ++ * sdelta margin adjustments. ++ */ ++ cap_steps = aggr_sdelta->cap_volt / step_volt; ++ for (i = 0; i < core_count; i++) ++ for (j = 0; j < temp_band_count; j++) { ++ index = i * temp_band_count + j; ++ aggr_sdelta->table[index] = ++ min(aggr_sdelta->table[index], ++ cap_steps); ++ } ++ } ++} ++ ++/** ++ * cpr3_regulator_aggregate_corners() - aggregate two corners together ++ * @aggr_corner: Pointer to accumulated aggregated corner which ++ * is both an input and an output ++ * @corner: Pointer to the corner to be aggregated with ++ * aggr_corner ++ * @aggr_quot: Flag indicating that target quotients should be ++ * aggregated as well. ++ * @step_volt: Step size in microvolts between available set ++ * points of the VDD supply. ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_aggregate_corners(struct cpr3_corner *aggr_corner, ++ const struct cpr3_corner *corner, bool aggr_quot, ++ int step_volt) ++{ ++ int i; ++ ++ aggr_corner->ceiling_volt ++ = max(aggr_corner->ceiling_volt, corner->ceiling_volt); ++ aggr_corner->floor_volt ++ = max(aggr_corner->floor_volt, corner->floor_volt); ++ aggr_corner->last_volt ++ = max(aggr_corner->last_volt, corner->last_volt); ++ aggr_corner->system_volt ++ = max(aggr_corner->system_volt, corner->system_volt); ++ aggr_corner->mem_acc_volt ++ = max(aggr_corner->mem_acc_volt, corner->mem_acc_volt); ++ aggr_corner->irq_en |= corner->irq_en; ++ aggr_corner->use_open_loop |= corner->use_open_loop; ++ ++ if (aggr_quot) { ++ aggr_corner->ro_mask &= corner->ro_mask; ++ ++ for (i = 0; i < CPR3_RO_COUNT; i++) ++ aggr_corner->target_quot[i] ++ = max(aggr_corner->target_quot[i], ++ corner->target_quot[i]); ++ } ++ ++ if (aggr_corner->sdelta && corner->sdelta ++ && (aggr_corner->sdelta->table ++ || aggr_corner->sdelta->boost_table)) { ++ cpr3_regulator_aggregate_sdelta(aggr_corner, corner, step_volt); ++ } else { ++ aggr_corner->open_loop_volt ++ = max(aggr_corner->open_loop_volt, ++ corner->open_loop_volt); ++ } ++} ++ ++/** ++ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller ++ * to reflect the corners used by all CPR3 regulators as well as ++ * the CPR operating mode ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * This function aggregates the CPR parameters for all CPR3 regulators ++ * associated with the VDD supply. Upon success, it sets the aggregated last ++ * known good voltage. ++ * ++ * The VDD supply voltage will not be physically configured unless this ++ * condition is met by at least one of the regulators of the controller: ++ * regulator->vreg_enabled == true && ++ * regulator->current_corner != CPR3_REGULATOR_CORNER_INVALID ++ * ++ * CPR registers for the controller and each thread are updated as long as ++ * ctrl->cpr_enabled == true. ++ * ++ * Note, CPR3 controller lock must be held by the caller. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_corner aggr_corner = {}; ++ struct cpr3_thread *thread; ++ struct cpr3_regulator *vreg; ++ struct cpr4_sdelta *sdelta; ++ bool valid = false; ++ bool thread_valid; ++ int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt; ++ u32 reg_last_measurement = 0, sdelta_size; ++ int *sdelta_table, *boost_table; ++ ++ last_corner_volt = 0; ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ vdd_volt = regulator_get_voltage(ctrl->vdd_regulator); ++ if (vdd_volt < 0) { ++ cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n", ++ vdd_volt); ++ return vdd_volt; ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ /* ++ * Save aggregated corner open-loop voltage which was programmed ++ * during last corner switch which is used when programming new ++ * aggregated corner open-loop voltage. ++ */ ++ last_corner_volt = ctrl->aggr_corner.open_loop_volt; ++ } ++ ++ if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop && ++ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ++ reg_last_measurement ++ = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT); ++ ++ aggr_corner.sdelta = ctrl->aggr_corner.sdelta; ++ if (aggr_corner.sdelta) { ++ sdelta = aggr_corner.sdelta; ++ sdelta_table = sdelta->table; ++ if (sdelta_table) { ++ sdelta_size = sdelta->max_core_count * ++ sdelta->temp_band_count; ++ memset(sdelta_table, 0, sdelta_size ++ * sizeof(*sdelta_table)); ++ } ++ ++ boost_table = sdelta->boost_table; ++ if (boost_table) ++ memset(boost_table, 0, sdelta->temp_band_count ++ * sizeof(*boost_table)); ++ ++ memset(sdelta, 0, sizeof(*sdelta)); ++ sdelta->table = sdelta_table; ++ sdelta->cap_volt = INT_MAX; ++ sdelta->boost_table = boost_table; ++ } ++ ++ /* Aggregate the requests of all threads */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ thread = &ctrl->thread[i]; ++ thread_valid = false; ++ ++ sdelta = thread->aggr_corner.sdelta; ++ if (sdelta) { ++ sdelta_table = sdelta->table; ++ if (sdelta_table) { ++ sdelta_size = sdelta->max_core_count * ++ sdelta->temp_band_count; ++ memset(sdelta_table, 0, sdelta_size ++ * sizeof(*sdelta_table)); ++ } ++ ++ boost_table = sdelta->boost_table; ++ if (boost_table) ++ memset(boost_table, 0, sdelta->temp_band_count ++ * sizeof(*boost_table)); ++ ++ memset(sdelta, 0, sizeof(*sdelta)); ++ sdelta->table = sdelta_table; ++ sdelta->cap_volt = INT_MAX; ++ sdelta->boost_table = boost_table; ++ } ++ ++ memset(&thread->aggr_corner, 0, sizeof(thread->aggr_corner)); ++ thread->aggr_corner.sdelta = sdelta; ++ thread->aggr_corner.ro_mask = CPR3_RO_MASK; ++ ++ for (j = 0; j < thread->vreg_count; j++) { ++ vreg = &thread->vreg[j]; ++ ++ if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop) ++ cpr3_update_vreg_closed_loop_volt(vreg, ++ vdd_volt, reg_last_measurement); ++ ++ if (!vreg->vreg_enabled ++ || vreg->current_corner ++ == CPR3_REGULATOR_CORNER_INVALID) { ++ /* Cannot participate in aggregation. */ ++ vreg->aggregated = false; ++ continue; ++ } else { ++ vreg->aggregated = true; ++ thread_valid = true; ++ } ++ ++ cpr3_regulator_aggregate_corners(&thread->aggr_corner, ++ &vreg->corner[vreg->current_corner], ++ true, ctrl->step_volt); ++ } ++ ++ valid |= thread_valid; ++ ++ if (thread_valid) ++ cpr3_regulator_aggregate_corners(&aggr_corner, ++ &thread->aggr_corner, ++ false, ctrl->step_volt); ++ } ++ ++ if (valid && ctrl->cpr_allowed_hw && ctrl->cpr_allowed_sw) { ++ rc = cpr3_closed_loop_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc); ++ return rc; ++ } ++ } else { ++ rc = cpr3_closed_loop_disable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc); ++ return rc; ++ } ++ } ++ ++ /* No threads are enabled with a valid corner so exit. */ ++ if (!valid) ++ return 0; ++ ++ /* ++ * When using CPR hardware closed-loop, the voltage may vary anywhere ++ * between the floor and ceiling voltage without software notification. ++ * Therefore, it is required that the floor to ceiling range for the ++ * aggregated corner not intersect the APM threshold voltage. Adjust ++ * the floor to ceiling range if this requirement is violated. ++ * ++ * The following algorithm is applied in the case that ++ * floor < threshold <= ceiling: ++ * if open_loop >= threshold - adj, then floor = threshold ++ * else ceiling = threshold - step ++ * where adj = an adjustment factor to ensure sufficient voltage margin ++ * and step = VDD output step size ++ * ++ * The open-loop and last known voltages are also bounded by the new ++ * floor or ceiling value as needed. ++ */ ++ if (ctrl->use_hw_closed_loop ++ && aggr_corner.ceiling_volt >= ctrl->apm_threshold_volt ++ && aggr_corner.floor_volt < ctrl->apm_threshold_volt) { ++ ++ if (aggr_corner.open_loop_volt ++ >= ctrl->apm_threshold_volt - ctrl->apm_adj_volt) ++ aggr_corner.floor_volt = ctrl->apm_threshold_volt; ++ else ++ aggr_corner.ceiling_volt ++ = ctrl->apm_threshold_volt - ctrl->step_volt; ++ ++ aggr_corner.last_volt ++ = max(aggr_corner.last_volt, aggr_corner.floor_volt); ++ aggr_corner.last_volt ++ = min(aggr_corner.last_volt, aggr_corner.ceiling_volt); ++ aggr_corner.open_loop_volt ++ = max(aggr_corner.open_loop_volt, aggr_corner.floor_volt); ++ aggr_corner.open_loop_volt ++ = min(aggr_corner.open_loop_volt, aggr_corner.ceiling_volt); ++ } ++ ++ if (ctrl->use_hw_closed_loop ++ && aggr_corner.ceiling_volt >= ctrl->mem_acc_threshold_volt ++ && aggr_corner.floor_volt < ctrl->mem_acc_threshold_volt) { ++ aggr_corner.floor_volt = ctrl->mem_acc_threshold_volt; ++ aggr_corner.last_volt = max(aggr_corner.last_volt, ++ aggr_corner.floor_volt); ++ aggr_corner.open_loop_volt = max(aggr_corner.open_loop_volt, ++ aggr_corner.floor_volt); ++ } ++ ++ if (ctrl->use_hw_closed_loop) { ++ dynamic_floor_volt ++ = cpr3_regulator_get_dynamic_floor_volt(ctrl, ++ reg_last_measurement); ++ if (aggr_corner.floor_volt < dynamic_floor_volt) { ++ aggr_corner.floor_volt = dynamic_floor_volt; ++ aggr_corner.last_volt = max(aggr_corner.last_volt, ++ aggr_corner.floor_volt); ++ aggr_corner.open_loop_volt ++ = max(aggr_corner.open_loop_volt, ++ aggr_corner.floor_volt); ++ aggr_corner.ceiling_volt = max(aggr_corner.ceiling_volt, ++ aggr_corner.floor_volt); ++ } ++ } ++ ++ if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) { ++ /* ++ * Always program open-loop voltage for CPR4 controllers which ++ * support hardware closed-loop. Storing the last closed loop ++ * voltage in corner structure can still help with debugging. ++ */ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) ++ new_volt = aggr_corner.last_volt; ++ else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ++ && ctrl->supports_hw_closed_loop) ++ new_volt = aggr_corner.open_loop_volt; ++ else ++ new_volt = min(aggr_corner.last_volt + ++ cpr3_regulator_max_sdelta_diff(aggr_corner.sdelta, ++ ctrl->step_volt), ++ aggr_corner.ceiling_volt); ++ ++ aggr_corner.last_volt = new_volt; ++ } else { ++ new_volt = aggr_corner.open_loop_volt; ++ aggr_corner.last_volt = aggr_corner.open_loop_volt; ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ++ && ctrl->supports_hw_closed_loop) { ++ /* ++ * Store last aggregated corner open-loop voltage in vdd_volt ++ * which is used when programming current aggregated corner ++ * required voltage. ++ */ ++ vdd_volt = last_corner_volt; ++ } ++ ++ cpr3_debug(ctrl, "setting new voltage=%d uV\n", new_volt); ++ rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt, ++ vdd_volt, &aggr_corner); ++ if (rc) { ++ cpr3_err(ctrl, "vdd voltage scaling failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ /* Only update registers if CPR is enabled. */ ++ if (ctrl->cpr_enabled) { ++ if (ctrl->use_hw_closed_loop) { ++ /* Hardware closed-loop */ ++ ++ /* Set ceiling and floor limits in hardware */ ++ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, ++ aggr_corner.floor_volt, ++ aggr_corner.ceiling_volt); ++ if (rc) { ++ cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } else { ++ /* Software closed-loop */ ++ ++ /* ++ * Disable UP or DOWN interrupts when at ceiling or ++ * floor respectively. ++ */ ++ if (new_volt == aggr_corner.floor_volt) ++ aggr_corner.irq_en &= ~CPR3_IRQ_DOWN; ++ if (new_volt == aggr_corner.ceiling_volt) ++ aggr_corner.irq_en &= ~CPR3_IRQ_UP; ++ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN); ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, aggr_corner.irq_en); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ cpr3_regulator_set_target_quot(&ctrl->thread[i]); ++ ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ if (vreg->vreg_enabled) ++ vreg->last_closed_loop_corner ++ = vreg->current_corner; ++ } ++ } ++ ++ if (ctrl->proc_clock_throttle) { ++ if (aggr_corner.ceiling_volt > aggr_corner.floor_volt ++ && (ctrl->use_hw_closed_loop ++ || new_volt < aggr_corner.ceiling_volt)) ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ ctrl->proc_clock_throttle); ++ else ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ } ++ ++ /* ++ * Ensure that all CPR register writes complete before ++ * re-enabling CPR loop operation. ++ */ ++ wmb(); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ++ && ctrl->vdd_limit_regulator) { ++ /* Set ceiling and floor limits in hardware */ ++ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, ++ aggr_corner.floor_volt, ++ aggr_corner.ceiling_volt); ++ if (rc) { ++ cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ ctrl->aggr_corner = aggr_corner; ++ ++ if (ctrl->allow_core_count_adj || ctrl->allow_temp_adj ++ || ctrl->allow_boost) { ++ rc = cpr3_controller_program_sdelta(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to program sdelta, rc=%d\n", rc); ++ return rc; ++ } ++ } ++ ++ /* ++ * Only enable the CPR controller if it is possible to set more than ++ * one vdd-supply voltage. ++ */ ++ if (aggr_corner.ceiling_volt > aggr_corner.floor_volt && ++ !aggr_corner.use_open_loop) ++ cpr3_ctrl_loop_enable(ctrl); ++ ++ ctrl->last_corner_was_closed_loop = ctrl->cpr_enabled; ++ cpr3_debug(ctrl, "CPR configuration updated\n"); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_wait_for_idle() - wait for the CPR controller to no longer be ++ * busy ++ * @ctrl: Pointer to the CPR3 controller ++ * @max_wait_ns: Max wait time in nanoseconds ++ * ++ * Return: 0 on success or -ETIMEDOUT if the controller was still busy after ++ * the maximum delay time ++ */ ++static int cpr3_regulator_wait_for_idle(struct cpr3_controller *ctrl, ++ s64 max_wait_ns) ++{ ++ ktime_t start, end; ++ s64 time_ns; ++ u32 reg; ++ ++ /* ++ * Ensure that all previous CPR register writes have completed before ++ * checking the status register. ++ */ ++ mb(); ++ ++ start = ktime_get(); ++ do { ++ end = ktime_get(); ++ time_ns = ktime_to_ns(ktime_sub(end, start)); ++ if (time_ns > max_wait_ns) { ++ cpr3_err(ctrl, "CPR controller still busy after %lld us\n", ++ div_s64(time_ns, 1000)); ++ return -ETIMEDOUT; ++ } ++ usleep_range(50, 100); ++ reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); ++ } while (reg & CPR3_CPR_STATUS_BUSY_MASK); ++ ++ return 0; ++} ++ ++/** ++ * cmp_int() - int comparison function to be passed into the sort() function ++ * which leads to ascending sorting ++ * @a: First int value ++ * @b: Second int value ++ * ++ * Return: >0 if a > b, 0 if a == b, <0 if a < b ++ */ ++static int cmp_int(const void *a, const void *b) ++{ ++ return *(int *)a - *(int *)b; ++} ++ ++/** ++ * cpr3_regulator_measure_aging() - measure the quotient difference for the ++ * specified CPR aging sensor ++ * @ctrl: Pointer to the CPR3 controller ++ * @aging_sensor: Aging sensor to measure ++ * ++ * Note that vdd-supply must be configured to the aging reference voltage before ++ * calling this function. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl, ++ struct cpr3_aging_sensor_info *aging_sensor) ++{ ++ u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max; ++ u32 quot_min_scaled, quot_max_scaled; ++ u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore; ++ u32 ro_mask_restore, cont_dly_restore, up_down_dly_restore = 0; ++ int quot_delta, quot_delta_scaled, quot_delta_scaled_sum; ++ int *quot_delta_results; ++ int rc, rc2, i, aging_measurement_count, filtered_count; ++ bool is_aging_measurement; ++ ++ quot_delta_results = kcalloc(CPR3_AGING_MEASUREMENT_ITERATIONS, ++ sizeof(*quot_delta_results), GFP_KERNEL); ++ if (!quot_delta_results) ++ return -ENOMEM; ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ kfree(quot_delta_results); ++ return rc; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ /* Enable up, down, and mid CPR interrupts */ ++ irq_restore = cpr3_read(ctrl, CPR3_REG_IRQ_EN); ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID); ++ ++ /* Ensure that the aging sensor is assigned to CPR thread 0 */ ++ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), 0); ++ ++ /* Switch from HW to SW closed-loop if necessary */ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ CPR3_HW_CLOSED_LOOP_DISABLE); ++ } ++ } ++ ++ /* Configure the GCNT for RO0 and RO1 that are used for aging */ ++ gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0)); ++ gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1)); ++ gcnt_ref = cpr3_regulator_get_gcnt(ctrl); ++ gcnt = gcnt_ref * 3 / 2; ++ cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt); ++ cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt); ++ ++ /* Unmask all RO's */ ++ ro_mask_restore = cpr3_read(ctrl, CPR3_REG_RO_MASK(0)); ++ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), 0); ++ ++ /* ++ * Mask all sensors except for the one to measure and bypass all ++ * sensors in collapsible domains. ++ */ ++ for (i = 0; i <= ctrl->sensor_count / 32; i++) { ++ mask = GENMASK(min(31, ctrl->sensor_count - i * 32), 0); ++ if (aging_sensor->sensor_id / 32 >= i ++ && aging_sensor->sensor_id / 32 < (i + 1)) ++ mask &= ~BIT(aging_sensor->sensor_id % 32); ++ cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), mask); ++ cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), ++ aging_sensor->bypass_mask[i]); ++ } ++ ++ /* Set CPR loop delays to 0 us */ ++ if (ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cont_dly_restore = cpr3_read(ctrl, CPR3_REG_CPR_TIMER_MID_CONT); ++ up_down_dly_restore = cpr3_read(ctrl, ++ CPR3_REG_CPR_TIMER_UP_DN_CONT); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, 0); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, 0); ++ } else { ++ cont_dly_restore = cpr3_read(ctrl, ++ CPR3_REG_CPR_TIMER_AUTO_CONT); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, 0); ++ } ++ ++ /* Set count mode to all-at-once min with no repeat */ ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK, ++ CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN ++ << CPR3_CPR_CTL_COUNT_MODE_SHIFT); ++ ++ cpr3_ctrl_loop_enable(ctrl); ++ ++ rc = cpr3_regulator_wait_for_idle(ctrl, ++ CPR3_AGING_MEASUREMENT_TIMEOUT_NS); ++ if (rc) ++ goto cleanup; ++ ++ /* Set count mode to all-at-once aging */ ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, CPR3_CPR_CTL_COUNT_MODE_MASK, ++ CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE ++ << CPR3_CPR_CTL_COUNT_MODE_SHIFT); ++ ++ aging_measurement_count = 0; ++ for (i = 0; i < CPR3_AGING_MEASUREMENT_ITERATIONS; i++) { ++ /* Send CONT_NACK */ ++ cpr3_write(ctrl, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK); ++ ++ rc = cpr3_regulator_wait_for_idle(ctrl, ++ CPR3_AGING_MEASUREMENT_TIMEOUT_NS); ++ if (rc) ++ goto cleanup; ++ ++ /* Check for PAGE_IS_AGE flag in status register */ ++ reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); ++ is_aging_measurement ++ = reg & CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK; ++ ++ /* Read CPR measurement results */ ++ result = cpr3_read(ctrl, CPR3_REG_RESULT1(0)); ++ quot_min = (result & CPR3_RESULT1_QUOT_MIN_MASK) ++ >> CPR3_RESULT1_QUOT_MIN_SHIFT; ++ quot_max = (result & CPR3_RESULT1_QUOT_MAX_MASK) ++ >> CPR3_RESULT1_QUOT_MAX_SHIFT; ++ sel_min = (result & CPR3_RESULT1_RO_MIN_MASK) ++ >> CPR3_RESULT1_RO_MIN_SHIFT; ++ sel_max = (result & CPR3_RESULT1_RO_MAX_MASK) ++ >> CPR3_RESULT1_RO_MAX_SHIFT; ++ ++ /* ++ * Scale the quotients so that they are equivalent to the fused ++ * values. This accounts for the difference in measurement ++ * interval times. ++ */ ++ quot_min_scaled = quot_min * (gcnt_ref + 1) / (gcnt + 1); ++ quot_max_scaled = quot_max * (gcnt_ref + 1) / (gcnt + 1); ++ ++ if (sel_max == 1) { ++ quot_delta = quot_max - quot_min; ++ quot_delta_scaled = quot_max_scaled - quot_min_scaled; ++ } else { ++ quot_delta = quot_min - quot_max; ++ quot_delta_scaled = quot_min_scaled - quot_max_scaled; ++ } ++ ++ if (is_aging_measurement) ++ quot_delta_results[aging_measurement_count++] ++ = quot_delta_scaled; ++ ++ cpr3_debug(ctrl, "aging results: page_is_age=%u, sel_min=%u, sel_max=%u, quot_min=%u, quot_max=%u, quot_delta=%d, quot_min_scaled=%u, quot_max_scaled=%u, quot_delta_scaled=%d\n", ++ is_aging_measurement, sel_min, sel_max, quot_min, ++ quot_max, quot_delta, quot_min_scaled, quot_max_scaled, ++ quot_delta_scaled); ++ } ++ ++ filtered_count ++ = aging_measurement_count - CPR3_AGING_MEASUREMENT_FILTER * 2; ++ if (filtered_count > 0) { ++ sort(quot_delta_results, aging_measurement_count, ++ sizeof(*quot_delta_results), cmp_int, NULL); ++ ++ quot_delta_scaled_sum = 0; ++ for (i = 0; i < filtered_count; i++) ++ quot_delta_scaled_sum ++ += quot_delta_results[i ++ + CPR3_AGING_MEASUREMENT_FILTER]; ++ ++ aging_sensor->measured_quot_diff ++ = quot_delta_scaled_sum / filtered_count; ++ cpr3_info(ctrl, "average quotient delta=%d (count=%d)\n", ++ aging_sensor->measured_quot_diff, ++ filtered_count); ++ } else { ++ cpr3_err(ctrl, "%d aging measurements completed after %d iterations\n", ++ aging_measurement_count, ++ CPR3_AGING_MEASUREMENT_ITERATIONS); ++ rc = -EBUSY; ++ } ++ ++cleanup: ++ kfree(quot_delta_results); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc2 = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc2) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc2); ++ rc = rc2; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_restore); ++ ++ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), ro_mask_restore); ++ ++ cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt0_restore); ++ cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt1_restore); ++ ++ if (ctrl->supports_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly_restore); ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, ++ up_down_dly_restore); ++ } else { ++ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, ++ cont_dly_restore); ++ } ++ ++ for (i = 0; i <= ctrl->sensor_count / 32; i++) { ++ cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), 0); ++ cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), 0); ++ } ++ ++ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, ++ CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK, ++ (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT) ++ | (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT)); ++ ++ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), ++ ctrl->sensor_owner[aging_sensor->sensor_id]); ++ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID); ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ ctrl->use_hw_closed_loop ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE ++ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ ctrl->use_hw_closed_loop ++ ? CPR3_HW_CLOSED_LOOP_ENABLE ++ : CPR3_HW_CLOSED_LOOP_DISABLE); ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_readjust_volt_and_quot() - readjust the target quotients as ++ * well as the floor, ceiling, and open-loop voltages for the ++ * regulator by removing the old adjustment and adding the new one ++ * @vreg: Pointer to the CPR3 regulator ++ * @old_adjust_volt: Old aging adjustment voltage in microvolts ++ * @new_adjust_volt: New aging adjustment voltage in microvolts ++ * ++ * Also reset the cached closed loop voltage (last_volt) to equal the open-loop ++ * voltage for each corner. ++ * ++ * Return: None ++ */ ++static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg, ++ int old_adjust_volt, int new_adjust_volt) ++{ ++ unsigned long long temp; ++ int i, j, old_volt, new_volt, rounded_volt; ++ ++ if (!vreg->aging_allowed) ++ return; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ temp = (unsigned long long)old_adjust_volt ++ * (unsigned long long)vreg->corner[i].aging_derate; ++ do_div(temp, 1000); ++ old_volt = temp; ++ ++ temp = (unsigned long long)new_adjust_volt ++ * (unsigned long long)vreg->corner[i].aging_derate; ++ do_div(temp, 1000); ++ new_volt = temp; ++ ++ old_volt = min(vreg->aging_max_adjust_volt, old_volt); ++ new_volt = min(vreg->aging_max_adjust_volt, new_volt); ++ ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j] != 0) { ++ vreg->corner[i].target_quot[j] ++ += cpr3_quot_adjustment( ++ vreg->corner[i].ro_scale[j], ++ new_volt) ++ - cpr3_quot_adjustment( ++ vreg->corner[i].ro_scale[j], ++ old_volt); ++ } ++ } ++ ++ rounded_volt = CPR3_ROUND(new_volt, ++ vreg->thread->ctrl->step_volt); ++ ++ if (!vreg->aging_allow_open_loop_adj) ++ rounded_volt = 0; ++ ++ vreg->corner[i].ceiling_volt ++ = vreg->corner[i].unaged_ceiling_volt + rounded_volt; ++ vreg->corner[i].ceiling_volt = min(vreg->corner[i].ceiling_volt, ++ vreg->corner[i].abs_ceiling_volt); ++ vreg->corner[i].floor_volt ++ = vreg->corner[i].unaged_floor_volt + rounded_volt; ++ vreg->corner[i].floor_volt = min(vreg->corner[i].floor_volt, ++ vreg->corner[i].ceiling_volt); ++ vreg->corner[i].open_loop_volt ++ = vreg->corner[i].unaged_open_loop_volt + rounded_volt; ++ vreg->corner[i].open_loop_volt ++ = min(vreg->corner[i].open_loop_volt, ++ vreg->corner[i].ceiling_volt); ++ ++ vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; ++ ++ cpr3_debug(vreg, "corner %d: applying %d uV closed-loop and %d uV open-loop voltage margin adjustment\n", ++ i, new_volt, rounded_volt); ++ } ++} ++ ++/** ++ * cpr3_regulator_set_aging_ref_adjustment() - adjust target quotients for the ++ * regulators managed by this CPR controller to account for aging ++ * @ctrl: Pointer to the CPR3 controller ++ * @ref_adjust_volt: New aging reference adjustment voltage in microvolts to ++ * apply to all regulators managed by this CPR controller ++ * ++ * The existing aging adjustment as defined by ctrl->aging_ref_adjust_volt is ++ * first removed and then the adjustment is applied. Lastly, the value of ++ * ctrl->aging_ref_adjust_volt is updated to ref_adjust_volt. ++ */ ++static void cpr3_regulator_set_aging_ref_adjustment( ++ struct cpr3_controller *ctrl, int ref_adjust_volt) ++{ ++ struct cpr3_regulator *vreg; ++ int i, j; ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ cpr3_regulator_readjust_volt_and_quot(vreg, ++ ctrl->aging_ref_adjust_volt, ref_adjust_volt); ++ } ++ } ++ ++ ctrl->aging_ref_adjust_volt = ref_adjust_volt; ++} ++ ++/** ++ * cpr3_regulator_aging_adjust() - adjust the target quotients for regulators ++ * based on the output of CPR aging sensors ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_aging_adjust(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_regulator *vreg; ++ struct cpr3_corner restore_aging_corner; ++ struct cpr3_corner *corner; ++ int *restore_current_corner; ++ bool *restore_vreg_enabled; ++ int i, j, id, rc, rc2, vreg_count, aging_volt, max_aging_volt = 0; ++ u32 reg; ++ ++ if (!ctrl->aging_required || !ctrl->cpr_enabled ++ || ctrl->aggr_corner.ceiling_volt == 0 ++ || ctrl->aggr_corner.ceiling_volt > ctrl->aging_ref_volt) ++ return 0; ++ ++ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ vreg_count++; ++ ++ if (vreg->aging_allowed && vreg->vreg_enabled ++ && vreg->current_corner > vreg->aging_corner) ++ return 0; ++ } ++ } ++ ++ /* Verify that none of the aging sensors are currently masked. */ ++ for (i = 0; i < ctrl->aging_sensor_count; i++) { ++ id = ctrl->aging_sensor[i].sensor_id; ++ reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id)); ++ if (reg & BIT(id % 32)) ++ return 0; ++ } ++ ++ /* ++ * Verify that the aging possible register (if specified) has an ++ * acceptable value. ++ */ ++ if (ctrl->aging_possible_reg) { ++ reg = readl_relaxed(ctrl->aging_possible_reg); ++ reg &= ctrl->aging_possible_mask; ++ if (reg != ctrl->aging_possible_val) ++ return 0; ++ } ++ ++ restore_current_corner = kcalloc(vreg_count, ++ sizeof(*restore_current_corner), GFP_KERNEL); ++ restore_vreg_enabled = kcalloc(vreg_count, ++ sizeof(*restore_vreg_enabled), GFP_KERNEL); ++ if (!restore_current_corner || !restore_vreg_enabled) { ++ kfree(restore_current_corner); ++ kfree(restore_vreg_enabled); ++ return -ENOMEM; ++ } ++ ++ /* Force all regulators to the aging corner */ ++ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ ++ restore_current_corner[vreg_count] ++ = vreg->current_corner; ++ restore_vreg_enabled[vreg_count] ++ = vreg->vreg_enabled; ++ ++ vreg->current_corner = vreg->aging_corner; ++ vreg->vreg_enabled = true; ++ } ++ } ++ ++ /* Force one of the regulators to require the aging reference voltage */ ++ vreg = &ctrl->thread[0].vreg[0]; ++ corner = &vreg->corner[vreg->current_corner]; ++ restore_aging_corner = *corner; ++ corner->ceiling_volt = ctrl->aging_ref_volt; ++ corner->floor_volt = ctrl->aging_ref_volt; ++ corner->open_loop_volt = ctrl->aging_ref_volt; ++ corner->last_volt = ctrl->aging_ref_volt; ++ ++ /* Skip last_volt caching */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ /* Set the vdd supply voltage to the aging reference voltage */ ++ rc = _cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "unable to force vdd-supply to the aging reference voltage=%d uV, rc=%d\n", ++ ctrl->aging_ref_volt, rc); ++ goto cleanup; ++ } ++ ++ if (ctrl->aging_vdd_mode) { ++ rc = regulator_set_mode(ctrl->vdd_regulator, ++ ctrl->aging_vdd_mode); ++ if (rc) { ++ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n", ++ ctrl->aging_vdd_mode, rc); ++ goto cleanup; ++ } ++ } ++ ++ /* Perform aging measurement on all aging sensors */ ++ for (i = 0; i < ctrl->aging_sensor_count; i++) { ++ for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) { ++ rc = cpr3_regulator_measure_aging(ctrl, ++ &ctrl->aging_sensor[i]); ++ if (!rc) ++ break; ++ } ++ ++ if (!rc) { ++ aging_volt = ++ cpr3_voltage_adjustment( ++ ctrl->aging_sensor[i].ro_scale, ++ ctrl->aging_sensor[i].measured_quot_diff ++ - ctrl->aging_sensor[i].init_quot_diff); ++ max_aging_volt = max(max_aging_volt, aging_volt); ++ } else { ++ cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n", ++ j, rc); ++ ctrl->aging_failed = true; ++ ctrl->aging_required = false; ++ goto cleanup; ++ } ++ } ++ ++cleanup: ++ vreg = &ctrl->thread[0].vreg[0]; ++ vreg->corner[vreg->current_corner] = restore_aging_corner; ++ ++ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ vreg->current_corner ++ = restore_current_corner[vreg_count]; ++ vreg->vreg_enabled = restore_vreg_enabled[vreg_count]; ++ } ++ } ++ ++ kfree(restore_current_corner); ++ kfree(restore_vreg_enabled); ++ ++ /* Adjust the CPR target quotients according to the aging measurement */ ++ if (!rc) { ++ cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt); ++ ++ cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n", ++ ctrl->aging_ref_adjust_volt); ++ ctrl->aging_succeeded = true; ++ ctrl->aging_required = false; ++ } ++ ++ if (ctrl->aging_complete_vdd_mode) { ++ rc = regulator_set_mode(ctrl->vdd_regulator, ++ ctrl->aging_complete_vdd_mode); ++ if (rc) ++ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n", ++ ctrl->aging_complete_vdd_mode, rc); ++ } ++ ++ /* Skip last_volt caching */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ /* ++ * Restore vdd-supply to the voltage before the aging measurement and ++ * restore the CPR3 controller hardware state. ++ */ ++ rc2 = _cpr3_regulator_update_ctrl_state(ctrl); ++ ++ /* Stop last_volt caching on for the next request */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ return rc ? rc : rc2; ++} ++ ++/** ++ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller ++ * to reflect the corners used by all CPR3 regulators as well as ++ * the CPR operating mode and perform aging adjustments if needed ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Note, CPR3 controller lock must be held by the caller. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = _cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) ++ return rc; ++ ++ return cpr3_regulator_aging_adjust(ctrl); ++} ++ ++/** ++ * cpr3_regulator_set_voltage() - set the voltage corner for the CPR3 regulator ++ * associated with the regulator device ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * @corner: New voltage corner to set (offset by CPR3_CORNER_OFFSET) ++ * @corner_max: Maximum voltage corner allowed (offset by ++ * CPR3_CORNER_OFFSET) ++ * @selector: Pointer which is filled with the selector value for the ++ * corner ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. The VDD voltage will not be ++ * physically configured until both this function and cpr3_regulator_enable() ++ * are called. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_set_voltage(struct regulator_dev *rdev, ++ int corner, int corner_max, unsigned *selector) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int rc = 0; ++ int last_corner; ++ ++ corner -= CPR3_CORNER_OFFSET; ++ corner_max -= CPR3_CORNER_OFFSET; ++ *selector = corner; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (!vreg->vreg_enabled) { ++ vreg->current_corner = corner; ++ cpr3_debug(vreg, "stored corner=%d\n", corner); ++ goto done; ++ } else if (vreg->current_corner == corner) { ++ goto done; ++ } ++ ++ last_corner = vreg->current_corner; ++ vreg->current_corner = corner; ++ ++ if (vreg->cpr4_regulator_data != NULL) ++ if (vreg->cpr4_regulator_data->mem_acc_funcs != NULL) ++ vreg->cpr4_regulator_data->mem_acc_funcs->set_mem_acc(rdev); ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); ++ vreg->current_corner = last_corner; ++ } ++ ++ if (vreg->cpr4_regulator_data != NULL) ++ if (vreg->cpr4_regulator_data->mem_acc_funcs != NULL) ++ vreg->cpr4_regulator_data->mem_acc_funcs->clear_mem_acc(rdev); ++ ++ cpr3_debug(vreg, "set corner=%d\n", corner); ++done: ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_handle_temp_open_loop_adjustment() - voltage based cold temperature ++ * ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * @is_cold: Flag to denote enter/exit cold condition ++ * ++ * This function is adjusts voltage margin based on cold condition ++ * ++ * Return: 0 = success ++ */ ++ ++int cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, ++ bool is_cold) ++{ ++ int i ,j, k, rc; ++ struct cpr3_regulator *vreg; ++ ++ mutex_lock(&ctrl->lock); ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ for (k = 0; k < vreg->corner_count; k++) { ++ vreg->corner[k].open_loop_volt = is_cold ? ++ vreg->corner[k].cold_temp_open_loop_volt : ++ vreg->corner[k].normal_temp_open_loop_volt; ++ } ++ } ++ } ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_get_voltage() - get the voltage corner for the CPR3 regulator ++ * associated with the regulator device ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: voltage corner value offset by CPR3_CORNER_OFFSET ++ */ ++static int cpr3_regulator_get_voltage(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ if (vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID) ++ return CPR3_CORNER_OFFSET; ++ else ++ return vreg->current_corner + CPR3_CORNER_OFFSET; ++} ++ ++/** ++ * cpr3_regulator_list_voltage() - return the voltage corner mapped to the ++ * specified selector ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * @selector: Regulator selector ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: voltage corner value offset by CPR3_CORNER_OFFSET ++ */ ++static int cpr3_regulator_list_voltage(struct regulator_dev *rdev, ++ unsigned selector) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ if (selector < vreg->corner_count) ++ return selector + CPR3_CORNER_OFFSET; ++ else ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_is_enabled() - return the enable state of the CPR3 regulator ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: true if regulator is enabled, false if regulator is disabled ++ */ ++static int cpr3_regulator_is_enabled(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ return vreg->vreg_enabled; ++} ++ ++/** ++ * cpr3_regulator_enable() - enable the CPR3 regulator ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_enable(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int rc = 0; ++ ++ if (vreg->vreg_enabled == true) ++ return 0; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->system_regulator) { ++ rc = regulator_enable(ctrl->system_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_enable(system) failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ rc = regulator_enable(ctrl->vdd_regulator); ++ if (rc) { ++ cpr3_err(vreg, "regulator_enable(vdd) failed, rc=%d\n", rc); ++ goto done; ++ } ++ ++ vreg->vreg_enabled = true; ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); ++ regulator_disable(ctrl->vdd_regulator); ++ vreg->vreg_enabled = false; ++ goto done; ++ } ++ ++ cpr3_debug(vreg, "Enabled\n"); ++done: ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_disable() - disable the CPR3 regulator ++ * @rdev: Regulator device pointer for the cpr3-regulator ++ * ++ * This function is passed as a callback function into the regulator ops that ++ * are registered for each cpr3-regulator device. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_disable(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int rc, rc2; ++ ++ if (vreg->vreg_enabled == false) ++ return 0; ++ ++ mutex_lock(&ctrl->lock); ++ rc = regulator_disable(ctrl->vdd_regulator); ++ if (rc) { ++ cpr3_err(vreg, "regulator_disable(vdd) failed, rc=%d\n", rc); ++ goto done; ++ } ++ ++ vreg->vreg_enabled = false; ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); ++ rc2 = regulator_enable(ctrl->vdd_regulator); ++ vreg->vreg_enabled = true; ++ goto done; ++ } ++ ++ if (ctrl->system_regulator) { ++ rc = regulator_disable(ctrl->system_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "regulator_disable(system) failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ cpr3_debug(vreg, "Disabled\n"); ++done: ++ mutex_unlock(&ctrl->lock); ++ ++ return rc; ++} ++ ++static struct regulator_ops cpr3_regulator_ops = { ++ .enable = cpr3_regulator_enable, ++ .disable = cpr3_regulator_disable, ++ .is_enabled = cpr3_regulator_is_enabled, ++ .set_voltage = cpr3_regulator_set_voltage, ++ .get_voltage = cpr3_regulator_get_voltage, ++ .list_voltage = cpr3_regulator_list_voltage, ++}; ++ ++/** ++ * cpr3_print_result() - print CPR measurement results to the kernel log for ++ * debugging purposes ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: None ++ */ ++static void cpr3_print_result(struct cpr3_thread *thread) ++{ ++ struct cpr3_controller *ctrl = thread->ctrl; ++ u32 result[3], busy, step_dn, step_up, error_steps, error, negative; ++ u32 quot_min, quot_max, ro_min, ro_max, step_quot_min, step_quot_max; ++ u32 sensor_min, sensor_max; ++ char *sign; ++ ++ result[0] = cpr3_read(ctrl, CPR3_REG_RESULT0(thread->thread_id)); ++ result[1] = cpr3_read(ctrl, CPR3_REG_RESULT1(thread->thread_id)); ++ result[2] = cpr3_read(ctrl, CPR3_REG_RESULT2(thread->thread_id)); ++ ++ busy = !!(result[0] & CPR3_RESULT0_BUSY_MASK); ++ step_dn = !!(result[0] & CPR3_RESULT0_STEP_DN_MASK); ++ step_up = !!(result[0] & CPR3_RESULT0_STEP_UP_MASK); ++ error_steps = (result[0] & CPR3_RESULT0_ERROR_STEPS_MASK) ++ >> CPR3_RESULT0_ERROR_STEPS_SHIFT; ++ error = (result[0] & CPR3_RESULT0_ERROR_MASK) ++ >> CPR3_RESULT0_ERROR_SHIFT; ++ negative = !!(result[0] & CPR3_RESULT0_NEGATIVE_MASK); ++ ++ quot_min = (result[1] & CPR3_RESULT1_QUOT_MIN_MASK) ++ >> CPR3_RESULT1_QUOT_MIN_SHIFT; ++ quot_max = (result[1] & CPR3_RESULT1_QUOT_MAX_MASK) ++ >> CPR3_RESULT1_QUOT_MAX_SHIFT; ++ ro_min = (result[1] & CPR3_RESULT1_RO_MIN_MASK) ++ >> CPR3_RESULT1_RO_MIN_SHIFT; ++ ro_max = (result[1] & CPR3_RESULT1_RO_MAX_MASK) ++ >> CPR3_RESULT1_RO_MAX_SHIFT; ++ ++ step_quot_min = (result[2] & CPR3_RESULT2_STEP_QUOT_MIN_MASK) ++ >> CPR3_RESULT2_STEP_QUOT_MIN_SHIFT; ++ step_quot_max = (result[2] & CPR3_RESULT2_STEP_QUOT_MAX_MASK) ++ >> CPR3_RESULT2_STEP_QUOT_MAX_SHIFT; ++ sensor_min = (result[2] & CPR3_RESULT2_SENSOR_MIN_MASK) ++ >> CPR3_RESULT2_SENSOR_MIN_SHIFT; ++ sensor_max = (result[2] & CPR3_RESULT2_SENSOR_MAX_MASK) ++ >> CPR3_RESULT2_SENSOR_MAX_SHIFT; ++ ++ sign = negative ? "-" : ""; ++ cpr3_debug(ctrl, "thread %u: busy=%u, step_dn=%u, step_up=%u, error_steps=%s%u, error=%s%u\n", ++ thread->thread_id, busy, step_dn, step_up, sign, error_steps, ++ sign, error); ++ cpr3_debug(ctrl, "thread %u: quot_min=%u, quot_max=%u, ro_min=%u, ro_max=%u\n", ++ thread->thread_id, quot_min, quot_max, ro_min, ro_max); ++ cpr3_debug(ctrl, "thread %u: step_quot_min=%u, step_quot_max=%u, sensor_min=%u, sensor_max=%u\n", ++ thread->thread_id, step_quot_min, step_quot_max, sensor_min, ++ sensor_max); ++} ++ ++/** ++ * cpr3_thread_busy() - returns if the specified CPR3 thread is busy taking ++ * a measurement ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: CPR3 busy status ++ */ ++static bool cpr3_thread_busy(struct cpr3_thread *thread) ++{ ++ u32 result; ++ ++ result = cpr3_read(thread->ctrl, CPR3_REG_RESULT0(thread->thread_id)); ++ ++ return !!(result & CPR3_RESULT0_BUSY_MASK); ++} ++ ++/** ++ * cpr3_irq_handler() - CPR interrupt handler callback function used for ++ * software closed-loop operation ++ * @irq: CPR interrupt number ++ * @data: Private data corresponding to the CPR3 controller ++ * pointer ++ * ++ * This function increases or decreases the vdd supply voltage based upon the ++ * CPR controller recommendation. ++ * ++ * Return: IRQ_HANDLED ++ */ ++static irqreturn_t cpr3_irq_handler(int irq, void *data) ++{ ++ struct cpr3_controller *ctrl = data; ++ struct cpr3_corner *aggr = &ctrl->aggr_corner; ++ u32 cont = CPR3_CONT_CMD_NACK; ++ u32 reg_last_measurement = 0; ++ struct cpr3_regulator *vreg; ++ struct cpr3_corner *corner; ++ unsigned long flags; ++ int i, j, new_volt, last_volt, dynamic_floor_volt, rc; ++ u32 irq_en, status, cpr_status, ctl; ++ bool up, down; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (!ctrl->cpr_enabled) { ++ cpr3_debug(ctrl, "CPR interrupt received but CPR is disabled\n"); ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++ } else if (ctrl->use_hw_closed_loop) { ++ cpr3_debug(ctrl, "CPR interrupt received but CPR is using HW closed-loop\n"); ++ goto done; ++ } ++ ++ /* ++ * CPR IRQ status checking and CPR controller disabling must happen ++ * atomically and without invening delay in order to avoid an interrupt ++ * storm caused by the handler racing with the CPR controller. ++ */ ++ local_irq_save(flags); ++ preempt_disable(); ++ ++ status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS); ++ up = status & CPR3_IRQ_UP; ++ down = status & CPR3_IRQ_DOWN; ++ ++ if (!up && !down) { ++ /* ++ * Toggle the CPR controller off and then back on since the ++ * hardware and software states are out of sync. This condition ++ * occurs after an aging measurement completes as the CPR IRQ ++ * physically triggers during the aging measurement but the ++ * handler is stuck waiting on the mutex lock. ++ */ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ local_irq_restore(flags); ++ preempt_enable(); ++ ++ /* Wait for the loop disable write to complete */ ++ mb(); ++ ++ /* Wait for BUSY=1 and LOOP_EN=0 in CPR controller registers. */ ++ for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) { ++ cpr_status = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); ++ ctl = cpr3_read(ctrl, CPR3_REG_CPR_CTL); ++ if (cpr_status & CPR3_CPR_STATUS_BUSY_MASK ++ && (ctl & CPR3_CPR_CTL_LOOP_EN_MASK) ++ == CPR3_CPR_CTL_LOOP_DISABLE) ++ break; ++ udelay(10); ++ } ++ if (i == CPR3_REGISTER_WRITE_DELAY_US / 10) ++ cpr3_debug(ctrl, "CPR controller not disabled after %d us\n", ++ CPR3_REGISTER_WRITE_DELAY_US); ++ ++ /* Clear interrupt status */ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, ++ CPR3_IRQ_UP | CPR3_IRQ_DOWN); ++ ++ /* Wait for the interrupt clearing write to complete */ ++ mb(); ++ ++ /* Wait for IRQ_STATUS register to be cleared. */ ++ for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) { ++ status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS); ++ if (!(status & (CPR3_IRQ_UP | CPR3_IRQ_DOWN))) ++ break; ++ udelay(10); ++ } ++ if (i == CPR3_REGISTER_WRITE_DELAY_US / 10) ++ cpr3_debug(ctrl, "CPR interrupts not cleared after %d us\n", ++ CPR3_REGISTER_WRITE_DELAY_US); ++ ++ cpr3_ctrl_loop_enable(ctrl); ++ ++ cpr3_debug(ctrl, "CPR interrupt received but no up or down status bit is set\n"); ++ ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++ } else if (up && down) { ++ cpr3_debug(ctrl, "both up and down status bits set\n"); ++ /* The up flag takes precedence over the down flag. */ ++ down = false; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) ++ reg_last_measurement ++ = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT); ++ dynamic_floor_volt = cpr3_regulator_get_dynamic_floor_volt(ctrl, ++ reg_last_measurement); ++ ++ local_irq_restore(flags); ++ preempt_enable(); ++ ++ irq_en = aggr->irq_en; ++ last_volt = aggr->last_volt; ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ if (cpr3_thread_busy(&ctrl->thread[i])) { ++ cpr3_debug(ctrl, "CPR thread %u busy when it should be waiting for SW cont\n", ++ ctrl->thread[i].thread_id); ++ goto done; ++ } ++ } ++ ++ new_volt = up ? last_volt + ctrl->step_volt ++ : last_volt - ctrl->step_volt; ++ ++ /* Re-enable UP/DOWN interrupt when its opposite is received. */ ++ irq_en |= up ? CPR3_IRQ_DOWN : CPR3_IRQ_UP; ++ ++ if (new_volt > aggr->ceiling_volt) { ++ new_volt = aggr->ceiling_volt; ++ irq_en &= ~CPR3_IRQ_UP; ++ cpr3_debug(ctrl, "limiting to ceiling=%d uV\n", ++ aggr->ceiling_volt); ++ } else if (new_volt < aggr->floor_volt) { ++ new_volt = aggr->floor_volt; ++ irq_en &= ~CPR3_IRQ_DOWN; ++ cpr3_debug(ctrl, "limiting to floor=%d uV\n", aggr->floor_volt); ++ } ++ ++ if (down && new_volt < dynamic_floor_volt) { ++ /* ++ * The vdd-supply voltage should not be decreased below the ++ * dynamic floor voltage. However, it is not necessary (and ++ * counter productive) to force the voltage up to this level ++ * if it happened to be below it since the closed-loop voltage ++ * must have gotten there in a safe manner while the power ++ * domains for the CPR3 regulator imposing the dynamic floor ++ * were not bypassed. ++ */ ++ new_volt = last_volt; ++ irq_en &= ~CPR3_IRQ_DOWN; ++ cpr3_debug(ctrl, "limiting to dynamic floor=%d uV\n", ++ dynamic_floor_volt); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ cpr3_print_result(&ctrl->thread[i]); ++ ++ cpr3_debug(ctrl, "%s: new_volt=%d uV, last_volt=%d uV\n", ++ up ? "UP" : "DN", new_volt, last_volt); ++ ++ if (ctrl->proc_clock_throttle && last_volt == aggr->ceiling_volt ++ && new_volt < last_volt) ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ ctrl->proc_clock_throttle); ++ ++ if (new_volt != last_volt) { ++ rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt, ++ last_volt, ++ aggr); ++ if (rc) { ++ cpr3_err(ctrl, "scale_vdd() failed to set vdd=%d uV, rc=%d\n", ++ new_volt, rc); ++ goto done; ++ } ++ cont = CPR3_CONT_CMD_ACK; ++ ++ /* ++ * Update the closed-loop voltage for all regulators managed ++ * by this CPR controller. ++ */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ cpr3_update_vreg_closed_loop_volt(vreg, ++ new_volt, reg_last_measurement); ++ } ++ } ++ } ++ ++ if (ctrl->proc_clock_throttle && new_volt == aggr->ceiling_volt) ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ ++ corner = &ctrl->thread[0].vreg[0].corner[ ++ ctrl->thread[0].vreg[0].current_corner]; ++ ++ if (irq_en != aggr->irq_en) { ++ aggr->irq_en = irq_en; ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_en); ++ } ++ ++ aggr->last_volt = new_volt; ++ ++done: ++ /* Clear interrupt status */ ++ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_UP | CPR3_IRQ_DOWN); ++ ++ /* ACK or NACK the CPR controller */ ++ cpr3_write(ctrl, CPR3_REG_CONT_CMD, cont); ++ ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * cpr3_ceiling_irq_handler() - CPR ceiling reached interrupt handler callback ++ * function used for hardware closed-loop operation ++ * @irq: CPR ceiling interrupt number ++ * @data: Private data corresponding to the CPR3 controller ++ * pointer ++ * ++ * This function disables processor clock throttling and closed-loop operation ++ * when the ceiling voltage is reached. ++ * ++ * Return: IRQ_HANDLED ++ */ ++static irqreturn_t cpr3_ceiling_irq_handler(int irq, void *data) ++{ ++ struct cpr3_controller *ctrl = data; ++ int volt; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (!ctrl->cpr_enabled) { ++ cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is disabled\n"); ++ goto done; ++ } else if (!ctrl->use_hw_closed_loop) { ++ cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is using SW closed-loop\n"); ++ goto done; ++ } ++ ++ volt = regulator_get_voltage(ctrl->vdd_regulator); ++ if (volt < 0) { ++ cpr3_err(ctrl, "could not get vdd voltage, rc=%d\n", volt); ++ goto done; ++ } else if (volt != ctrl->aggr_corner.ceiling_volt) { ++ cpr3_debug(ctrl, "CPR ceiling interrupt received but vdd voltage: %d uV != ceiling voltage: %d uV\n", ++ volt, ctrl->aggr_corner.ceiling_volt); ++ goto done; ++ } ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ /* ++ * Since the ceiling voltage has been reached, disable processor ++ * clock throttling as well as CPR closed-loop operation. ++ */ ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ cpr3_ctrl_loop_disable(ctrl); ++ cpr3_debug(ctrl, "CPR closed-loop and throttling disabled\n"); ++ } ++ ++done: ++ mutex_unlock(&ctrl->lock); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * cpr3_regulator_vreg_register() - register a regulator device for a CPR3 ++ * regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function initializes all regulator framework related structures and then ++ * calls regulator_register() for the CPR3 regulator. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_vreg_register(struct cpr3_regulator *vreg) ++{ ++ struct regulator_config config = {}; ++ struct regulator_desc *rdesc; ++ struct regulator_init_data *init_data; ++ int rc; ++ ++ init_data = of_get_regulator_init_data(vreg->thread->ctrl->dev, ++ vreg->of_node, &vreg->rdesc); ++ if (!init_data) { ++ cpr3_err(vreg, "regulator init data is missing\n"); ++ return -EINVAL; ++ } ++ ++ init_data->constraints.input_uV = init_data->constraints.max_uV; ++ rdesc = &vreg->rdesc; ++ init_data->constraints.valid_ops_mask |= ++ REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS; ++ rdesc->ops = &cpr3_regulator_ops; ++ ++ rdesc->n_voltages = vreg->corner_count; ++ rdesc->name = init_data->constraints.name; ++ rdesc->owner = THIS_MODULE; ++ rdesc->type = REGULATOR_VOLTAGE; ++ ++ config.dev = vreg->thread->ctrl->dev; ++ config.driver_data = vreg; ++ config.init_data = init_data; ++ config.of_node = vreg->of_node; ++ ++ vreg->rdev = regulator_register(rdesc, &config); ++ if (IS_ERR(vreg->rdev)) { ++ rc = PTR_ERR(vreg->rdev); ++ cpr3_err(vreg, "regulator_register failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int debugfs_int_set(void *data, u64 val) ++{ ++ *(int *)data = val; ++ return 0; ++} ++ ++static int debugfs_int_get(void *data, u64 *val) ++{ ++ *val = *(int *)data; ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(fops_int, debugfs_int_get, debugfs_int_set, "%lld\n"); ++DEFINE_SIMPLE_ATTRIBUTE(fops_int_ro, debugfs_int_get, NULL, "%lld\n"); ++DEFINE_SIMPLE_ATTRIBUTE(fops_int_wo, NULL, debugfs_int_set, "%lld\n"); ++ ++/** ++ * debugfs_create_int - create a debugfs file that is used to read and write a ++ * signed int value ++ * @name: Pointer to a string containing the name of the file to ++ * create ++ * @mode: The permissions that the file should have ++ * @parent: Pointer to the parent dentry for this file. This should ++ * be a directory dentry if set. If this parameter is ++ * %NULL, then the file will be created in the root of the ++ * debugfs filesystem. ++ * @value: Pointer to the variable that the file should read to and ++ * write from ++ * ++ * This function creates a file in debugfs with the given name that ++ * contains the value of the variable @value. If the @mode variable is so ++ * set, it can be read from, and written to. ++ * ++ * This function will return a pointer to a dentry if it succeeds. This ++ * pointer must be passed to the debugfs_remove() function when the file is ++ * to be removed. If an error occurs, %NULL will be returned. ++ */ ++static struct dentry *debugfs_create_int(const char *name, umode_t mode, ++ struct dentry *parent, int *value) ++{ ++ /* if there are no write bits set, make read only */ ++ if (!(mode & S_IWUGO)) ++ return debugfs_create_file(name, mode, parent, value, ++ &fops_int_ro); ++ /* if there are no read bits set, make write only */ ++ if (!(mode & S_IRUGO)) ++ return debugfs_create_file(name, mode, parent, value, ++ &fops_int_wo); ++ ++ return debugfs_create_file(name, mode, parent, value, &fops_int); ++} ++ ++static int debugfs_bool_get(void *data, u64 *val) ++{ ++ *val = *(bool *)data; ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%lld\n"); ++ ++/** ++ * struct cpr3_debug_corner_info - data structure used by the ++ * cpr3_debugfs_create_corner_int function ++ * @vreg: Pointer to the CPR3 regulator ++ * @index: Pointer to the corner array index ++ * @member_offset: Offset in bytes from the beginning of struct cpr3_corner ++ * to the beginning of the value to be read from ++ * @corner: Pointer to the CPR3 corner array ++ */ ++struct cpr3_debug_corner_info { ++ struct cpr3_regulator *vreg; ++ int *index; ++ size_t member_offset; ++ struct cpr3_corner *corner; ++}; ++ ++static int cpr3_debug_corner_int_get(void *data, u64 *val) ++{ ++ struct cpr3_debug_corner_info *info = data; ++ struct cpr3_controller *ctrl = info->vreg->thread->ctrl; ++ int i; ++ ++ mutex_lock(&ctrl->lock); ++ ++ i = *info->index; ++ if (i < 0) ++ i = 0; ++ ++ *val = *(int *)((char *)&info->vreg->corner[i] + info->member_offset); ++ ++ mutex_unlock(&ctrl->lock); ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_int_fops, cpr3_debug_corner_int_get, ++ NULL, "%lld\n"); ++ ++/** ++ * cpr3_debugfs_create_corner_int - create a debugfs file that is used to read ++ * a signed int value out of a CPR3 regulator's corner array ++ * @vreg: Pointer to the CPR3 regulator ++ * @name: Pointer to a string containing the name of the file to ++ * create ++ * @mode: The permissions that the file should have ++ * @parent: Pointer to the parent dentry for this file. This should ++ * be a directory dentry if set. If this parameter is ++ * %NULL, then the file will be created in the root of the ++ * debugfs filesystem. ++ * @index: Pointer to the corner array index ++ * @member_offset: Offset in bytes from the beginning of struct cpr3_corner ++ * to the beginning of the value to be read from ++ * ++ * This function creates a file in debugfs with the given name that ++ * contains the value of the int type variable vreg->corner[index].member ++ * where member_offset == offsetof(struct cpr3_corner, member). ++ */ ++static struct dentry *cpr3_debugfs_create_corner_int( ++ struct cpr3_regulator *vreg, const char *name, umode_t mode, ++ struct dentry *parent, int *index, size_t member_offset) ++{ ++ struct cpr3_debug_corner_info *info; ++ ++ info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return NULL; ++ ++ info->vreg = vreg; ++ info->index = index; ++ info->member_offset = member_offset; ++ ++ return debugfs_create_file(name, mode, parent, info, ++ &cpr3_debug_corner_int_fops); ++} ++ ++static int cpr3_debug_quot_open(struct inode *inode, struct file *file) ++{ ++ struct cpr3_debug_corner_info *info = inode->i_private; ++ struct cpr3_thread *thread = info->vreg->thread; ++ int size, i, pos; ++ u32 *quot; ++ char *buf; ++ ++ /* ++ * Max size: ++ * - 10 digits + ' ' or '\n' = 11 bytes per number ++ * - terminating '\0' ++ */ ++ size = CPR3_RO_COUNT * 11; ++ buf = kzalloc(size + 1, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ file->private_data = buf; ++ ++ mutex_lock(&thread->ctrl->lock); ++ ++ quot = info->corner[*info->index].target_quot; ++ ++ for (i = 0, pos = 0; i < CPR3_RO_COUNT; i++) ++ pos += scnprintf(buf + pos, size - pos, "%u%c", ++ quot[i], i < CPR3_RO_COUNT - 1 ? ' ' : '\n'); ++ ++ mutex_unlock(&thread->ctrl->lock); ++ ++ return nonseekable_open(inode, file); ++} ++ ++static ssize_t cpr3_debug_quot_read(struct file *file, char __user *buf, ++ size_t len, loff_t *ppos) ++{ ++ return simple_read_from_buffer(buf, len, ppos, file->private_data, ++ strlen(file->private_data)); ++} ++ ++static int cpr3_debug_quot_release(struct inode *inode, struct file *file) ++{ ++ kfree(file->private_data); ++ ++ return 0; ++} ++ ++static const struct file_operations cpr3_debug_quot_fops = { ++ .owner = THIS_MODULE, ++ .open = cpr3_debug_quot_open, ++ .release = cpr3_debug_quot_release, ++ .read = cpr3_debug_quot_read, ++ .llseek = no_llseek, ++}; ++ ++/** ++ * cpr3_regulator_debugfs_corner_add() - add debugfs files to expose ++ * configuration data for the CPR corner ++ * @vreg: Pointer to the CPR3 regulator ++ * @corner_dir: Pointer to the parent corner dentry for the new files ++ * @index: Pointer to the corner array index ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_corner_add(struct cpr3_regulator *vreg, ++ struct dentry *corner_dir, int *index) ++{ ++ struct cpr3_debug_corner_info *info; ++ struct dentry *temp; ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "floor_volt", S_IRUGO, ++ corner_dir, index, offsetof(struct cpr3_corner, floor_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "floor_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "ceiling_volt", S_IRUGO, ++ corner_dir, index, offsetof(struct cpr3_corner, ceiling_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "ceiling_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "open_loop_volt", S_IRUGO, ++ corner_dir, index, ++ offsetof(struct cpr3_corner, open_loop_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "open_loop_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = cpr3_debugfs_create_corner_int(vreg, "last_volt", S_IRUGO, ++ corner_dir, index, offsetof(struct cpr3_corner, last_volt)); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "last_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return; ++ ++ info->vreg = vreg; ++ info->index = index; ++ info->corner = vreg->corner; ++ ++ temp = debugfs_create_file("target_quots", S_IRUGO, corner_dir, ++ info, &cpr3_debug_quot_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "target_quots debugfs file creation failed\n"); ++ return; ++ } ++} ++ ++/** ++ * cpr3_debug_corner_index_set() - debugfs callback used to change the ++ * value of the CPR3 regulator debug_corner index ++ * @data: Pointer to private data which is equal to the CPR3 ++ * regulator pointer ++ * @val: New value for debug_corner ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_corner_index_set(void *data, u64 val) ++{ ++ struct cpr3_regulator *vreg = data; ++ ++ if (val < CPR3_CORNER_OFFSET || val > vreg->corner_count) { ++ cpr3_err(vreg, "invalid corner index %llu; allowed values: %d-%d\n", ++ val, CPR3_CORNER_OFFSET, vreg->corner_count); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&vreg->thread->ctrl->lock); ++ vreg->debug_corner = val - CPR3_CORNER_OFFSET; ++ mutex_unlock(&vreg->thread->ctrl->lock); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_debug_corner_index_get() - debugfs callback used to retrieve ++ * the value of the CPR3 regulator debug_corner index ++ * @data: Pointer to private data which is equal to the CPR3 ++ * regulator pointer ++ * @val: Output parameter written with the value of ++ * debug_corner ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_corner_index_get(void *data, u64 *val) ++{ ++ struct cpr3_regulator *vreg = data; ++ ++ *val = vreg->debug_corner + CPR3_CORNER_OFFSET; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_index_fops, ++ cpr3_debug_corner_index_get, ++ cpr3_debug_corner_index_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_debug_current_corner_index_get() - debugfs callback used to retrieve ++ * the value of the CPR3 regulator current_corner index ++ * @data: Pointer to private data which is equal to the CPR3 ++ * regulator pointer ++ * @val: Output parameter written with the value of ++ * current_corner ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_current_corner_index_get(void *data, u64 *val) ++{ ++ struct cpr3_regulator *vreg = data; ++ ++ *val = vreg->current_corner + CPR3_CORNER_OFFSET; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_current_corner_index_fops, ++ cpr3_debug_current_corner_index_get, ++ NULL, "%llu\n"); ++ ++/** ++ * cpr3_regulator_debugfs_vreg_add() - add debugfs files to expose configuration ++ * data for the CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * @thread_dir CPR3 thread debugfs directory handle ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_vreg_add(struct cpr3_regulator *vreg, ++ struct dentry *thread_dir) ++{ ++ struct dentry *temp, *corner_dir, *vreg_dir; ++ ++ vreg_dir = debugfs_create_dir(vreg->name, thread_dir); ++ if (IS_ERR_OR_NULL(vreg_dir)) { ++ cpr3_err(vreg, "%s debugfs directory creation failed\n", ++ vreg->name); ++ return; ++ } ++ ++ temp = debugfs_create_int("speed_bin_fuse", S_IRUGO, vreg_dir, ++ &vreg->speed_bin_fuse); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "speed_bin_fuse debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("cpr_rev_fuse", S_IRUGO, vreg_dir, ++ &vreg->cpr_rev_fuse); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "cpr_rev_fuse debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("fuse_combo", S_IRUGO, vreg_dir, ++ &vreg->fuse_combo); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "fuse_combo debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("corner_count", S_IRUGO, vreg_dir, ++ &vreg->corner_count); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "corner_count debugfs file creation failed\n"); ++ return; ++ } ++ ++ corner_dir = debugfs_create_dir("corner", vreg_dir); ++ if (IS_ERR_OR_NULL(corner_dir)) { ++ cpr3_err(vreg, "corner debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("index", S_IRUGO | S_IWUSR, corner_dir, ++ vreg, &cpr3_debug_corner_index_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "index debugfs file creation failed\n"); ++ return; ++ } ++ ++ cpr3_regulator_debugfs_corner_add(vreg, corner_dir, ++ &vreg->debug_corner); ++ ++ corner_dir = debugfs_create_dir("current_corner", vreg_dir); ++ if (IS_ERR_OR_NULL(corner_dir)) { ++ cpr3_err(vreg, "current_corner debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("index", S_IRUGO, corner_dir, ++ vreg, &cpr3_debug_current_corner_index_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(vreg, "index debugfs file creation failed\n"); ++ return; ++ } ++ ++ cpr3_regulator_debugfs_corner_add(vreg, corner_dir, ++ &vreg->current_corner); ++} ++ ++/** ++ * cpr3_regulator_debugfs_thread_add() - add debugfs files to expose ++ * configuration data for the CPR thread ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_thread_add(struct cpr3_thread *thread) ++{ ++ struct cpr3_controller *ctrl = thread->ctrl; ++ struct dentry *aggr_dir, *temp, *thread_dir; ++ struct cpr3_debug_corner_info *info; ++ char buf[20]; ++ int *index; ++ int i; ++ ++ scnprintf(buf, sizeof(buf), "thread%u", thread->thread_id); ++ thread_dir = debugfs_create_dir(buf, thread->ctrl->debugfs); ++ if (IS_ERR_OR_NULL(thread_dir)) { ++ cpr3_err(ctrl, "thread %u %s debugfs directory creation failed\n", ++ thread->thread_id, buf); ++ return; ++ } ++ ++ aggr_dir = debugfs_create_dir("max_aggregated_params", thread_dir); ++ if (IS_ERR_OR_NULL(aggr_dir)) { ++ cpr3_err(ctrl, "thread %u max_aggregated_params debugfs directory creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("floor_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.floor_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr floor_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("ceiling_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.ceiling_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr ceiling_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("open_loop_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.open_loop_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr open_loop_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ temp = debugfs_create_int("last_volt", S_IRUGO, aggr_dir, ++ &thread->aggr_corner.last_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u aggr last_volt debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ info = devm_kzalloc(thread->ctrl->dev, sizeof(*info), GFP_KERNEL); ++ index = devm_kzalloc(thread->ctrl->dev, sizeof(*index), GFP_KERNEL); ++ if (!info || !index) ++ return; ++ *index = 0; ++ info->vreg = &thread->vreg[0]; ++ info->index = index; ++ info->corner = &thread->aggr_corner; ++ ++ temp = debugfs_create_file("target_quots", S_IRUGO, aggr_dir, ++ info, &cpr3_debug_quot_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread %u target_quots debugfs file creation failed\n", ++ thread->thread_id); ++ return; ++ } ++ ++ for (i = 0; i < thread->vreg_count; i++) ++ cpr3_regulator_debugfs_vreg_add(&thread->vreg[i], thread_dir); ++} ++ ++/** ++ * cpr3_debug_closed_loop_enable_set() - debugfs callback used to change the ++ * value of the CPR controller cpr_allowed_sw flag which enables or ++ * disables closed-loop operation ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: New value for cpr_allowed_sw ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_closed_loop_enable_set(void *data, u64 val) ++{ ++ struct cpr3_controller *ctrl = data; ++ bool enable = !!val; ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->cpr_allowed_sw == enable) ++ goto done; ++ ++ if (enable && !ctrl->cpr_allowed_hw) { ++ cpr3_err(ctrl, "CPR closed-loop operation is not allowed\n"); ++ goto done; ++ } ++ ++ ctrl->cpr_allowed_sw = enable; ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not change CPR enable state=%u, rc=%d\n", ++ enable, rc); ++ goto done; ++ } ++ ++ if (ctrl->proc_clock_throttle && !ctrl->cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ctrl->cpr_enabled = true; ++ ++ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, ++ CPR3_PD_THROTTLE_DISABLE); ++ ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ cpr3_debug(ctrl, "closed-loop=%s\n", enable ? "enabled" : "disabled"); ++done: ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_debug_closed_loop_enable_get() - debugfs callback used to retrieve ++ * the value of the CPR controller cpr_allowed_sw flag which ++ * indicates if closed-loop operation is enabled ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: Output parameter written with the value of ++ * cpr_allowed_sw ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_closed_loop_enable_get(void *data, u64 *val) ++{ ++ struct cpr3_controller *ctrl = data; ++ ++ *val = ctrl->cpr_allowed_sw; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_closed_loop_enable_fops, ++ cpr3_debug_closed_loop_enable_get, ++ cpr3_debug_closed_loop_enable_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_debug_hw_closed_loop_enable_set() - debugfs callback used to change the ++ * value of the CPR controller use_hw_closed_loop flag which ++ * switches between software closed-loop and hardware closed-loop ++ * operation for CPR3 and CPR4 controllers and between open-loop ++ * and full hardware closed-loop operation for CPRh controllers. ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: New value for use_hw_closed_loop ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) ++{ ++ struct cpr3_controller *ctrl = data; ++ bool use_hw_closed_loop = !!val; ++ struct cpr3_regulator *vreg; ++ bool cpr_enabled; ++ int i, j, k, rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->use_hw_closed_loop == use_hw_closed_loop) ++ goto done; ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ ctrl->use_hw_closed_loop = use_hw_closed_loop; ++ ++ cpr_enabled = ctrl->cpr_enabled; ++ ++ /* Ensure that CPR clocks are enabled before writing to registers. */ ++ if (!cpr_enabled) { ++ rc = cpr3_clock_enable(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); ++ goto done; ++ } ++ ctrl->cpr_enabled = true; ++ } ++ ++ if (ctrl->use_hw_closed_loop) ++ cpr3_write(ctrl, CPR3_REG_IRQ_EN, 0); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, ++ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, ++ ctrl->use_hw_closed_loop ++ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE ++ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); ++ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ++ ctrl->use_hw_closed_loop ++ ? CPR3_HW_CLOSED_LOOP_ENABLE ++ : CPR3_HW_CLOSED_LOOP_DISABLE); ++ } ++ ++ /* Turn off CPR clocks if they were off before this function call. */ ++ if (!cpr_enabled) { ++ cpr3_clock_disable(ctrl); ++ ctrl->cpr_enabled = false; ++ } ++ ++ if (ctrl->use_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ rc = regulator_enable(ctrl->vdd_limit_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } else if (!ctrl->use_hw_closed_loop ++ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ rc = regulator_disable(ctrl->vdd_limit_regulator); ++ if (rc) { ++ cpr3_err(ctrl, "CPR limit regulator disable failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ /* ++ * Due to APM and mem-acc floor restriction constraints, ++ * the closed-loop voltage may be different when using ++ * software closed-loop vs hardware closed-loop. Therefore, ++ * reset the cached closed-loop voltage for all corners to the ++ * corresponding open-loop voltage when switching between ++ * SW and HW closed-loop mode. ++ */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ for (k = 0; k < vreg->corner_count; k++) ++ vreg->corner[k].last_volt ++ = vreg->corner[k].open_loop_volt; ++ } ++ } ++ ++ /* Skip last_volt caching */ ++ ctrl->last_corner_was_closed_loop = false; ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not change CPR HW closed-loop enable state=%u, rc=%d\n", ++ use_hw_closed_loop, rc); ++ goto done; ++ } ++ ++ cpr3_debug(ctrl, "CPR mode=%s\n", ++ use_hw_closed_loop ? ++ "HW closed-loop" : "SW closed-loop"); ++done: ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_debug_hw_closed_loop_enable_get() - debugfs callback used to retrieve ++ * the value of the CPR controller use_hw_closed_loop flag which ++ * indicates if hardware closed-loop operation is being used in ++ * place of software closed-loop operation ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: Output parameter written with the value of ++ * use_hw_closed_loop ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_hw_closed_loop_enable_get(void *data, u64 *val) ++{ ++ struct cpr3_controller *ctrl = data; ++ ++ *val = ctrl->use_hw_closed_loop; ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_hw_closed_loop_enable_fops, ++ cpr3_debug_hw_closed_loop_enable_get, ++ cpr3_debug_hw_closed_loop_enable_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_debug_trigger_aging_measurement_set() - debugfs callback used to trigger ++ * another CPR measurement ++ * @data: Pointer to private data which is equal to the CPR ++ * controller pointer ++ * @val: Unused ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_debug_trigger_aging_measurement_set(void *data, u64 val) ++{ ++ struct cpr3_controller *ctrl = data; ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX); ++ ctrl->aging_required = true; ++ ctrl->aging_succeeded = false; ++ ctrl->aging_failed = false; ++ ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not update the CPR controller state, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++done: ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_trigger_aging_measurement_fops, ++ NULL, ++ cpr3_debug_trigger_aging_measurement_set, ++ "%llu\n"); ++ ++/** ++ * cpr3_regulator_debugfs_ctrl_add() - add debugfs files to expose configuration ++ * data for the CPR controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_ctrl_add(struct cpr3_controller *ctrl) ++{ ++ struct dentry *temp, *aggr_dir; ++ int i; ++ ++ /* Add cpr3-regulator base directory if it isn't present already. */ ++ if (cpr3_debugfs_base == NULL) { ++ cpr3_debugfs_base = debugfs_create_dir("cpr3-regulator", NULL); ++ if (IS_ERR_OR_NULL(cpr3_debugfs_base)) { ++ cpr3_err(ctrl, "cpr3-regulator debugfs base directory creation failed\n"); ++ cpr3_debugfs_base = NULL; ++ return; ++ } ++ } ++ ++ ctrl->debugfs = debugfs_create_dir(ctrl->name, cpr3_debugfs_base); ++ if (IS_ERR_OR_NULL(ctrl->debugfs)) { ++ cpr3_err(ctrl, "cpr3-regulator controller debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("cpr_closed_loop_enable", S_IRUGO | S_IWUSR, ++ ctrl->debugfs, ctrl, ++ &cpr3_debug_closed_loop_enable_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "cpr_closed_loop_enable debugfs file creation failed\n"); ++ return; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) { ++ temp = debugfs_create_file("use_hw_closed_loop", ++ S_IRUGO | S_IWUSR, ctrl->debugfs, ctrl, ++ &cpr3_debug_hw_closed_loop_enable_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "use_hw_closed_loop debugfs file creation failed\n"); ++ return; ++ } ++ } ++ ++ temp = debugfs_create_int("thread_count", S_IRUGO, ctrl->debugfs, ++ &ctrl->thread_count); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "thread_count debugfs file creation failed\n"); ++ return; ++ } ++ ++ if (ctrl->apm) { ++ temp = debugfs_create_int("apm_threshold_volt", S_IRUGO, ++ ctrl->debugfs, &ctrl->apm_threshold_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "apm_threshold_volt debugfs file creation failed\n"); ++ return; ++ } ++ } ++ ++ if (ctrl->aging_required || ctrl->aging_succeeded ++ || ctrl->aging_failed) { ++ temp = debugfs_create_int("aging_adj_volt", S_IRUGO, ++ ctrl->debugfs, &ctrl->aging_ref_adjust_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_adj_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("aging_succeeded", S_IRUGO, ++ ctrl->debugfs, &ctrl->aging_succeeded, &fops_bool_ro); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_succeeded debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("aging_failed", S_IRUGO, ++ ctrl->debugfs, &ctrl->aging_failed, &fops_bool_ro); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_failed debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_file("aging_trigger", S_IWUSR, ++ ctrl->debugfs, ctrl, ++ &cpr3_debug_trigger_aging_measurement_fops); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aging_trigger debugfs file creation failed\n"); ++ return; ++ } ++ } ++ ++ aggr_dir = debugfs_create_dir("max_aggregated_voltages", ctrl->debugfs); ++ if (IS_ERR_OR_NULL(aggr_dir)) { ++ cpr3_err(ctrl, "max_aggregated_voltages debugfs directory creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("floor_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.floor_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr floor_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("ceiling_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.ceiling_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr ceiling_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("open_loop_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.open_loop_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr open_loop_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ temp = debugfs_create_int("last_volt", S_IRUGO, aggr_dir, ++ &ctrl->aggr_corner.last_volt); ++ if (IS_ERR_OR_NULL(temp)) { ++ cpr3_err(ctrl, "aggr last_volt debugfs file creation failed\n"); ++ return; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ cpr3_regulator_debugfs_thread_add(&ctrl->thread[i]); ++} ++ ++/** ++ * cpr3_regulator_debugfs_ctrl_remove() - remove debugfs files for the CPR ++ * controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Note, this function must be called after the controller has been removed from ++ * cpr3_controller_list and while the cpr3_controller_list_mutex lock is held. ++ * ++ * Return: none ++ */ ++static void cpr3_regulator_debugfs_ctrl_remove(struct cpr3_controller *ctrl) ++{ ++ if (list_empty(&cpr3_controller_list)) { ++ debugfs_remove_recursive(cpr3_debugfs_base); ++ cpr3_debugfs_base = NULL; ++ } else { ++ debugfs_remove_recursive(ctrl->debugfs); ++ } ++} ++ ++/** ++ * cpr3_regulator_init_ctrl_data() - performs initialization of CPR controller ++ * elements ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ /* Read the initial vdd voltage from hardware. */ ++ ctrl->aggr_corner.last_volt ++ = regulator_get_voltage(ctrl->vdd_regulator); ++ if (ctrl->aggr_corner.last_volt < 0) { ++ cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n", ++ ctrl->aggr_corner.last_volt); ++ return ctrl->aggr_corner.last_volt; ++ } ++ ctrl->aggr_corner.open_loop_volt = ctrl->aggr_corner.last_volt; ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_init_vreg_data() - performs initialization of common CPR3 ++ * regulator elements and validate aging configurations ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_init_vreg_data(struct cpr3_regulator *vreg) ++{ ++ int i, j; ++ bool init_aging; ++ ++ vreg->current_corner = CPR3_REGULATOR_CORNER_INVALID; ++ vreg->last_closed_loop_corner = CPR3_REGULATOR_CORNER_INVALID; ++ ++ init_aging = vreg->aging_allowed && vreg->thread->ctrl->aging_required; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; ++ vreg->corner[i].irq_en = CPR3_IRQ_UP | CPR3_IRQ_DOWN; ++ ++ vreg->corner[i].ro_mask = 0; ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j] == 0) ++ vreg->corner[i].ro_mask |= BIT(j); ++ } ++ ++ if (init_aging) { ++ vreg->corner[i].unaged_floor_volt ++ = vreg->corner[i].floor_volt; ++ vreg->corner[i].unaged_ceiling_volt ++ = vreg->corner[i].ceiling_volt; ++ vreg->corner[i].unaged_open_loop_volt ++ = vreg->corner[i].open_loop_volt; ++ } ++ ++ if (vreg->aging_allowed) { ++ if (vreg->corner[i].unaged_floor_volt <= 0) { ++ cpr3_err(vreg, "invalid unaged_floor_volt[%d] = %d\n", ++ i, vreg->corner[i].unaged_floor_volt); ++ return -EINVAL; ++ } ++ if (vreg->corner[i].unaged_ceiling_volt <= 0) { ++ cpr3_err(vreg, "invalid unaged_ceiling_volt[%d] = %d\n", ++ i, vreg->corner[i].unaged_ceiling_volt); ++ return -EINVAL; ++ } ++ if (vreg->corner[i].unaged_open_loop_volt <= 0) { ++ cpr3_err(vreg, "invalid unaged_open_loop_volt[%d] = %d\n", ++ i, vreg->corner[i].unaged_open_loop_volt); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ if (vreg->aging_allowed && vreg->corner[vreg->aging_corner].ceiling_volt ++ > vreg->thread->ctrl->aging_ref_volt) { ++ cpr3_err(vreg, "aging corner %d ceiling voltage = %d > aging ref voltage = %d uV\n", ++ vreg->aging_corner, ++ vreg->corner[vreg->aging_corner].ceiling_volt, ++ vreg->thread->ctrl->aging_ref_volt); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_suspend() - perform common required CPR3 power down steps ++ * before the system enters suspend ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_suspend(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ mutex_unlock(&ctrl->lock); ++ return rc; ++ } ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ rc = cpr3_closed_loop_disable(ctrl); ++ if (rc) ++ cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc); ++ ++ ctrl->cpr_suspended = true; ++ ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_resume() - perform common required CPR3 power up steps after ++ * the system resumes from suspend ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_resume(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ mutex_lock(&ctrl->lock); ++ ++ ctrl->cpr_suspended = false; ++ rc = cpr3_regulator_update_ctrl_state(ctrl); ++ if (rc) ++ cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc); ++ ++ mutex_unlock(&ctrl->lock); ++ return 0; ++} ++ ++/** ++ * cpr3_regulator_validate_controller() - verify the data passed in via the ++ * cpr3_controller data structure ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl) ++{ ++ struct cpr3_thread *thread; ++ struct cpr3_regulator *vreg; ++ int i, j, allow_boost_vreg_count = 0; ++ ++ if (!ctrl->vdd_regulator) { ++ cpr3_err(ctrl, "vdd regulator missing\n"); ++ return -EINVAL; ++ } else if (ctrl->sensor_count <= 0 ++ || ctrl->sensor_count > CPR3_MAX_SENSOR_COUNT) { ++ cpr3_err(ctrl, "invalid CPR sensor count=%d\n", ++ ctrl->sensor_count); ++ return -EINVAL; ++ } else if (!ctrl->sensor_owner) { ++ cpr3_err(ctrl, "CPR sensor ownership table missing\n"); ++ return -EINVAL; ++ } ++ ++ if (ctrl->aging_required) { ++ for (i = 0; i < ctrl->aging_sensor_count; i++) { ++ if (ctrl->aging_sensor[i].sensor_id ++ >= ctrl->sensor_count) { ++ cpr3_err(ctrl, "aging_sensor[%d] id=%u is not in the value range 0-%d", ++ i, ctrl->aging_sensor[i].sensor_id, ++ ctrl->sensor_count - 1); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ thread = &ctrl->thread[i]; ++ for (j = 0; j < thread->vreg_count; j++) { ++ vreg = &thread->vreg[j]; ++ if (vreg->allow_boost) ++ allow_boost_vreg_count++; ++ } ++ } ++ ++ if (allow_boost_vreg_count > 1) { ++ /* ++ * Boost feature is not allowed to be used for more ++ * than one CPR3 regulator of a CPR3 controller. ++ */ ++ cpr3_err(ctrl, "Boost feature is enabled for more than one regulator\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_panic_callback() - panic notification callback function. This function ++ * is invoked when a kernel panic occurs. ++ * @nfb: Notifier block pointer of CPR3 controller ++ * @event: Value passed unmodified to notifier function ++ * @data: Pointer passed unmodified to notifier function ++ * ++ * Return: NOTIFY_OK ++ */ ++static int cpr3_panic_callback(struct notifier_block *nfb, ++ unsigned long event, void *data) ++{ ++ struct cpr3_controller *ctrl = container_of(nfb, ++ struct cpr3_controller, panic_notifier); ++ struct cpr3_panic_regs_info *regs_info = ctrl->panic_regs_info; ++ struct cpr3_reg_info *reg; ++ int i = 0; ++ ++ for (i = 0; i < regs_info->reg_count; i++) { ++ reg = &(regs_info->regs[i]); ++ reg->value = readl_relaxed(reg->virt_addr); ++ pr_err("%s[0x%08x] = 0x%08x\n", reg->name, reg->addr, ++ reg->value); ++ } ++ /* ++ * Barrier to ensure that the information has been updated in the ++ * structure. ++ */ ++ mb(); ++ ++ return NOTIFY_OK; ++} ++ ++/** ++ * cpr3_regulator_register() - register the regulators for a CPR3 controller and ++ * perform CPR hardware initialization ++ * @pdev: Platform device pointer for the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ int i, j, rc; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "%s: Device tree node is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!ctrl || !ctrl->name) { ++ dev_err(dev, "%s: CPR controller data is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ rc = cpr3_regulator_validate_controller(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "controller validation failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ mutex_init(&ctrl->lock); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpr_ctrl"); ++ if (!res || !res->start) { ++ cpr3_err(ctrl, "CPR controller address is missing\n"); ++ return -ENXIO; ++ } ++ ctrl->cpr_ctrl_base = devm_ioremap(dev, res->start, resource_size(res)); ++ ++ if (ctrl->aging_possible_mask) { ++ /* ++ * Aging possible register address is required if an aging ++ * possible mask has been specified. ++ */ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "aging_allowed"); ++ if (!res || !res->start) { ++ cpr3_err(ctrl, "CPR aging allowed address is missing\n"); ++ return -ENXIO; ++ } ++ ctrl->aging_possible_reg = devm_ioremap(dev, res->start, ++ resource_size(res)); ++ } ++ ++ ctrl->irq = platform_get_irq_byname(pdev, "cpr"); ++ if (ctrl->irq < 0) { ++ cpr3_err(ctrl, "missing CPR interrupt\n"); ++ return ctrl->irq; ++ } ++ ++ if (ctrl->supports_hw_closed_loop) { ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ ctrl->ceiling_irq = platform_get_irq_byname(pdev, ++ "ceiling"); ++ if (ctrl->ceiling_irq < 0) { ++ cpr3_err(ctrl, "missing ceiling interrupt\n"); ++ return ctrl->ceiling_irq; ++ } ++ } ++ } ++ ++ rc = cpr3_regulator_init_ctrl_data(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ rc = cpr3_regulator_init_vreg_data( ++ &ctrl->thread[i].vreg[j]); ++ if (rc) ++ return rc; ++ cpr3_print_quots(&ctrl->thread[i].vreg[j]); ++ } ++ } ++ ++ /* ++ * Add the maximum possible aging voltage margin until it is possible ++ * to perform an aging measurement. ++ */ ++ if (ctrl->aging_required) ++ cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX); ++ ++ rc = cpr3_regulator_init_ctrl(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR controller initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* Register regulator devices for all threads. */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ rc = cpr3_regulator_vreg_register( ++ &ctrl->thread[i].vreg[j]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n", ++ rc); ++ goto free_regulators; ++ } ++ } ++ } ++ ++ rc = devm_request_threaded_irq(dev, ctrl->irq, NULL, ++ cpr3_irq_handler, ++ IRQF_ONESHOT | ++ IRQF_TRIGGER_RISING, ++ "cpr3", ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not request IRQ %d, rc=%d\n", ++ ctrl->irq, rc); ++ goto free_regulators; ++ } ++ ++ if (ctrl->supports_hw_closed_loop && ++ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { ++ rc = devm_request_threaded_irq(dev, ctrl->ceiling_irq, NULL, ++ cpr3_ceiling_irq_handler, ++ IRQF_ONESHOT | IRQF_TRIGGER_RISING, ++ "cpr3_ceiling", ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "could not request ceiling IRQ %d, rc=%d\n", ++ ctrl->ceiling_irq, rc); ++ goto free_regulators; ++ } ++ } ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ cpr3_regulator_debugfs_ctrl_add(ctrl); ++ list_add(&ctrl->list, &cpr3_controller_list); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ if (ctrl->panic_regs_info) { ++ /* Register panic notification call back */ ++ ctrl->panic_notifier.notifier_call = cpr3_panic_callback; ++ atomic_notifier_chain_register(&panic_notifier_list, ++ &ctrl->panic_notifier); ++ } ++ ++ return 0; ++ ++free_regulators: ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev)) ++ regulator_unregister( ++ ctrl->thread[i].vreg[j].rdev); ++ return rc; ++} ++ ++/** ++ * cpr3_open_loop_regulator_register() - register the regulators for a CPR3 ++ * controller which will always work in Open loop and ++ * won't support close loop. ++ * @pdev: Platform device pointer for the CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_open_loop_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl) ++{ ++ struct device *dev = &pdev->dev; ++ struct cpr3_regulator *vreg; ++ int i, j, rc; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "%s: Device tree node is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!ctrl || !ctrl->name) { ++ dev_err(dev, "%s: CPR controller data is missing\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!ctrl->vdd_regulator) { ++ cpr3_err(ctrl, "vdd regulator missing\n"); ++ return -EINVAL; ++ } ++ ++ mutex_init(&ctrl->lock); ++ ++ rc = cpr3_regulator_init_ctrl_data(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ vreg = &ctrl->thread[i].vreg[j]; ++ vreg->corner[i].last_volt = ++ vreg->corner[i].open_loop_volt; ++ } ++ } ++ ++ /* Register regulator devices for all threads. */ ++ for (i = 0; i < ctrl->thread_count; i++) { ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { ++ rc = cpr3_regulator_vreg_register( ++ &ctrl->thread[i].vreg[j]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n", ++ rc); ++ goto free_regulators; ++ } ++ } ++ } ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ list_add(&ctrl->list, &cpr3_controller_list); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ return 0; ++ ++free_regulators: ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev)) ++ regulator_unregister( ++ ctrl->thread[i].vreg[j].rdev); ++ return rc; ++} ++ ++/** ++ * cpr3_regulator_unregister() - unregister the regulators for a CPR3 controller ++ * and perform CPR hardware shutdown ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ int i, j, rc = 0; ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ list_del(&ctrl->list); ++ cpr3_regulator_debugfs_ctrl_remove(ctrl); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { ++ rc = cpr3_ctrl_clear_cpr4_config(ctrl); ++ if (rc) ++ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", ++ rc); ++ } ++ ++ cpr3_ctrl_loop_disable(ctrl); ++ ++ cpr3_closed_loop_disable(ctrl); ++ ++ if (ctrl->vdd_limit_regulator) { ++ regulator_disable(ctrl->vdd_limit_regulator); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ regulator_unregister(ctrl->thread[i].vreg[j].rdev); ++ ++ if (ctrl->panic_notifier.notifier_call) ++ atomic_notifier_chain_unregister(&panic_notifier_list, ++ &ctrl->panic_notifier); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_open_loop_regulator_unregister() - unregister the regulators for a CPR3 ++ * open loop controller and perform CPR hardware shutdown ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ int i, j; ++ ++ mutex_lock(&cpr3_controller_list_mutex); ++ list_del(&ctrl->list); ++ mutex_unlock(&cpr3_controller_list_mutex); ++ ++ if (ctrl->vdd_limit_regulator) { ++ regulator_disable(ctrl->vdd_limit_regulator); ++ } ++ ++ for (i = 0; i < ctrl->thread_count; i++) ++ for (j = 0; j < ctrl->thread[i].vreg_count; j++) ++ regulator_unregister(ctrl->thread[i].vreg[j].rdev); ++ ++ if (ctrl->panic_notifier.notifier_call) ++ atomic_notifier_chain_unregister(&panic_notifier_list, ++ &ctrl->panic_notifier); ++ ++ return 0; ++} +diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h +new file mode 100644 +index 0000000000000..7c69c4630543b +--- /dev/null ++++ b/drivers/regulator/cpr3-regulator.h +@@ -0,0 +1,1211 @@ ++/* ++ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __REGULATOR_CPR3_REGULATOR_H__ ++#define __REGULATOR_CPR3_REGULATOR_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct cpr3_controller; ++struct cpr3_thread; ++ ++/** ++ * struct cpr3_fuse_param - defines one contiguous segment of a fuse parameter ++ * that is contained within a given row. ++ * @row: Fuse row number ++ * @bit_start: The first bit within the row of the fuse parameter segment ++ * @bit_end: The last bit within the row of the fuse parameter segment ++ * ++ * Each fuse row is 64 bits in length. bit_start and bit_end may take values ++ * from 0 to 63. bit_start must be less than or equal to bit_end. ++ */ ++struct cpr3_fuse_param { ++ unsigned row; ++ unsigned bit_start; ++ unsigned bit_end; ++}; ++ ++/* Each CPR3 sensor has 16 ring oscillators */ ++#define CPR3_RO_COUNT 16 ++ ++/* The maximum number of sensors that can be present on a single CPR loop. */ ++#define CPR3_MAX_SENSOR_COUNT 256 ++ ++/* This constant is used when allocating array printing buffers. */ ++#define MAX_CHARS_PER_INT 10 ++ ++/** ++ * struct cpr4_sdelta - CPR4 controller specific data structure for the sdelta ++ * adjustment table which is used to adjust the VDD supply ++ * voltage automatically based upon the temperature and/or ++ * the number of online CPU cores. ++ * @allow_core_count_adj: Core count adjustments are allowed. ++ * @allow_temp_adj: Temperature based adjustments are allowed. ++ * @max_core_count: Maximum number of cores considered for core count ++ * adjustment logic. ++ * @temp_band_count: Number of temperature bands considered for temperature ++ * based adjustment logic. ++ * @cap_volt: CAP in uV to apply to SDELTA margins with multiple ++ * cpr3-regulators defined for single controller. ++ * @table: SDELTA table with per-online-core and temperature based ++ * adjustments of size (max_core_count * temp_band_count) ++ * Outer: core count ++ * Inner: temperature band ++ * Each element has units of VDD supply steps. Positive ++ * values correspond to a reduction in voltage and negative ++ * value correspond to an increase (this follows the SDELTA ++ * register semantics). ++ * @allow_boost: Voltage boost allowed. ++ * @boost_num_cores: The number of online cores at which the boost voltage ++ * adjustments will be applied ++ * @boost_table: SDELTA table with boost voltage adjustments of size ++ * temp_band_count. Each element has units of VDD supply ++ * steps. Positive values correspond to a reduction in ++ * voltage and negative value correspond to an increase ++ * (this follows the SDELTA register semantics). ++ */ ++struct cpr4_sdelta { ++ bool allow_core_count_adj; ++ bool allow_temp_adj; ++ int max_core_count; ++ int temp_band_count; ++ int cap_volt; ++ int *table; ++ bool allow_boost; ++ int boost_num_cores; ++ int *boost_table; ++}; ++ ++/** ++ * struct cpr3_corner - CPR3 virtual voltage corner data structure ++ * @floor_volt: CPR closed-loop floor voltage in microvolts ++ * @ceiling_volt: CPR closed-loop ceiling voltage in microvolts ++ * @open_loop_volt: CPR open-loop voltage (i.e. initial voltage) in ++ * microvolts ++ * @last_volt: Last known settled CPR closed-loop voltage which is used ++ * when switching to a new corner ++ * @abs_ceiling_volt: The absolute CPR closed-loop ceiling voltage in ++ * microvolts. This is used to limit the ceiling_volt ++ * value when it is increased as a result of aging ++ * adjustment. ++ * @unaged_floor_volt: The CPR closed-loop floor voltage in microvolts before ++ * any aging adjustment is performed ++ * @unaged_ceiling_volt: The CPR closed-loop ceiling voltage in microvolts ++ * before any aging adjustment is performed ++ * @unaged_open_loop_volt: The CPR open-loop voltage (i.e. initial voltage) in ++ * microvolts before any aging adjusment is performed ++ * @system_volt: The system-supply voltage in microvolts or corners or ++ * levels ++ * @mem_acc_volt: The mem-acc-supply voltage in corners ++ * @proc_freq: Processor frequency in Hertz. For CPR rev. 3 and 4 ++ * conrollers, this field is only used by platform specific ++ * CPR3 driver for interpolation. For CPRh-compliant ++ * controllers, this frequency is also utilized by the ++ * clock driver to determine the corner to CPU clock ++ * frequency mappings. ++ * @cpr_fuse_corner: Fused corner index associated with this virtual corner ++ * (only used by platform specific CPR3 driver for ++ * mapping purposes) ++ * @target_quot: Array of target quotient values to use for each ring ++ * oscillator (RO) for this corner. A value of 0 should be ++ * specified as the target quotient for each RO that is ++ * unused by this corner. ++ * @ro_scale: Array of CPR ring oscillator (RO) scaling factors. The ++ * scaling factor for each RO is defined from RO0 to RO15 ++ * with units of QUOT/V. A value of 0 may be specified for ++ * an RO that is unused. ++ * @ro_mask: Bitmap where each of the 16 LSBs indicate if the ++ * corresponding ROs should be masked for this corner ++ * @irq_en: Bitmap of the CPR interrupts to enable for this corner ++ * @aging_derate: The amount to derate the aging voltage adjustment ++ * determined for the reference corner in units of uV/mV. ++ * E.g. a value of 900 would imply that the adjustment for ++ * this corner should be 90% (900/1000) of that for the ++ * reference corner. ++ * @use_open_loop: Boolean indicating that open-loop (i.e CPR disabled) as ++ * opposed to closed-loop operation must be used for this ++ * corner on CPRh controllers. ++ * @sdelta: The CPR4 controller specific data for this corner. This ++ * field is applicable for CPR4 controllers. ++ * ++ * The value of last_volt is initialized inside of the cpr3_regulator_register() ++ * call with the open_loop_volt value. It can later be updated to the settled ++ * VDD supply voltage. The values for unaged_floor_volt, unaged_ceiling_volt, ++ * and unaged_open_loop_volt are initialized inside of cpr3_regulator_register() ++ * if ctrl->aging_required == true. These three values must be pre-initialized ++ * if cpr3_regulator_register() is called with ctrl->aging_required == false and ++ * ctrl->aging_succeeded == true. ++ * ++ * The values of ro_mask and irq_en are initialized inside of the ++ * cpr3_regulator_register() call. ++ */ ++struct cpr3_corner { ++ int floor_volt; ++ int ceiling_volt; ++ int cold_temp_open_loop_volt; ++ int normal_temp_open_loop_volt; ++ int open_loop_volt; ++ int last_volt; ++ int abs_ceiling_volt; ++ int unaged_floor_volt; ++ int unaged_ceiling_volt; ++ int unaged_open_loop_volt; ++ int system_volt; ++ int mem_acc_volt; ++ u32 proc_freq; ++ int cpr_fuse_corner; ++ u32 target_quot[CPR3_RO_COUNT]; ++ u32 ro_scale[CPR3_RO_COUNT]; ++ u32 ro_mask; ++ u32 irq_en; ++ int aging_derate; ++ bool use_open_loop; ++ struct cpr4_sdelta *sdelta; ++}; ++ ++/** ++ * struct cprh_corner_band - CPRh controller specific data structure which ++ * encapsulates the range of corners and the SDELTA ++ * adjustment table to be applied to the corners within ++ * the min and max bounds of the corner band. ++ * @corner: Corner number which defines the corner band boundary ++ * @sdelta: The SDELTA adjustment table which contains core-count ++ * and temp based margin adjustments that are applicable ++ * to the corner band. ++ */ ++struct cprh_corner_band { ++ int corner; ++ struct cpr4_sdelta *sdelta; ++}; ++ ++/** ++ * struct cpr3_fuse_parameters - CPR4 fuse specific data structure which has ++ * the required fuse parameters need for Close Loop CPR ++ * @(*apss_ro_sel_param)[2]: Pointer to RO select fuse details ++ * @(*apss_init_voltage_param)[2]: Pointer to Target voltage fuse details ++ * @(*apss_target_quot_param)[2]: Pointer to Target quot fuse details ++ * @(*apss_quot_offset_param)[2]: Pointer to quot offset fuse details ++ * @cpr_fusing_rev_param: Pointer to CPR revision fuse details ++ * @apss_speed_bin_param: Pointer to Speed bin fuse details ++ * @cpr_boost_fuse_cfg_param: Pointer to Boost fuse cfg details ++ * @apss_boost_fuse_volt_param: Pointer to Boost fuse volt details ++ * @misc_fuse_volt_adj_param: Pointer to Misc fuse volt fuse details ++ */ ++struct cpr3_fuse_parameters { ++ struct cpr3_fuse_param (*apss_ro_sel_param)[2]; ++ struct cpr3_fuse_param (*apss_init_voltage_param)[2]; ++ struct cpr3_fuse_param (*apss_target_quot_param)[2]; ++ struct cpr3_fuse_param (*apss_quot_offset_param)[2]; ++ struct cpr3_fuse_param *cpr_fusing_rev_param; ++ struct cpr3_fuse_param *apss_speed_bin_param; ++ struct cpr3_fuse_param *cpr_boost_fuse_cfg_param; ++ struct cpr3_fuse_param *apss_boost_fuse_volt_param; ++ struct cpr3_fuse_param *misc_fuse_volt_adj_param; ++}; ++ ++struct cpr4_mem_acc_func { ++ void (*set_mem_acc)(struct regulator_dev *); ++ void (*clear_mem_acc)(struct regulator_dev *); ++}; ++ ++/** ++ * struct cpr4_reg_data - CPR4 regulator specific data structure which is ++ * target specific ++ * @cpr_valid_fuse_count: Number of valid fuse corners ++ * @fuse_ref_volt: Pointer to fuse reference voltage ++ * @fuse_step_volt: CPR step voltage available in fuse ++ * @cpr_clk_rate: CPR clock rate ++ * @boost_fuse_ref_volt: Boost fuse reference voltage ++ * @boost_ceiling_volt: Boost ceiling voltage ++ * @boost_floor_volt: Boost floor voltage ++ * @cpr3_fuse_params: Pointer to CPR fuse parameters ++ * @mem_acc_funcs: Pointer to MEM ACC set/clear functions ++ **/ ++struct cpr4_reg_data { ++ u32 cpr_valid_fuse_count; ++ int *fuse_ref_volt; ++ u32 fuse_step_volt; ++ u32 cpr_clk_rate; ++ int boost_fuse_ref_volt; ++ int boost_ceiling_volt; ++ int boost_floor_volt; ++ struct cpr3_fuse_parameters *cpr3_fuse_params; ++ struct cpr4_mem_acc_func *mem_acc_funcs; ++}; ++/** ++ * struct cpr3_reg_data - CPR3 regulator specific data structure which is ++ * target specific ++ * @cpr_valid_fuse_count: Number of valid fuse corners ++ * @(*init_voltage_param)[2]: Pointer to Target voltage fuse details ++ * @fuse_ref_volt: Pointer to fuse reference voltage ++ * @fuse_step_volt: CPR step voltage available in fuse ++ * @cpr_clk_rate: CPR clock rate ++ * @cpr3_fuse_params: Pointer to CPR fuse parameters ++ **/ ++struct cpr3_reg_data { ++ u32 cpr_valid_fuse_count; ++ struct cpr3_fuse_param (*init_voltage_param)[2]; ++ int *fuse_ref_volt; ++ u32 fuse_step_volt; ++ u32 cpr_clk_rate; ++}; ++ ++/** ++ * struct cpr3_regulator - CPR3 logical regulator instance associated with a ++ * given CPR3 hardware thread ++ * @of_node: Device node associated with the device tree child node ++ * of this CPR3 regulator ++ * @thread: Pointer to the CPR3 thread which manages this CPR3 ++ * regulator ++ * @name: Unique name for this CPR3 regulator which is filled ++ * using the device tree regulator-name property ++ * @rdesc: Regulator description for this CPR3 regulator ++ * @rdev: Regulator device pointer for the regulator registered ++ * for this CPR3 regulator ++ * @mem_acc_regulator: Pointer to the optional mem-acc supply regulator used ++ * to manage memory circuitry settings based upon CPR3 ++ * regulator output voltage. ++ * @corner: Array of all corners supported by this CPR3 regulator ++ * @corner_count: The number of elements in the corner array ++ * @corner_band: Array of all corner bands supported by CPRh compatible ++ * controllers ++ * @cpr4_regulator_data Target specific cpr4 regulator data ++ * @cpr3_regulator_data Target specific cpr3 regulator data ++ * @corner_band_count: The number of elements in the corner band array ++ * @platform_fuses: Pointer to platform specific CPR fuse data (only used by ++ * platform specific CPR3 driver) ++ * @speed_bin_fuse: Value read from the speed bin fuse parameter ++ * @speed_bins_supported: The number of speed bins supported by the device tree ++ * configuration for this CPR3 regulator ++ * @cpr_rev_fuse: Value read from the CPR fusing revision fuse parameter ++ * @fuse_combo: Platform specific enum value identifying the specific ++ * combination of fuse values found on a given chip ++ * @fuse_combos_supported: The number of fuse combinations supported by the ++ * device tree configuration for this CPR3 regulator ++ * @fuse_corner_count: Number of corners defined by fuse parameters ++ * @fuse_corner_map: Array of length fuse_corner_count which specifies the ++ * highest corner associated with each fuse corner. Note ++ * that each element must correspond to a valid corner ++ * and that element values must be strictly increasing. ++ * Also, it is acceptable for the lowest fuse corner to map ++ * to a corner other than the lowest. Likewise, it is ++ * acceptable for the highest fuse corner to map to a ++ * corner other than the highest. ++ * @fuse_combo_corner_sum: The sum of the corner counts across all fuse combos ++ * @fuse_combo_offset: The device tree property array offset for the selected ++ * fuse combo ++ * @speed_bin_corner_sum: The sum of the corner counts across all speed bins ++ * This may be specified as 0 if per speed bin parsing ++ * support is not required. ++ * @speed_bin_offset: The device tree property array offset for the selected ++ * speed bin ++ * @fuse_combo_corner_band_sum: The sum of the corner band counts across all ++ * fuse combos ++ * @fuse_combo_corner_band_offset: The device tree property array offset for ++ * the corner band count corresponding to the selected ++ * fuse combo ++ * @speed_bin_corner_band_sum: The sum of the corner band counts across all ++ * speed bins. This may be specified as 0 if per speed bin ++ * parsing support is not required ++ * @speed_bin_corner_band_offset: The device tree property array offset for the ++ * corner band count corresponding to the selected speed ++ * bin ++ * @pd_bypass_mask: Bit mask of power domains associated with this CPR3 ++ * regulator ++ * @dynamic_floor_corner: Index identifying the voltage corner for the CPR3 ++ * regulator whose last_volt value should be used as the ++ * global CPR floor voltage if all of the power domains ++ * associated with this CPR3 regulator are bypassed ++ * @uses_dynamic_floor: Boolean flag indicating that dynamic_floor_corner should ++ * be utilized for the CPR3 regulator ++ * @current_corner: Index identifying the currently selected voltage corner ++ * for the CPR3 regulator or less than 0 if no corner has ++ * been requested ++ * @last_closed_loop_corner: Index identifying the last voltage corner for the ++ * CPR3 regulator which was configured when operating in ++ * CPR closed-loop mode or less than 0 if no corner has ++ * been requested. CPR registers are only written to when ++ * using closed-loop mode. ++ * @aggregated: Boolean flag indicating that this CPR3 regulator ++ * participated in the last aggregation event ++ * @debug_corner: Index identifying voltage corner used for displaying ++ * corner configuration values in debugfs ++ * @vreg_enabled: Boolean defining the enable state of the CPR3 ++ * regulator's regulator within the regulator framework. ++ * @aging_allowed: Boolean defining if CPR aging adjustments are allowed ++ * for this CPR3 regulator given the fuse combo of the ++ * device ++ * @aging_allow_open_loop_adj: Boolean defining if the open-loop voltage of each ++ * corner of this regulator should be adjusted as a result ++ * of an aging measurement. This flag can be set to false ++ * when the open-loop voltage adjustments have been ++ * specified such that they include the maximum possible ++ * aging adjustment. This flag is only used if ++ * aging_allowed == true. ++ * @aging_corner: The corner that should be configured for this regulator ++ * when an aging measurement is performed. ++ * @aging_max_adjust_volt: The maximum aging voltage margin in microvolts that ++ * may be added to the target quotients of this regulator. ++ * A value of 0 may be specified if this regulator does not ++ * require any aging adjustment. ++ * @allow_core_count_adj: Core count adjustments are allowed for this regulator. ++ * @allow_temp_adj: Temperature based adjustments are allowed for this ++ * regulator. ++ * @max_core_count: Maximum number of cores considered for core count ++ * adjustment logic. ++ * @allow_boost: Voltage boost allowed for this regulator. ++ * ++ * This structure contains both configuration and runtime state data. The ++ * elements current_corner, last_closed_loop_corner, aggregated, debug_corner, ++ * and vreg_enabled are state variables. ++ */ ++struct cpr3_regulator { ++ struct device_node *of_node; ++ struct cpr3_thread *thread; ++ const char *name; ++ struct regulator_desc rdesc; ++ struct regulator_dev *rdev; ++ struct regulator *mem_acc_regulator; ++ struct cpr3_corner *corner; ++ int corner_count; ++ struct cprh_corner_band *corner_band; ++ struct cpr4_reg_data *cpr4_regulator_data; ++ struct cpr3_reg_data *cpr3_regulator_data; ++ u32 corner_band_count; ++ ++ void *platform_fuses; ++ int speed_bin_fuse; ++ int speed_bins_supported; ++ int cpr_rev_fuse; ++ int part_type; ++ int part_type_supported; ++ int fuse_combo; ++ int fuse_combos_supported; ++ int fuse_corner_count; ++ int *fuse_corner_map; ++ int fuse_combo_corner_sum; ++ int fuse_combo_offset; ++ int speed_bin_corner_sum; ++ int speed_bin_offset; ++ int fuse_combo_corner_band_sum; ++ int fuse_combo_corner_band_offset; ++ int speed_bin_corner_band_sum; ++ int speed_bin_corner_band_offset; ++ u32 pd_bypass_mask; ++ int dynamic_floor_corner; ++ bool uses_dynamic_floor; ++ ++ int current_corner; ++ int last_closed_loop_corner; ++ bool aggregated; ++ int debug_corner; ++ bool vreg_enabled; ++ ++ bool aging_allowed; ++ bool aging_allow_open_loop_adj; ++ int aging_corner; ++ int aging_max_adjust_volt; ++ ++ bool allow_core_count_adj; ++ bool allow_temp_adj; ++ int max_core_count; ++ bool allow_boost; ++}; ++ ++/** ++ * struct cpr3_thread - CPR3 hardware thread data structure ++ * @thread_id: Hardware thread ID ++ * @of_node: Device node associated with the device tree child node ++ * of this CPR3 thread ++ * @ctrl: Pointer to the CPR3 controller which manages this thread ++ * @vreg: Array of CPR3 regulators handled by the CPR3 thread ++ * @vreg_count: Number of elements in the vreg array ++ * @aggr_corner: CPR corner containing the in process aggregated voltage ++ * and target quotient configurations which will be applied ++ * @last_closed_loop_aggr_corner: CPR corner containing the most recent ++ * configurations which were written into hardware ++ * registers when operating in closed loop mode (i.e. with ++ * CPR enabled) ++ * @consecutive_up: The number of consecutive CPR step up events needed to ++ * to trigger an up interrupt ++ * @consecutive_down: The number of consecutive CPR step down events needed to ++ * to trigger a down interrupt ++ * @up_threshold: The number CPR error steps required to generate an up ++ * event ++ * @down_threshold: The number CPR error steps required to generate a down ++ * event ++ * ++ * This structure contains both configuration and runtime state data. The ++ * elements aggr_corner and last_closed_loop_aggr_corner are state variables. ++ */ ++struct cpr3_thread { ++ u32 thread_id; ++ struct device_node *of_node; ++ struct cpr3_controller *ctrl; ++ struct cpr3_regulator *vreg; ++ int vreg_count; ++ struct cpr3_corner aggr_corner; ++ struct cpr3_corner last_closed_loop_aggr_corner; ++ ++ u32 consecutive_up; ++ u32 consecutive_down; ++ u32 up_threshold; ++ u32 down_threshold; ++}; ++ ++/* Per CPR controller data */ ++/** ++ * enum cpr3_mem_acc_corners - Constants which define the number of mem-acc ++ * regulator corners available in the mem-acc corner map array. ++ * %CPR3_MEM_ACC_LOW_CORNER: Index in mem-acc corner map array mapping to the ++ * mem-acc regulator corner ++ * to be used for low voltage vdd supply ++ * %CPR3_MEM_ACC_HIGH_CORNER: Index in mem-acc corner map array mapping to the ++ * mem-acc regulator corner to be used for high ++ * voltage vdd supply ++ * %CPR3_MEM_ACC_CORNERS: Number of elements in the mem-acc corner map ++ * array ++ */ ++enum cpr3_mem_acc_corners { ++ CPR3_MEM_ACC_LOW_CORNER = 0, ++ CPR3_MEM_ACC_HIGH_CORNER = 1, ++ CPR3_MEM_ACC_CORNERS = 2, ++}; ++ ++/** ++ * enum cpr3_count_mode - CPR3 controller count mode which defines the ++ * method that CPR sensor data is acquired ++ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MIN: Capture all CPR sensor readings ++ * simultaneously and report the minimum ++ * value seen in successive measurements ++ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MAX: Capture all CPR sensor readings ++ * simultaneously and report the maximum ++ * value seen in successive measurements ++ * %CPR3_COUNT_MODE_STAGGERED: Read one sensor at a time in a ++ * sequential fashion ++ * %CPR3_COUNT_MODE_ALL_AT_ONCE_AGE: Capture all CPR aging sensor readings ++ * simultaneously. ++ */ ++enum cpr3_count_mode { ++ CPR3_COUNT_MODE_ALL_AT_ONCE_MIN = 0, ++ CPR3_COUNT_MODE_ALL_AT_ONCE_MAX = 1, ++ CPR3_COUNT_MODE_STAGGERED = 2, ++ CPR3_COUNT_MODE_ALL_AT_ONCE_AGE = 3, ++}; ++ ++/** ++ * enum cpr_controller_type - supported CPR controller hardware types ++ * %CPR_CTRL_TYPE_CPR3: HW has CPR3 controller ++ * %CPR_CTRL_TYPE_CPR4: HW has CPR4 controller ++ */ ++enum cpr_controller_type { ++ CPR_CTRL_TYPE_CPR3, ++ CPR_CTRL_TYPE_CPR4, ++}; ++ ++/** ++ * cpr_setting - supported CPR global settings ++ * %CPR_DEFAULT: default mode from dts will be used ++ * %CPR_DISABLED: ceiling voltage will be used for all the corners ++ * %CPR_OPEN_LOOP_EN: CPR will work in OL ++ * %CPR_CLOSED_LOOP_EN: CPR will work in CL, if supported ++ */ ++enum cpr_setting { ++ CPR_DEFAULT = 0, ++ CPR_DISABLED = 1, ++ CPR_OPEN_LOOP_EN = 2, ++ CPR_CLOSED_LOOP_EN = 3, ++}; ++ ++/** ++ * struct cpr3_aging_sensor_info - CPR3 aging sensor information ++ * @sensor_id The index of the CPR3 sensor to be used in the aging ++ * measurement. ++ * @ro_scale The CPR ring oscillator (RO) scaling factor for the ++ * aging sensor with units of QUOT/V. ++ * @init_quot_diff: The fused quotient difference between aged and un-aged ++ * paths that was measured at manufacturing time. ++ * @measured_quot_diff: The quotient difference measured at runtime. ++ * @bypass_mask: Bit mask of the CPR sensors that must be bypassed during ++ * the aging measurement for this sensor ++ * ++ * This structure contains both configuration and runtime state data. The ++ * element measured_quot_diff is a state variable. ++ */ ++struct cpr3_aging_sensor_info { ++ u32 sensor_id; ++ u32 ro_scale; ++ int init_quot_diff; ++ int measured_quot_diff; ++ u32 bypass_mask[CPR3_MAX_SENSOR_COUNT / 32]; ++}; ++ ++/** ++ * struct cpr3_reg_info - Register information data structure ++ * @name: Register name ++ * @addr: Register physical address ++ * @value: Register content ++ * @virt_addr: Register virtual address ++ * ++ * This data structure is used to dump some critical register contents ++ * when the device crashes due to a kernel panic. ++ */ ++struct cpr3_reg_info { ++ const char *name; ++ u32 addr; ++ u32 value; ++ void __iomem *virt_addr; ++}; ++ ++/** ++ * struct cpr3_panic_regs_info - Data structure to dump critical register ++ * contents. ++ * @reg_count: Number of elements in the regs array ++ * @regs: Array of critical registers information ++ * ++ * This data structure is used to dump critical register contents when ++ * the device crashes due to a kernel panic. ++ */ ++struct cpr3_panic_regs_info { ++ int reg_count; ++ struct cpr3_reg_info *regs; ++}; ++ ++/** ++ * struct cpr3_controller - CPR3 controller data structure ++ * @dev: Device pointer for the CPR3 controller device ++ * @name: Unique name for the CPR3 controller ++ * @ctrl_id: Controller ID corresponding to the VDD supply number ++ * that this CPR3 controller manages. ++ * @cpr_ctrl_base: Virtual address of the CPR3 controller base register ++ * @fuse_base: Virtual address of fuse row 0 ++ * @aging_possible_reg: Virtual address of an optional platform-specific ++ * register that must be ready to determine if it is ++ * possible to perform an aging measurement. ++ * @list: list head used in a global cpr3-regulator list so that ++ * cpr3-regulator structs can be found easily in RAM dumps ++ * @thread: Array of CPR3 threads managed by the CPR3 controller ++ * @thread_count: Number of elements in the thread array ++ * @sensor_owner: Array of thread IDs indicating which thread owns a given ++ * CPR sensor ++ * @sensor_count: The number of CPR sensors found on the CPR loop managed ++ * by this CPR controller. Must be equal to the number of ++ * elements in the sensor_owner array ++ * @soc_revision: Revision number of the SoC. This may be unused by ++ * platforms that do not have different behavior for ++ * different SoC revisions. ++ * @lock: Mutex lock used to ensure mutual exclusion between ++ * all of the threads associated with the controller ++ * @vdd_regulator: Pointer to the VDD supply regulator which this CPR3 ++ * controller manages ++ * @system_regulator: Pointer to the optional system-supply regulator upon ++ * which the VDD supply regulator depends. ++ * @mem_acc_regulator: Pointer to the optional mem-acc supply regulator used ++ * to manage memory circuitry settings based upon the ++ * VDD supply output voltage. ++ * @vdd_limit_regulator: Pointer to the VDD supply limit regulator which is used ++ * for hardware closed-loop in order specify ceiling and ++ * floor voltage limits (platform specific) ++ * @system_supply_max_volt: Voltage in microvolts which corresponds to the ++ * absolute ceiling voltage of the system-supply ++ * @mem_acc_threshold_volt: mem-acc threshold voltage in microvolts ++ * @mem_acc_corner_map: mem-acc regulator corners mapping to low and high ++ * voltage mem-acc settings for the memories powered by ++ * this CPR3 controller and its associated CPR3 regulators ++ * @mem_acc_crossover_volt: Voltage in microvolts corresponding to the voltage ++ * that the VDD supply must be set to while a MEM ACC ++ * switch is in progress. This element must be initialized ++ * for CPRh controllers when a MEM ACC threshold voltage is ++ * defined. ++ * @core_clk: Pointer to the CPR3 controller core clock ++ * @iface_clk: Pointer to the CPR3 interface clock (platform specific) ++ * @bus_clk: Pointer to the CPR3 bus clock (platform specific) ++ * @irq: CPR interrupt number ++ * @irq_affinity_mask: The cpumask for the CPUs which the CPR interrupt should ++ * have affinity for ++ * @cpu_hotplug_notifier: CPU hotplug notifier used to reset IRQ affinity when a ++ * CPU is brought back online ++ * @ceiling_irq: Interrupt number for the interrupt that is triggered ++ * when hardware closed-loop attempts to exceed the ceiling ++ * voltage ++ * @apm: Handle to the array power mux (APM) ++ * @apm_threshold_volt: Voltage in microvolts which defines the threshold ++ * voltage to determine the APM supply selection for ++ * each corner ++ * @apm_crossover_volt: Voltage in microvolts corresponding to the voltage that ++ * the VDD supply must be set to while an APM switch is in ++ * progress. This element must be initialized for CPRh ++ * controllers when an APM threshold voltage is defined ++ * @apm_adj_volt: Minimum difference between APM threshold voltage and ++ * open-loop voltage which allows the APM threshold voltage ++ * to be used as a ceiling ++ * @apm_high_supply: APM supply to configure if VDD voltage is greater than ++ * or equal to the APM threshold voltage ++ * @apm_low_supply: APM supply to configure if the VDD voltage is less than ++ * the APM threshold voltage ++ * @base_volt: Minimum voltage in microvolts supported by the VDD ++ * supply managed by this CPR controller ++ * @corner_switch_delay_time: The delay time in nanoseconds used by the CPR ++ * controller to wait for voltage settling before ++ * acknowledging the OSM block after corner changes ++ * @cpr_clock_rate: CPR reference clock frequency in Hz. ++ * @sensor_time: The time in nanoseconds that each sensor takes to ++ * perform a measurement. ++ * @loop_time: The time in nanoseconds between consecutive CPR ++ * measurements. ++ * @up_down_delay_time: The time to delay in nanoseconds between consecutive CPR ++ * measurements when the last measurement recommended ++ * increasing or decreasing the vdd-supply voltage. ++ * (platform specific) ++ * @idle_clocks: Number of CPR reference clock ticks that the CPR ++ * controller waits in transitional states. ++ * @step_quot_init_min: The default minimum CPR step quotient value. The step ++ * quotient is the number of additional ring oscillator ++ * ticks observed when increasing one step in vdd-supply ++ * output voltage. ++ * @step_quot_init_max: The default maximum CPR step quotient value. ++ * @step_volt: Step size in microvolts between available set points ++ * of the VDD supply ++ * @down_error_step_limit: CPR4 hardware closed-loop down error step limit which ++ * defines the maximum number of VDD supply regulator steps ++ * that the voltage may be reduced as the result of a ++ * single CPR measurement. ++ * @up_error_step_limit: CPR4 hardware closed-loop up error step limit which ++ * defines the maximum number of VDD supply regulator steps ++ * that the voltage may be increased as the result of a ++ * single CPR measurement. ++ * @count_mode: CPR controller count mode ++ * @count_repeat: Number of times to perform consecutive sensor ++ * measurements when using all-at-once count modes. ++ * @proc_clock_throttle: Defines the processor clock frequency throttling ++ * register value to use. This can be used to reduce the ++ * clock frequency when a power domain exits a low power ++ * mode until CPR settles at a new voltage. ++ * (platform specific) ++ * @cpr_allowed_hw: Boolean which indicates if closed-loop CPR operation is ++ * permitted for a given chip based upon hardware fuse ++ * values ++ * @cpr_allowed_sw: Boolean which indicates if closed-loop CPR operation is ++ * permitted based upon software policies ++ * @supports_hw_closed_loop: Boolean which indicates if this CPR3/4 controller ++ * physically supports hardware closed-loop CPR operation ++ * @use_hw_closed_loop: Boolean which indicates that this controller will be ++ * using hardware closed-loop operation in place of ++ * software closed-loop operation. ++ * @ctrl_type: CPR controller type ++ * @saw_use_unit_mV: Boolean which indicates the unit used in SAW PVC ++ * interface is mV. ++ * @aggr_corner: CPR corner containing the most recently aggregated ++ * voltage configurations which are being used currently ++ * @cpr_enabled: Boolean which indicates that the CPR controller is ++ * enabled and operating in closed-loop mode. CPR clocks ++ * have been prepared and enabled whenever this flag is ++ * true. ++ * @last_corner_was_closed_loop: Boolean indicating if the last known corners ++ * were updated during closed loop operation. ++ * @cpr_suspended: Boolean which indicates that CPR has been temporarily ++ * disabled while enterring system suspend. ++ * @debugfs: Pointer to the debugfs directory of this CPR3 controller ++ * @aging_ref_volt: Reference voltage in microvolts to configure when ++ * performing CPR aging measurements. ++ * @aging_vdd_mode: vdd-supply regulator mode to configure before performing ++ * a CPR aging measurement. It should be one of ++ * REGULATOR_MODE_*. ++ * @aging_complete_vdd_mode: vdd-supply regulator mode to configure after ++ * performing a CPR aging measurement. It should be one of ++ * REGULATOR_MODE_*. ++ * @aging_ref_adjust_volt: The reference aging voltage margin in microvolts that ++ * should be added to the target quotients of the ++ * regulators managed by this controller after derating. ++ * @aging_required: Flag which indicates that a CPR aging measurement still ++ * needs to be performed for this CPR3 controller. ++ * @aging_succeeded: Flag which indicates that a CPR aging measurement has ++ * completed successfully. ++ * @aging_failed: Flag which indicates that a CPR aging measurement has ++ * failed to complete successfully. ++ * @aging_sensor: Array of CPR3 aging sensors which are used to perform ++ * aging measurements at a runtime. ++ * @aging_sensor_count: Number of elements in the aging_sensor array ++ * @aging_possible_mask: Optional bitmask used to mask off the ++ * aging_possible_reg register. ++ * @aging_possible_val: Optional value that the masked aging_possible_reg ++ * register must have in order for a CPR aging measurement ++ * to be possible. ++ * @step_quot_fixed: Fixed step quotient value used for target quotient ++ * adjustment if use_dynamic_step_quot is not set. ++ * This parameter is only relevant for CPR4 controllers ++ * when using the per-online-core or per-temperature ++ * adjustments. ++ * @initial_temp_band: Temperature band used for calculation of base-line ++ * target quotients (fused). ++ * @use_dynamic_step_quot: Boolean value which indicates that margin adjustment ++ * of target quotient will be based on the step quotient ++ * calculated dynamically in hardware for each RO. ++ * @allow_core_count_adj: Core count adjustments are allowed for this controller ++ * @allow_temp_adj: Temperature based adjustments are allowed for ++ * this controller ++ * @allow_boost: Voltage boost allowed for this controller. ++ * @temp_band_count: Number of temperature bands used for temperature based ++ * adjustment logic ++ * @temp_points: Array of temperature points in decidegrees Celsius used ++ * to specify the ranges for selected temperature bands. ++ * The array must have (temp_band_count - 1) elements ++ * allocated. ++ * @temp_sensor_id_start: Start ID of temperature sensors used for temperature ++ * based adjustments. ++ * @temp_sensor_id_end: End ID of temperature sensors used for temperature ++ * based adjustments. ++ * @voltage_settling_time: The time in nanoseconds that it takes for the ++ * VDD supply voltage to settle after being increased or ++ * decreased by step_volt microvolts which is used when ++ * SDELTA voltage margin adjustments are applied. ++ * @cpr_global_setting: Global setting for this CPR controller ++ * @panic_regs_info: Array of panic registers information which provides the ++ * list of registers to dump when the device crashes. ++ * @panic_notifier: Notifier block registered to global panic notifier list. ++ * ++ * This structure contains both configuration and runtime state data. The ++ * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled, ++ * last_corner_was_closed_loop, cpr_suspended, aging_ref_adjust_volt, ++ * aging_required, aging_succeeded, and aging_failed are state variables. ++ * ++ * The apm* elements do not need to be initialized if the VDD supply managed by ++ * the CPR3 controller does not utilize an APM. ++ * ++ * The elements step_quot_fixed, initial_temp_band, allow_core_count_adj, ++ * allow_temp_adj and temp* need to be initialized for CPR4 controllers which ++ * are using per-online-core or per-temperature adjustments. ++ */ ++struct cpr3_controller { ++ struct device *dev; ++ const char *name; ++ int ctrl_id; ++ void __iomem *cpr_ctrl_base; ++ void __iomem *fuse_base; ++ void __iomem *aging_possible_reg; ++ struct list_head list; ++ struct cpr3_thread *thread; ++ int thread_count; ++ u8 *sensor_owner; ++ int sensor_count; ++ int soc_revision; ++ struct mutex lock; ++ struct regulator *vdd_regulator; ++ struct regulator *system_regulator; ++ struct regulator *mem_acc_regulator; ++ struct regulator *vdd_limit_regulator; ++ int system_supply_max_volt; ++ int mem_acc_threshold_volt; ++ int mem_acc_corner_map[CPR3_MEM_ACC_CORNERS]; ++ int mem_acc_crossover_volt; ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ struct clk *bus_clk; ++ int irq; ++ struct cpumask irq_affinity_mask; ++ struct notifier_block cpu_hotplug_notifier; ++ int ceiling_irq; ++ struct msm_apm_ctrl_dev *apm; ++ int apm_threshold_volt; ++ int apm_crossover_volt; ++ int apm_adj_volt; ++ enum msm_apm_supply apm_high_supply; ++ enum msm_apm_supply apm_low_supply; ++ int base_volt; ++ u32 corner_switch_delay_time; ++ u32 cpr_clock_rate; ++ u32 sensor_time; ++ u32 loop_time; ++ u32 up_down_delay_time; ++ u32 idle_clocks; ++ u32 step_quot_init_min; ++ u32 step_quot_init_max; ++ int step_volt; ++ u32 down_error_step_limit; ++ u32 up_error_step_limit; ++ enum cpr3_count_mode count_mode; ++ u32 count_repeat; ++ u32 proc_clock_throttle; ++ bool cpr_allowed_hw; ++ bool cpr_allowed_sw; ++ bool supports_hw_closed_loop; ++ bool use_hw_closed_loop; ++ enum cpr_controller_type ctrl_type; ++ bool saw_use_unit_mV; ++ struct cpr3_corner aggr_corner; ++ bool cpr_enabled; ++ bool last_corner_was_closed_loop; ++ bool cpr_suspended; ++ struct dentry *debugfs; ++ ++ int aging_ref_volt; ++ unsigned int aging_vdd_mode; ++ unsigned int aging_complete_vdd_mode; ++ int aging_ref_adjust_volt; ++ bool aging_required; ++ bool aging_succeeded; ++ bool aging_failed; ++ struct cpr3_aging_sensor_info *aging_sensor; ++ int aging_sensor_count; ++ u32 cur_sensor_state; ++ u32 aging_possible_mask; ++ u32 aging_possible_val; ++ ++ u32 step_quot_fixed; ++ u32 initial_temp_band; ++ bool use_dynamic_step_quot; ++ bool allow_core_count_adj; ++ bool allow_temp_adj; ++ bool allow_boost; ++ int temp_band_count; ++ int *temp_points; ++ u32 temp_sensor_id_start; ++ u32 temp_sensor_id_end; ++ u32 voltage_settling_time; ++ enum cpr_setting cpr_global_setting; ++ struct cpr3_panic_regs_info *panic_regs_info; ++ struct notifier_block panic_notifier; ++}; ++ ++/* Used for rounding voltages to the closest physically available set point. */ ++#define CPR3_ROUND(n, d) (DIV_ROUND_UP(n, d) * (d)) ++ ++#define cpr3_err(cpr3_thread, message, ...) \ ++ pr_err("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) ++#define cpr3_info(cpr3_thread, message, ...) \ ++ pr_info("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) ++#define cpr3_debug(cpr3_thread, message, ...) \ ++ pr_debug("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) ++ ++/* ++ * Offset subtracted from voltage corner values passed in from the regulator ++ * framework in order to get internal voltage corner values. This is needed ++ * since the regulator framework treats 0 as an error value at regulator ++ * registration time. ++ */ ++#define CPR3_CORNER_OFFSET 1 ++ ++#ifdef CONFIG_REGULATOR_CPR3 ++ ++int cpr3_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl); ++int cpr3_open_loop_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl); ++int cpr3_regulator_unregister(struct cpr3_controller *ctrl); ++int cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl); ++int cpr3_regulator_suspend(struct cpr3_controller *ctrl); ++int cpr3_regulator_resume(struct cpr3_controller *ctrl); ++ ++int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id, ++ u32 max_thread_id); ++int cpr3_map_fuse_base(struct cpr3_controller *ctrl, ++ struct platform_device *pdev); ++int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, ++ struct platform_device *pdev, u8 start, u8 end); ++int cpr3_read_fuse_param(void __iomem *fuse_base_addr, ++ const struct cpr3_fuse_param *param, u64 *param_value); ++int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse, ++ int fuse_len); ++u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x); ++int cpr3_parse_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out); ++int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out); ++int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out); ++int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg); ++int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max); ++int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max); ++int cpr3_parse_common_thread_data(struct cpr3_thread *thread); ++int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl); ++int cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl); ++int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg); ++void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg); ++int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg); ++void cpr3_print_quots(struct cpr3_regulator *vreg); ++int cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt); ++int cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, ++ int *fuse_volt); ++int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, ++ int *fuse_volt); ++int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg); ++int cpr3_quot_adjustment(int ro_scale, int volt_adjust); ++int cpr3_voltage_adjustment(int ro_scale, int quot_adjust); ++int cpr3_parse_closed_loop_voltage_adjustments(struct cpr3_regulator *vreg, ++ u64 *ro_sel, int *volt_adjust, ++ int *volt_adjust_fuse, int *ro_scale); ++int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg, ++ bool use_corner_band); ++int cpr3_apm_init(struct cpr3_controller *ctrl); ++int cpr3_mem_acc_init(struct cpr3_regulator *vreg); ++void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg); ++void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg); ++int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ int *fuse_volt_adjust); ++int cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, ++ bool is_cold); ++int cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp); ++bool cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg); ++ ++#else ++ ++static inline int cpr3_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int ++cpr3_open_loop_regulator_register(struct platform_device *pdev, ++ struct cpr3_controller *ctrl); ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int ++cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_regulator_suspend(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_regulator_resume(struct cpr3_controller *ctrl) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_get_thread_name(struct cpr3_thread *thread, ++ struct device_node *thread_node) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_allocate_threads(struct cpr3_controller *ctrl, ++ u32 min_thread_id, u32 max_thread_id) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_map_fuse_base(struct cpr3_controller *ctrl, ++ struct platform_device *pdev) ++{ ++ return -ENXIO; ++} ++ ++static inline int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, ++ struct platform_device *pdev, u8 start, u8 end) ++{ ++ return 0; ++} ++ ++static inline int cpr3_read_fuse_param(void __iomem *fuse_base_addr, ++ const struct cpr3_fuse_param *param, u64 *param_value) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_convert_open_loop_voltage_fuse(int ref_volt, ++ int step_volt, u32 fuse, int fuse_len) ++{ ++ return -EPERM; ++} ++ ++static inline u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x) ++{ ++ return 0; ++} ++ ++static inline int cpr3_parse_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_corner_band_array_property( ++ struct cpr3_regulator *vreg, const char *prop_name, ++ int tuple_size, u32 *out) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_thread_u32(struct cpr3_thread *thread, ++ const char *propname, u32 *out_value, u32 value_min, ++ u32 value_max) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, ++ const char *propname, u32 *out_value, u32 value_min, ++ u32 value_max) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_common_thread_data(struct cpr3_thread *thread) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ return -EPERM; ++} ++ ++static inline int ++cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline void cpr3_open_loop_voltage_as_ceiling( ++ struct cpr3_regulator *vreg) ++{ ++ return; ++} ++ ++static inline int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline void cpr3_print_quots(struct cpr3_regulator *vreg) ++{ ++ return; ++} ++ ++static inline int ++cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt) ++{ ++ return -EPERM; ++} ++ ++static inline int ++cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, ++ int *fuse_volt) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_adjust_fused_open_loop_voltages( ++ struct cpr3_regulator *vreg, int *fuse_volt) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ return -EPERM; ++} ++ ++static inline int cpr3_quot_adjustment(int ro_scale, int volt_adjust) ++{ ++ return 0; ++} ++ ++static inline int cpr3_voltage_adjustment(int ro_scale, int quot_adjust) ++{ ++ return 0; ++} ++ ++static inline int cpr3_parse_closed_loop_voltage_adjustments( ++ struct cpr3_regulator *vreg, u64 *ro_sel, ++ int *volt_adjust, int *volt_adjust_fuse, int *ro_scale) ++{ ++ return 0; ++} ++ ++static inline int cpr4_parse_core_count_temp_voltage_adj( ++ struct cpr3_regulator *vreg, bool use_corner_band) ++{ ++ return 0; ++} ++ ++static inline int cpr3_apm_init(struct cpr3_controller *ctrl) ++{ ++ return 0; ++} ++ ++static inline int cpr3_mem_acc_init(struct cpr3_regulator *vreg) ++{ ++ return 0; ++} ++ ++static inline void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) ++{ ++} ++ ++static inline void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) ++{ ++} ++ ++static inline int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ int *fuse_volt_adjust) ++{ ++ return 0; ++} ++ ++static inline int ++cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, ++ bool is_cold) ++{ ++ return 0; ++} ++ ++static inline bool ++cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg) ++{ ++ return false; ++} ++ ++static inline int ++cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp) ++{ ++ return 0; ++} ++#endif /* CONFIG_REGULATOR_CPR3 */ ++ ++#endif /* __REGULATOR_CPR_REGULATOR_H__ */ +diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c +new file mode 100644 +index 0000000000000..45493af714e25 +--- /dev/null ++++ b/drivers/regulator/cpr3-util.c +@@ -0,0 +1,2750 @@ ++/* ++ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/* ++ * This file contains utility functions to be used by platform specific CPR3 ++ * regulator drivers. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "cpr3-regulator.h" ++ ++#define BYTES_PER_FUSE_ROW 8 ++#define MAX_FUSE_ROW_BIT 63 ++ ++#define CPR3_CONSECUTIVE_UP_DOWN_MIN 0 ++#define CPR3_CONSECUTIVE_UP_DOWN_MAX 15 ++#define CPR3_UP_DOWN_THRESHOLD_MIN 0 ++#define CPR3_UP_DOWN_THRESHOLD_MAX 31 ++#define CPR3_STEP_QUOT_MIN 0 ++#define CPR3_STEP_QUOT_MAX 63 ++#define CPR3_IDLE_CLOCKS_MIN 0 ++#define CPR3_IDLE_CLOCKS_MAX 31 ++ ++/* This constant has units of uV/mV so 1000 corresponds to 100%. */ ++#define CPR3_AGING_DERATE_UNITY 1000 ++ ++/** ++ * cpr3_allocate_regulators() - allocate and initialize CPR3 regulators for a ++ * given thread based upon device tree data ++ * @thread: Pointer to the CPR3 thread ++ * ++ * This function allocates the thread->vreg array based upon the number of ++ * device tree regulator subnodes. It also initializes generic elements of each ++ * regulator struct such as name, of_node, and thread. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_allocate_regulators(struct cpr3_thread *thread) ++{ ++ struct device_node *node; ++ int i, rc; ++ ++ thread->vreg_count = 0; ++ ++ for_each_available_child_of_node(thread->of_node, node) { ++ thread->vreg_count++; ++ } ++ ++ thread->vreg = devm_kcalloc(thread->ctrl->dev, thread->vreg_count, ++ sizeof(*thread->vreg), GFP_KERNEL); ++ if (!thread->vreg) ++ return -ENOMEM; ++ ++ i = 0; ++ for_each_available_child_of_node(thread->of_node, node) { ++ thread->vreg[i].of_node = node; ++ thread->vreg[i].thread = thread; ++ ++ rc = of_property_read_string(node, "regulator-name", ++ &thread->vreg[i].name); ++ if (rc) { ++ dev_err(thread->ctrl->dev, "could not find regulator name, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ i++; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_allocate_threads() - allocate and initialize CPR3 threads for a given ++ * controller based upon device tree data ++ * @ctrl: Pointer to the CPR3 controller ++ * @min_thread_id: Minimum allowed hardware thread ID for this controller ++ * @max_thread_id: Maximum allowed hardware thread ID for this controller ++ * ++ * This function allocates the ctrl->thread array based upon the number of ++ * device tree thread subnodes. It also initializes generic elements of each ++ * thread struct such as thread_id, of_node, ctrl, and vreg array. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id, ++ u32 max_thread_id) ++{ ++ struct device *dev = ctrl->dev; ++ struct device_node *thread_node; ++ int i, j, rc; ++ ++ ctrl->thread_count = 0; ++ ++ for_each_available_child_of_node(dev->of_node, thread_node) { ++ ctrl->thread_count++; ++ } ++ ++ ctrl->thread = devm_kcalloc(dev, ctrl->thread_count, ++ sizeof(*ctrl->thread), GFP_KERNEL); ++ if (!ctrl->thread) ++ return -ENOMEM; ++ ++ i = 0; ++ for_each_available_child_of_node(dev->of_node, thread_node) { ++ ctrl->thread[i].of_node = thread_node; ++ ctrl->thread[i].ctrl = ctrl; ++ ++ rc = of_property_read_u32(thread_node, "qcom,cpr-thread-id", ++ &ctrl->thread[i].thread_id); ++ if (rc) { ++ dev_err(dev, "could not read DT property qcom,cpr-thread-id, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->thread[i].thread_id < min_thread_id || ++ ctrl->thread[i].thread_id > max_thread_id) { ++ dev_err(dev, "invalid thread id = %u; not within [%u, %u]\n", ++ ctrl->thread[i].thread_id, min_thread_id, ++ max_thread_id); ++ return -EINVAL; ++ } ++ ++ /* Verify that the thread ID is unique for all child nodes. */ ++ for (j = 0; j < i; j++) { ++ if (ctrl->thread[j].thread_id ++ == ctrl->thread[i].thread_id) { ++ dev_err(dev, "duplicate thread id = %u found\n", ++ ctrl->thread[i].thread_id); ++ return -EINVAL; ++ } ++ } ++ ++ rc = cpr3_allocate_regulators(&ctrl->thread[i]); ++ if (rc) ++ return rc; ++ ++ i++; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_map_fuse_base() - ioremap the base address of the fuse region ++ * @ctrl: Pointer to the CPR3 controller ++ * @pdev: Platform device pointer for the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_map_fuse_base(struct cpr3_controller *ctrl, ++ struct platform_device *pdev) ++{ ++ struct resource *res; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fuse_base"); ++ if (!res || !res->start) { ++ dev_err(&pdev->dev, "fuse base address is missing\n"); ++ return -ENXIO; ++ } ++ ++ ctrl->fuse_base = devm_ioremap(&pdev->dev, res->start, ++ resource_size(res)); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_read_tcsr_setting - reads the CPR setting bits from TCSR register ++ * @ctrl: Pointer to the CPR3 controller ++ * @pdev: Platform device pointer for the CPR3 controller ++ * @start: start bit in TCSR register ++ * @end: end bit in TCSR register ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, ++ struct platform_device *pdev, u8 start, u8 end) ++{ ++ struct resource *res; ++ void __iomem *tcsr_reg; ++ u32 val; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "cpr_tcsr_reg"); ++ if (!res || !res->start) ++ return 0; ++ ++ tcsr_reg = ioremap(res->start, resource_size(res)); ++ if (!tcsr_reg) { ++ dev_err(&pdev->dev, "tcsr ioremap failed\n"); ++ return 0; ++ } ++ ++ val = readl_relaxed(tcsr_reg); ++ val &= GENMASK(end, start); ++ val >>= start; ++ ++ switch (val) { ++ case 1: ++ ctrl->cpr_global_setting = CPR_DISABLED; ++ break; ++ case 2: ++ ctrl->cpr_global_setting = CPR_OPEN_LOOP_EN; ++ break; ++ case 3: ++ ctrl->cpr_global_setting = CPR_CLOSED_LOOP_EN; ++ break; ++ default: ++ ctrl->cpr_global_setting = CPR_DEFAULT; ++ } ++ ++ iounmap(tcsr_reg); ++ ++ return 0; ++} ++ ++/** ++ * cpr3_read_fuse_param() - reads a CPR3 fuse parameter out of eFuses ++ * @fuse_base_addr: Virtual memory address of the eFuse base address ++ * @param: Null terminated array of fuse param segments to read ++ * from ++ * @param_value: Output with value read from the eFuses ++ * ++ * This function reads from each of the parameter segments listed in the param ++ * array and concatenates their values together. Reading stops when an element ++ * is reached which has all 0 struct values. The total number of bits specified ++ * for the fuse parameter across all segments must be less than or equal to 64. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_read_fuse_param(void __iomem *fuse_base_addr, ++ const struct cpr3_fuse_param *param, u64 *param_value) ++{ ++ u64 fuse_val, val; ++ int bits; ++ int bits_total = 0; ++ ++ *param_value = 0; ++ ++ while (param->row || param->bit_start || param->bit_end) { ++ if (param->bit_start > param->bit_end ++ || param->bit_end > MAX_FUSE_ROW_BIT) { ++ pr_err("Invalid fuse parameter segment: row=%u, start=%u, end=%u\n", ++ param->row, param->bit_start, param->bit_end); ++ return -EINVAL; ++ } ++ ++ bits = param->bit_end - param->bit_start + 1; ++ if (bits_total + bits > 64) { ++ pr_err("Invalid fuse parameter segments; total bits = %d\n", ++ bits_total + bits); ++ return -EINVAL; ++ } ++ ++ fuse_val = readq_relaxed(fuse_base_addr ++ + param->row * BYTES_PER_FUSE_ROW); ++ val = (fuse_val >> param->bit_start) & ((1ULL << bits) - 1); ++ *param_value |= val << bits_total; ++ bits_total += bits; ++ ++ param++; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_convert_open_loop_voltage_fuse() - converts an open loop voltage fuse ++ * value into an absolute voltage with units of microvolts ++ * @ref_volt: Reference voltage in microvolts ++ * @step_volt: The step size in microvolts of the fuse LSB ++ * @fuse: Open loop voltage fuse value ++ * @fuse_len: The bit length of the fuse value ++ * ++ * The MSB of the fuse parameter corresponds to a sign bit. If it is set, then ++ * the lower bits correspond to the number of steps to go down from the ++ * reference voltage. If it is not set, then the lower bits correspond to the ++ * number of steps to go up from the reference voltage. ++ */ ++int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse, ++ int fuse_len) ++{ ++ int sign, steps; ++ ++ sign = (fuse & (1 << (fuse_len - 1))) ? -1 : 1; ++ steps = fuse & ((1 << (fuse_len - 1)) - 1); ++ ++ return ref_volt + sign * steps * step_volt; ++} ++ ++/** ++ * cpr3_interpolate() - performs linear interpolation ++ * @x1 Lower known x value ++ * @y1 Lower known y value ++ * @x2 Upper known x value ++ * @y2 Upper known y value ++ * @x Intermediate x value ++ * ++ * Returns y where (x, y) falls on the line between (x1, y1) and (x2, y2). ++ * It is required that x1 < x2, y1 <= y2, and x1 <= x <= x2. If these ++ * conditions are not met, then y2 will be returned. ++ */ ++u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x) ++{ ++ u64 temp; ++ ++ if (x1 >= x2 || y1 > y2 || x1 > x || x > x2) ++ return y2; ++ ++ temp = (x2 - x) * (y2 - y1); ++ do_div(temp, (u32)(x2 - x1)); ++ ++ return y2 - temp; ++} ++ ++/** ++ * cpr3_parse_array_property() - fill an array from a portion of the values ++ * specified for a device tree property ++ * @vreg: Pointer to the CPR3 regulator ++ * @prop_name: The name of the device tree property to read from ++ * @tuple_size: The number of elements in each tuple ++ * @out: Output data array which must be of size tuple_size ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that fuse combo and speed bin size elements are initialized. ++ * ++ * Three formats are supported for the device tree property: ++ * 1. Length == tuple_size ++ * (reading begins at index 0) ++ * 2. Length == tuple_size * vreg->fuse_combos_supported ++ * (reading begins at index tuple_size * vreg->fuse_combo) ++ * 3. Length == tuple_size * vreg->speed_bins_supported ++ * (reading begins at index tuple_size * vreg->speed_bin_fuse) ++ * ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ struct device_node *node = vreg->of_node; ++ int len = 0; ++ int i, offset, rc; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_size * sizeof(u32)) { ++ offset = 0; ++ } else if (len == tuple_size * vreg->fuse_combos_supported ++ * sizeof(u32)) { ++ offset = tuple_size * vreg->fuse_combo; ++ } else if (vreg->speed_bins_supported > 0 && ++ len == tuple_size * vreg->speed_bins_supported * sizeof(u32)) { ++ offset = tuple_size * vreg->speed_bin_fuse; ++ } else { ++ if (vreg->speed_bins_supported > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", ++ prop_name, len, ++ tuple_size * sizeof(u32), ++ tuple_size * vreg->speed_bins_supported ++ * sizeof(u32), ++ tuple_size * vreg->fuse_combos_supported ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_size * sizeof(u32), ++ tuple_size * vreg->fuse_combos_supported ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < tuple_size; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &out[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_corner_array_property() - fill a per-corner array from a portion ++ * of the values specified for a device tree property ++ * @vreg: Pointer to the CPR3 regulator ++ * @prop_name: The name of the device tree property to read from ++ * @tuple_size: The number of elements in each per-corner tuple ++ * @out: Output data array which must be of size: ++ * tuple_size * vreg->corner_count ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that fuse combo and speed bin size elements are initialized. ++ * ++ * Three formats are supported for the device tree property: ++ * 1. Length == tuple_size * vreg->corner_count ++ * (reading begins at index 0) ++ * 2. Length == tuple_size * vreg->fuse_combo_corner_sum ++ * (reading begins at index tuple_size * vreg->fuse_combo_offset) ++ * 3. Length == tuple_size * vreg->speed_bin_corner_sum ++ * (reading begins at index tuple_size * vreg->speed_bin_offset) ++ * ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ struct device_node *node = vreg->of_node; ++ int len = 0; ++ int i, offset, rc; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_size * vreg->corner_count * sizeof(u32)) { ++ offset = 0; ++ } else if (len == tuple_size * vreg->fuse_combo_corner_sum ++ * sizeof(u32)) { ++ offset = tuple_size * vreg->fuse_combo_offset; ++ } else if (vreg->speed_bin_corner_sum > 0 && ++ len == tuple_size * vreg->speed_bin_corner_sum * sizeof(u32)) { ++ offset = tuple_size * vreg->speed_bin_offset; ++ } else { ++ if (vreg->speed_bin_corner_sum > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_count * sizeof(u32), ++ tuple_size * vreg->speed_bin_corner_sum ++ * sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_sum ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_count * sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_sum ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < tuple_size * vreg->corner_count; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &out[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_corner_band_array_property() - fill a per-corner band array ++ * from a portion of the values specified for a device tree ++ * property ++ * @vreg: Pointer to the CPR3 regulator ++ * @prop_name: The name of the device tree property to read from ++ * @tuple_size: The number of elements in each per-corner band tuple ++ * @out: Output data array which must be of size: ++ * tuple_size * vreg->corner_band_count ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that fuse combo and speed bin size elements are initialized. ++ * In addition, corner band fuse combo and speed bin sum and offset elements ++ * must be initialized prior to executing this function. ++ * ++ * Three formats are supported for the device tree property: ++ * 1. Length == tuple_size * vreg->corner_band_count ++ * (reading begins at index 0) ++ * 2. Length == tuple_size * vreg->fuse_combo_corner_band_sum ++ * (reading begins at index tuple_size * ++ * vreg->fuse_combo_corner_band_offset) ++ * 3. Length == tuple_size * vreg->speed_bin_corner_band_sum ++ * (reading begins at index tuple_size * ++ * vreg->speed_bin_corner_band_offset) ++ * ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg, ++ const char *prop_name, int tuple_size, u32 *out) ++{ ++ struct device_node *node = vreg->of_node; ++ int len = 0; ++ int i, offset, rc; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_size * vreg->corner_band_count * sizeof(u32)) { ++ offset = 0; ++ } else if (len == tuple_size * vreg->fuse_combo_corner_band_sum ++ * sizeof(u32)) { ++ offset = tuple_size * vreg->fuse_combo_corner_band_offset; ++ } else if (vreg->speed_bin_corner_band_sum > 0 && ++ len == tuple_size * vreg->speed_bin_corner_band_sum * ++ sizeof(u32)) { ++ offset = tuple_size * vreg->speed_bin_corner_band_offset; ++ } else { ++ if (vreg->speed_bin_corner_band_sum > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_band_count * ++ sizeof(u32), ++ tuple_size * vreg->speed_bin_corner_band_sum ++ * sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_band_sum ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_size * vreg->corner_band_count * ++ sizeof(u32), ++ tuple_size * vreg->fuse_combo_corner_band_sum ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < tuple_size * vreg->corner_band_count; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &out[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_common_corner_data() - parse common CPR3 properties relating to ++ * the corners supported by a CPR3 regulator from device tree ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function reads, validates, and utilizes the following device tree ++ * properties: qcom,cpr-fuse-corners, qcom,cpr-fuse-combos, qcom,cpr-speed-bins, ++ * qcom,cpr-speed-bin-corners, qcom,cpr-corners, qcom,cpr-voltage-ceiling, ++ * qcom,cpr-voltage-floor, qcom,corner-frequencies, ++ * and qcom,cpr-corner-fmax-map. ++ * ++ * It initializes these CPR3 regulator elements: corner, corner_count, ++ * fuse_combos_supported, fuse_corner_map, and speed_bins_supported. It ++ * initializes these elements for each corner: ceiling_volt, floor_volt, ++ * proc_freq, and cpr_fuse_corner. ++ * ++ * It requires that the following CPR3 regulator elements be initialized before ++ * being called: fuse_corner_count, fuse_combo, and speed_bin_fuse. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ u32 max_fuse_combos, fuse_corners, aging_allowed = 0; ++ u32 max_speed_bins = 0; ++ u32 *combo_corners; ++ u32 *speed_bin_corners; ++ u32 *temp; ++ int i, j, rc; ++ ++ rc = of_property_read_u32(node, "qcom,cpr-fuse-corners", &fuse_corners); ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-fuse-corners, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (vreg->fuse_corner_count != fuse_corners) { ++ cpr3_err(vreg, "device tree config supports %d fuse corners but the hardware has %d fuse corners\n", ++ fuse_corners, vreg->fuse_corner_count); ++ return -EINVAL; ++ } ++ ++ rc = of_property_read_u32(node, "qcom,cpr-fuse-combos", ++ &max_fuse_combos); ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-fuse-combos, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* ++ * Sanity check against arbitrarily large value to avoid excessive ++ * memory allocation. ++ */ ++ if (max_fuse_combos > 100 || max_fuse_combos == 0) { ++ cpr3_err(vreg, "qcom,cpr-fuse-combos is invalid: %u\n", ++ max_fuse_combos); ++ return -EINVAL; ++ } ++ ++ if (vreg->fuse_combo >= max_fuse_combos) { ++ cpr3_err(vreg, "device tree config supports fuse combos 0-%u but the hardware has combo %d\n", ++ max_fuse_combos - 1, vreg->fuse_combo); ++ BUG_ON(1); ++ return -EINVAL; ++ } ++ ++ vreg->fuse_combos_supported = max_fuse_combos; ++ ++ of_property_read_u32(node, "qcom,cpr-speed-bins", &max_speed_bins); ++ ++ /* ++ * Sanity check against arbitrarily large value to avoid excessive ++ * memory allocation. ++ */ ++ if (max_speed_bins > 100) { ++ cpr3_err(vreg, "qcom,cpr-speed-bins is invalid: %u\n", ++ max_speed_bins); ++ return -EINVAL; ++ } ++ ++ if (max_speed_bins && vreg->speed_bin_fuse >= max_speed_bins) { ++ cpr3_err(vreg, "device tree config supports speed bins 0-%u but the hardware has speed bin %d\n", ++ max_speed_bins - 1, vreg->speed_bin_fuse); ++ BUG(); ++ return -EINVAL; ++ } ++ ++ vreg->speed_bins_supported = max_speed_bins; ++ ++ combo_corners = kcalloc(vreg->fuse_combos_supported, ++ sizeof(*combo_corners), GFP_KERNEL); ++ if (!combo_corners) ++ return -ENOMEM; ++ ++ rc = of_property_read_u32_array(node, "qcom,cpr-corners", combo_corners, ++ vreg->fuse_combos_supported); ++ if (rc == -EOVERFLOW) { ++ /* Single value case */ ++ rc = of_property_read_u32(node, "qcom,cpr-corners", ++ combo_corners); ++ for (i = 1; i < vreg->fuse_combos_supported; i++) ++ combo_corners[i] = combo_corners[0]; ++ } ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-corners, rc=%d\n", ++ rc); ++ kfree(combo_corners); ++ return rc; ++ } ++ ++ vreg->fuse_combo_offset = 0; ++ vreg->fuse_combo_corner_sum = 0; ++ for (i = 0; i < vreg->fuse_combos_supported; i++) { ++ vreg->fuse_combo_corner_sum += combo_corners[i]; ++ if (i < vreg->fuse_combo) ++ vreg->fuse_combo_offset += combo_corners[i]; ++ } ++ ++ vreg->corner_count = combo_corners[vreg->fuse_combo]; ++ ++ kfree(combo_corners); ++ ++ vreg->speed_bin_offset = 0; ++ vreg->speed_bin_corner_sum = 0; ++ if (vreg->speed_bins_supported > 0) { ++ speed_bin_corners = kcalloc(vreg->speed_bins_supported, ++ sizeof(*speed_bin_corners), GFP_KERNEL); ++ if (!speed_bin_corners) ++ return -ENOMEM; ++ ++ rc = of_property_read_u32_array(node, ++ "qcom,cpr-speed-bin-corners", speed_bin_corners, ++ vreg->speed_bins_supported); ++ if (rc) { ++ cpr3_err(vreg, "error reading property qcom,cpr-speed-bin-corners, rc=%d\n", ++ rc); ++ kfree(speed_bin_corners); ++ return rc; ++ } ++ ++ for (i = 0; i < vreg->speed_bins_supported; i++) { ++ vreg->speed_bin_corner_sum += speed_bin_corners[i]; ++ if (i < vreg->speed_bin_fuse) ++ vreg->speed_bin_offset += speed_bin_corners[i]; ++ } ++ ++ if (speed_bin_corners[vreg->speed_bin_fuse] ++ != vreg->corner_count) { ++ cpr3_err(vreg, "qcom,cpr-corners and qcom,cpr-speed-bin-corners conflict on number of corners: %d vs %u\n", ++ vreg->corner_count, ++ speed_bin_corners[vreg->speed_bin_fuse]); ++ kfree(speed_bin_corners); ++ return -EINVAL; ++ } ++ ++ kfree(speed_bin_corners); ++ } ++ ++ vreg->corner = devm_kcalloc(ctrl->dev, vreg->corner_count, ++ sizeof(*vreg->corner), GFP_KERNEL); ++ temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); ++ if (!vreg->corner || !temp) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-ceiling", ++ 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) { ++ vreg->corner[i].ceiling_volt ++ = CPR3_ROUND(temp[i], ctrl->step_volt); ++ vreg->corner[i].abs_ceiling_volt = vreg->corner[i].ceiling_volt; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-floor", ++ 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].floor_volt ++ = CPR3_ROUND(temp[i], ctrl->step_volt); ++ ++ /* Validate ceiling and floor values */ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (vreg->corner[i].floor_volt ++ > vreg->corner[i].ceiling_volt) { ++ cpr3_err(vreg, "CPR floor[%d]=%d > ceiling[%d]=%d uV\n", ++ i, vreg->corner[i].floor_volt, ++ i, vreg->corner[i].ceiling_volt); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ } ++ ++ /* Load optional system-supply voltages */ ++ if (of_find_property(vreg->of_node, "qcom,system-voltage", NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,system-voltage", 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].system_volt = temp[i]; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,corner-frequencies", ++ 1, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].proc_freq = temp[i]; ++ ++ /* Validate frequencies */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ if (vreg->corner[i].proc_freq ++ < vreg->corner[i - 1].proc_freq) { ++ cpr3_err(vreg, "invalid frequency: freq[%d]=%u < freq[%d]=%u\n", ++ i, vreg->corner[i].proc_freq, i - 1, ++ vreg->corner[i - 1].proc_freq); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ } ++ ++ vreg->fuse_corner_map = devm_kcalloc(ctrl->dev, vreg->fuse_corner_count, ++ sizeof(*vreg->fuse_corner_map), GFP_KERNEL); ++ if (!vreg->fuse_corner_map) { ++ rc = -ENOMEM; ++ goto free_temp; ++ } ++ ++ rc = cpr3_parse_array_property(vreg, "qcom,cpr-corner-fmax-map", ++ vreg->fuse_corner_count, temp); ++ if (rc) ++ goto free_temp; ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ vreg->fuse_corner_map[i] = temp[i] - CPR3_CORNER_OFFSET; ++ if (temp[i] < CPR3_CORNER_OFFSET ++ || temp[i] > vreg->corner_count + CPR3_CORNER_OFFSET) { ++ cpr3_err(vreg, "invalid corner value specified in qcom,cpr-corner-fmax-map: %u\n", ++ temp[i]); ++ rc = -EINVAL; ++ goto free_temp; ++ } else if (i > 0 && temp[i - 1] >= temp[i]) { ++ cpr3_err(vreg, "invalid corner %u less than or equal to previous corner %u\n", ++ temp[i], temp[i - 1]); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ } ++ if (temp[vreg->fuse_corner_count - 1] != vreg->corner_count) ++ cpr3_debug(vreg, "Note: highest Fmax corner %u in qcom,cpr-corner-fmax-map does not match highest supported corner %d\n", ++ temp[vreg->fuse_corner_count - 1], ++ vreg->corner_count); ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ for (j = 0; j < vreg->fuse_corner_count; j++) { ++ if (i + CPR3_CORNER_OFFSET <= temp[j]) { ++ vreg->corner[i].cpr_fuse_corner = j; ++ break; ++ } ++ } ++ if (j == vreg->fuse_corner_count) { ++ /* ++ * Handle the case where the highest fuse corner maps ++ * to a corner below the highest corner. ++ */ ++ vreg->corner[i].cpr_fuse_corner ++ = vreg->fuse_corner_count - 1; ++ } ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,allow-aging-voltage-adjustment", NULL)) { ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,allow-aging-voltage-adjustment", ++ 1, &aging_allowed); ++ if (rc) ++ goto free_temp; ++ ++ vreg->aging_allowed = aging_allowed; ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,allow-aging-open-loop-voltage-adjustment", NULL)) { ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,allow-aging-open-loop-voltage-adjustment", ++ 1, &aging_allowed); ++ if (rc) ++ goto free_temp; ++ ++ vreg->aging_allow_open_loop_adj = aging_allowed; ++ } ++ ++ if (vreg->aging_allowed) { ++ if (ctrl->aging_ref_volt <= 0) { ++ cpr3_err(ctrl, "qcom,cpr-aging-ref-voltage must be specified\n"); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-aging-max-voltage-adjustment", ++ 1, &vreg->aging_max_adjust_volt); ++ if (rc) ++ goto free_temp; ++ ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-aging-ref-corner", 1, &vreg->aging_corner); ++ if (rc) { ++ goto free_temp; ++ } else if (vreg->aging_corner < CPR3_CORNER_OFFSET ++ || vreg->aging_corner > vreg->corner_count - 1 ++ + CPR3_CORNER_OFFSET) { ++ cpr3_err(vreg, "aging reference corner=%d not in range [%d, %d]\n", ++ vreg->aging_corner, CPR3_CORNER_OFFSET, ++ vreg->corner_count - 1 + CPR3_CORNER_OFFSET); ++ rc = -EINVAL; ++ goto free_temp; ++ } ++ vreg->aging_corner -= CPR3_CORNER_OFFSET; ++ ++ if (of_find_property(vreg->of_node, "qcom,cpr-aging-derate", ++ NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-aging-derate", 1, temp); ++ if (rc) ++ goto free_temp; ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].aging_derate = temp[i]; ++ } else { ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].aging_derate ++ = CPR3_AGING_DERATE_UNITY; ++ } ++ } ++ ++free_temp: ++ kfree(temp); ++ return rc; ++} ++ ++/** ++ * cpr3_parse_thread_u32() - parse the specified property from the CPR3 thread's ++ * device tree node and verify that it is within the allowed limits ++ * @thread: Pointer to the CPR3 thread ++ * @propname: The name of the device tree property to read ++ * @out_value: The output pointer to fill with the value read ++ * @value_min: The minimum allowed property value ++ * @value_max: The maximum allowed property value ++ * ++ * This function prints a verbose error message if the property is missing or ++ * has a value which is not within the specified range. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max) ++{ ++ int rc; ++ ++ rc = of_property_read_u32(thread->of_node, propname, out_value); ++ if (rc) { ++ cpr3_err(thread->ctrl, "thread %u error reading property %s, rc=%d\n", ++ thread->thread_id, propname, rc); ++ return rc; ++ } ++ ++ if (*out_value < value_min || *out_value > value_max) { ++ cpr3_err(thread->ctrl, "thread %u %s=%u is invalid; allowed range: [%u, %u]\n", ++ thread->thread_id, propname, *out_value, value_min, ++ value_max); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_ctrl_u32() - parse the specified property from the CPR3 ++ * controller's device tree node and verify that it is within the ++ * allowed limits ++ * @ctrl: Pointer to the CPR3 controller ++ * @propname: The name of the device tree property to read ++ * @out_value: The output pointer to fill with the value read ++ * @value_min: The minimum allowed property value ++ * @value_max: The maximum allowed property value ++ * ++ * This function prints a verbose error message if the property is missing or ++ * has a value which is not within the specified range. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname, ++ u32 *out_value, u32 value_min, u32 value_max) ++{ ++ int rc; ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, propname, out_value); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property %s, rc=%d\n", ++ propname, rc); ++ return rc; ++ } ++ ++ if (*out_value < value_min || *out_value > value_max) { ++ cpr3_err(ctrl, "%s=%u is invalid; allowed range: [%u, %u]\n", ++ propname, *out_value, value_min, value_max); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_parse_common_thread_data() - parse common CPR3 thread properties from ++ * device tree ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_common_thread_data(struct cpr3_thread *thread) ++{ ++ int rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-up", ++ &thread->consecutive_up, CPR3_CONSECUTIVE_UP_DOWN_MIN, ++ CPR3_CONSECUTIVE_UP_DOWN_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-down", ++ &thread->consecutive_down, CPR3_CONSECUTIVE_UP_DOWN_MIN, ++ CPR3_CONSECUTIVE_UP_DOWN_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-up-threshold", ++ &thread->up_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, ++ CPR3_UP_DOWN_THRESHOLD_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-down-threshold", ++ &thread->down_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, ++ CPR3_UP_DOWN_THRESHOLD_MAX); ++ if (rc) ++ return rc; ++ ++ return rc; ++} ++ ++/** ++ * cpr3_parse_irq_affinity() - parse CPR IRQ affinity information ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_parse_irq_affinity(struct cpr3_controller *ctrl) ++{ ++ struct device_node *cpu_node; ++ int i, cpu; ++ int len = 0; ++ ++ if (!of_find_property(ctrl->dev->of_node, "qcom,cpr-interrupt-affinity", ++ &len)) { ++ /* No IRQ affinity required */ ++ return 0; ++ } ++ ++ len /= sizeof(u32); ++ ++ for (i = 0; i < len; i++) { ++ cpu_node = of_parse_phandle(ctrl->dev->of_node, ++ "qcom,cpr-interrupt-affinity", i); ++ if (!cpu_node) { ++ cpr3_err(ctrl, "could not find CPU node %d\n", i); ++ return -EINVAL; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ if (of_get_cpu_node(cpu, NULL) == cpu_node) { ++ cpumask_set_cpu(cpu, &ctrl->irq_affinity_mask); ++ break; ++ } ++ } ++ of_node_put(cpu_node); ++ } ++ ++ return 0; ++} ++ ++static int cpr3_panic_notifier_init(struct cpr3_controller *ctrl) ++{ ++ struct device_node *node = ctrl->dev->of_node; ++ struct cpr3_panic_regs_info *panic_regs_info; ++ struct cpr3_reg_info *regs; ++ int i, reg_count, len, rc = 0; ++ ++ if (!of_find_property(node, "qcom,cpr-panic-reg-addr-list", &len)) { ++ /* panic register address list not specified */ ++ return rc; ++ } ++ ++ reg_count = len / sizeof(u32); ++ if (!reg_count) { ++ cpr3_err(ctrl, "qcom,cpr-panic-reg-addr-list has invalid len = %d\n", ++ len); ++ return -EINVAL; ++ } ++ ++ if (!of_find_property(node, "qcom,cpr-panic-reg-name-list", NULL)) { ++ cpr3_err(ctrl, "property qcom,cpr-panic-reg-name-list not specified\n"); ++ return -EINVAL; ++ } ++ ++ len = of_property_count_strings(node, "qcom,cpr-panic-reg-name-list"); ++ if (reg_count != len) { ++ cpr3_err(ctrl, "qcom,cpr-panic-reg-name-list should have %d strings\n", ++ reg_count); ++ return -EINVAL; ++ } ++ ++ panic_regs_info = devm_kzalloc(ctrl->dev, sizeof(*panic_regs_info), ++ GFP_KERNEL); ++ if (!panic_regs_info) ++ return -ENOMEM; ++ ++ regs = devm_kcalloc(ctrl->dev, reg_count, sizeof(*regs), GFP_KERNEL); ++ if (!regs) ++ return -ENOMEM; ++ ++ for (i = 0; i < reg_count; i++) { ++ rc = of_property_read_string_index(node, ++ "qcom,cpr-panic-reg-name-list", i, ++ &(regs[i].name)); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-name-list, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32_index(node, ++ "qcom,cpr-panic-reg-addr-list", i, ++ &(regs[i].addr)); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-addr-list, rc=%d\n", ++ rc); ++ return rc; ++ } ++ regs[i].virt_addr = devm_ioremap(ctrl->dev, regs[i].addr, 0x4); ++ if (!regs[i].virt_addr) { ++ pr_err("Unable to map panic register addr 0x%08x\n", ++ regs[i].addr); ++ return -EINVAL; ++ } ++ regs[i].value = 0xFFFFFFFF; ++ } ++ ++ panic_regs_info->reg_count = reg_count; ++ panic_regs_info->regs = regs; ++ ctrl->panic_regs_info = panic_regs_info; ++ ++ return rc; ++} ++ ++/** ++ * cpr3_parse_common_ctrl_data() - parse common CPR3 controller properties from ++ * device tree ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-sensor-time", ++ &ctrl->sensor_time, 0, UINT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-loop-time", ++ &ctrl->loop_time, 0, UINT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-idle-cycles", ++ &ctrl->idle_clocks, CPR3_IDLE_CLOCKS_MIN, ++ CPR3_IDLE_CLOCKS_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-min", ++ &ctrl->step_quot_init_min, CPR3_STEP_QUOT_MIN, ++ CPR3_STEP_QUOT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-max", ++ &ctrl->step_quot_init_max, CPR3_STEP_QUOT_MIN, ++ CPR3_STEP_QUOT_MAX); ++ if (rc) ++ return rc; ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step", ++ &ctrl->step_volt); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n", ++ rc); ++ return rc; ++ } ++ if (ctrl->step_volt <= 0) { ++ cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n", ++ ctrl->step_volt); ++ return -EINVAL; ++ } ++ ++ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-count-mode", ++ &ctrl->count_mode, CPR3_COUNT_MODE_ALL_AT_ONCE_MIN, ++ CPR3_COUNT_MODE_STAGGERED); ++ if (rc) ++ return rc; ++ ++ /* Count repeat is optional */ ++ ctrl->count_repeat = 0; ++ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-count-repeat", ++ &ctrl->count_repeat); ++ ++ ctrl->cpr_allowed_sw = ++ of_property_read_bool(ctrl->dev->of_node, "qcom,cpr-enable") || ++ ctrl->cpr_global_setting == CPR_CLOSED_LOOP_EN; ++ ++ rc = cpr3_parse_irq_affinity(ctrl); ++ if (rc) ++ return rc; ++ ++ /* Aging reference voltage is optional */ ++ ctrl->aging_ref_volt = 0; ++ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-aging-ref-voltage", ++ &ctrl->aging_ref_volt); ++ ++ /* Aging possible bitmask is optional */ ++ ctrl->aging_possible_mask = 0; ++ of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-aging-allowed-reg-mask", ++ &ctrl->aging_possible_mask); ++ ++ if (ctrl->aging_possible_mask) { ++ /* ++ * Aging possible register value required if bitmask is ++ * specified ++ */ ++ rc = cpr3_parse_ctrl_u32(ctrl, ++ "qcom,cpr-aging-allowed-reg-value", ++ &ctrl->aging_possible_val, 0, UINT_MAX); ++ if (rc) ++ return rc; ++ } ++ ++ if (of_find_property(ctrl->dev->of_node, "clock-names", NULL)) { ++ ctrl->core_clk = devm_clk_get(ctrl->dev, "core_clk"); ++ if (IS_ERR(ctrl->core_clk)) { ++ rc = PTR_ERR(ctrl->core_clk); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable request core clock, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_panic_notifier_init(ctrl); ++ if (rc) ++ return rc; ++ ++ if (of_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { ++ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd"); ++ if (IS_ERR(ctrl->vdd_regulator)) { ++ rc = PTR_ERR(ctrl->vdd_regulator); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } else { ++ cpr3_err(ctrl, "vdd supply is not defined\n"); ++ return -ENODEV; ++ } ++ ++ ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev, ++ "system"); ++ if (IS_ERR(ctrl->system_regulator)) { ++ rc = PTR_ERR(ctrl->system_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->system_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } ++ ++ ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev, ++ "mem-acc"); ++ if (IS_ERR(ctrl->mem_acc_regulator)) { ++ rc = PTR_ERR(ctrl->mem_acc_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->mem_acc_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_parse_open_loop_common_ctrl_data() - parse common open loop CPR3 ++ * controller properties from device tree ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step", ++ &ctrl->step_volt); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->step_volt <= 0) { ++ cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n", ++ ctrl->step_volt); ++ return -EINVAL; ++ } ++ ++ if (of_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { ++ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd"); ++ if (IS_ERR(ctrl->vdd_regulator)) { ++ rc = PTR_ERR(ctrl->vdd_regulator); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } else { ++ cpr3_err(ctrl, "vdd supply is not defined\n"); ++ return -ENODEV; ++ } ++ ++ ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev, ++ "system"); ++ if (IS_ERR(ctrl->system_regulator)) { ++ rc = PTR_ERR(ctrl->system_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->system_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } else { ++ rc = regulator_enable(ctrl->system_regulator); ++ } ++ ++ ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev, ++ "mem-acc"); ++ if (IS_ERR(ctrl->mem_acc_regulator)) { ++ rc = PTR_ERR(ctrl->mem_acc_regulator); ++ if (rc != -EPROBE_DEFER) { ++ rc = 0; ++ ctrl->mem_acc_regulator = NULL; ++ } else { ++ return rc; ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_limit_open_loop_voltages() - modify the open-loop voltage of each corner ++ * so that it fits within the floor to ceiling ++ * voltage range of the corner ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function clips the open-loop voltage for each corner so that it is ++ * limited to the floor to ceiling range. It also rounds each open-loop voltage ++ * so that it corresponds to a set point available to the underlying regulator. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ int i, volt; ++ ++ cpr3_debug(vreg, "open-loop voltages after trimming and rounding:\n"); ++ for (i = 0; i < vreg->corner_count; i++) { ++ volt = CPR3_ROUND(vreg->corner[i].open_loop_volt, ++ vreg->thread->ctrl->step_volt); ++ if (volt < vreg->corner[i].floor_volt) ++ volt = vreg->corner[i].floor_volt; ++ else if (volt > vreg->corner[i].ceiling_volt) ++ volt = vreg->corner[i].ceiling_volt; ++ vreg->corner[i].open_loop_volt = volt; ++ cpr3_debug(vreg, "corner[%2d]: open-loop=%d uV\n", i, volt); ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr3_open_loop_voltage_as_ceiling() - configures the ceiling voltage for each ++ * corner to equal the open-loop voltage if the relevant device ++ * tree property is found for the CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function assumes that the the open-loop voltage for each corner has ++ * already been rounded to the nearest allowed set point and that it falls ++ * within the floor to ceiling range. ++ * ++ * Return: none ++ */ ++void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg) ++{ ++ int i; ++ ++ if (!of_property_read_bool(vreg->of_node, ++ "qcom,cpr-scaled-open-loop-voltage-as-ceiling")) ++ return; ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].ceiling_volt ++ = vreg->corner[i].open_loop_volt; ++} ++ ++/** ++ * cpr3_limit_floor_voltages() - raise the floor voltage of each corner so that ++ * the optional maximum floor to ceiling voltage range specified in ++ * device tree is satisfied ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function also ensures that the open-loop voltage for each corner falls ++ * within the final floor to ceiling voltage range and that floor voltages ++ * increase monotonically. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg) ++{ ++ char *prop = "qcom,cpr-floor-to-ceiling-max-range"; ++ int i, floor_new; ++ u32 *floor_range; ++ int rc = 0; ++ ++ if (!of_find_property(vreg->of_node, prop, NULL)) ++ goto enforce_monotonicity; ++ ++ floor_range = kcalloc(vreg->corner_count, sizeof(*floor_range), ++ GFP_KERNEL); ++ if (!floor_range) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, prop, 1, floor_range); ++ if (rc) ++ goto free_floor_adjust; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if ((s32)floor_range[i] >= 0) { ++ floor_new = CPR3_ROUND(vreg->corner[i].ceiling_volt ++ - floor_range[i], ++ vreg->thread->ctrl->step_volt); ++ ++ vreg->corner[i].floor_volt = max(floor_new, ++ vreg->corner[i].floor_volt); ++ if (vreg->corner[i].open_loop_volt ++ < vreg->corner[i].floor_volt) ++ vreg->corner[i].open_loop_volt ++ = vreg->corner[i].floor_volt; ++ } ++ } ++ ++free_floor_adjust: ++ kfree(floor_range); ++ ++enforce_monotonicity: ++ /* Ensure that floor voltages increase monotonically. */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ if (vreg->corner[i].floor_volt ++ < vreg->corner[i - 1].floor_volt) { ++ cpr3_debug(vreg, "corner %d floor voltage=%d uV < corner %d voltage=%d uV; overriding: corner %d voltage=%d\n", ++ i, vreg->corner[i].floor_volt, ++ i - 1, vreg->corner[i - 1].floor_volt, ++ i, vreg->corner[i - 1].floor_volt); ++ vreg->corner[i].floor_volt ++ = vreg->corner[i - 1].floor_volt; ++ ++ if (vreg->corner[i].open_loop_volt ++ < vreg->corner[i].floor_volt) ++ vreg->corner[i].open_loop_volt ++ = vreg->corner[i].floor_volt; ++ if (vreg->corner[i].ceiling_volt ++ < vreg->corner[i].floor_volt) ++ vreg->corner[i].ceiling_volt ++ = vreg->corner[i].floor_volt; ++ } ++ } ++ ++ return rc; ++} ++ ++/** ++ * cpr3_print_quots() - print CPR target quotients into the kernel log for ++ * debugging purposes ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: none ++ */ ++void cpr3_print_quots(struct cpr3_regulator *vreg) ++{ ++ int i, j, pos; ++ size_t buflen; ++ char *buf; ++ ++ buflen = sizeof(*buf) * CPR3_RO_COUNT * (MAX_CHARS_PER_INT + 2); ++ buf = kzalloc(buflen, GFP_KERNEL); ++ if (!buf) ++ return; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ for (j = 0, pos = 0; j < CPR3_RO_COUNT; j++) ++ pos += scnprintf(buf + pos, buflen - pos, " %u", ++ vreg->corner[i].target_quot[j]); ++ cpr3_debug(vreg, "target quots[%2d]:%s\n", i, buf); ++ } ++ ++ kfree(buf); ++} ++ ++/** ++ * cpr3_determine_part_type() - determine the part type (SS/TT/FF). ++ * ++ * qcom,cpr-part-types prop tells the number of part types for which correction ++ * voltages are different. Another prop qcom,cpr-parts-voltage will contain the ++ * open loop fuse voltage which will be compared with this part voltage ++ * and accordingly part type will de determined. ++ * ++ * if qcom,cpr-part-types has value n, then qcom,cpr-parts-voltage will be ++ * array of n - 1 elements which will contain the voltage in increasing order. ++ * This function compares the fused volatge with all these voltage and returns ++ * the first index for which the fused volatge is greater. ++ * ++ * @vreg: Pointer to the CPR3 regulator ++ * @fuse_volt: fused open loop voltage which will be compared with ++ * qcom,cpr-parts-voltage array ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt) ++{ ++ int i, rc, len; ++ u32 volt; ++ int soc_version_major; ++ char prop_name[100]; ++ const char prop_name_def[] = "qcom,cpr-parts-voltage"; ++ const char prop_name_v2[] = "qcom,cpr-parts-voltage-v2"; ++ ++ soc_version_major = read_ipq_soc_version_major(); ++ BUG_ON(soc_version_major <= 0); ++ ++ if (of_property_read_u32(vreg->of_node, "qcom,cpr-part-types", ++ &vreg->part_type_supported)) ++ return 0; ++ ++ if (soc_version_major > 1) ++ strlcpy(prop_name, prop_name_v2, sizeof(prop_name_v2)); ++ else ++ strlcpy(prop_name, prop_name_def, sizeof(prop_name_def)); ++ ++ if (!of_find_property(vreg->of_node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len != (vreg->part_type_supported - 1) * sizeof(u32)) { ++ cpr3_err(vreg, "wrong len in qcom,cpr-parts-voltage\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < vreg->part_type_supported - 1; i++) { ++ rc = of_property_read_u32_index(vreg->of_node, ++ prop_name, i, &volt); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ ++ if (fuse_volt < volt) ++ break; ++ } ++ ++ vreg->part_type = i; ++ return 0; ++} ++ ++int cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, ++ int *fuse_volt) ++{ ++ int i, rc, prev_volt; ++ int *volt_adjust; ++ char prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (vreg->part_type_supported) { ++ if (soc_version_major > 1) ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-v2-%d", ++ vreg->part_type); ++ else ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-%d", ++ vreg->part_type); ++ } else { ++ strlcpy(prop_str, "qcom,cpr-cold-temp-voltage-adjustment", ++ sizeof(prop_str)); ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ cpr3_info(vreg, "No cold temperature adjustment required.\n"); ++ return 0; ++ } ++ ++ volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ if (!volt_adjust) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, prop_str, ++ vreg->fuse_corner_count, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load cold temp voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (volt_adjust[i]) { ++ prev_volt = fuse_volt[i]; ++ fuse_volt[i] += volt_adjust[i]; ++ cpr3_debug(vreg, ++ "adjusted fuse corner %d open-loop voltage: %d -> %d uV\n", ++ i, prev_volt, fuse_volt[i]); ++ } ++ } ++ ++done: ++ kfree(volt_adjust); ++ return rc; ++} ++ ++/** ++ * cpr3_can_adjust_cold_temp() - Is cold temperature adjustment available ++ * ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function checks the cold temperature threshold is available ++ * ++ * Return: true on cold temperature threshold is available, else false ++ */ ++bool cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg) ++{ ++ char prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (soc_version_major > 1) ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", ++ sizeof(prop_str)); ++ else ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", ++ sizeof(prop_str)); ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ return false; ++ } else ++ return true; ++} ++ ++/** ++ * cpr3_get_cold_temp_threshold() - get cold temperature threshold ++ * ++ * @vreg: Pointer to the CPR3 regulator ++ * @cold_temp: cold temperature read. ++ * ++ * This function reads the cold temperature threshold below which ++ * cold temperature adjustment margins will be applied. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp) ++{ ++ int rc; ++ u32 temp; ++ char req_prop_str[75], prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (vreg->part_type_supported) { ++ if (soc_version_major > 1) ++ snprintf(req_prop_str, sizeof(req_prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-v2-%d", ++ vreg->part_type); ++ else ++ snprintf(req_prop_str, sizeof(req_prop_str), ++ "qcom,cpr-cold-temp-voltage-adjustment-%d", ++ vreg->part_type); ++ } else { ++ strlcpy(req_prop_str, "qcom,cpr-cold-temp-voltage-adjustment", ++ sizeof(req_prop_str)); ++ } ++ ++ if (soc_version_major > 1) ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", ++ sizeof(prop_str)); ++ else ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", ++ sizeof(prop_str)); ++ ++ if (!of_find_property(vreg->of_node, req_prop_str, NULL)) { ++ /* No adjustment required. */ ++ cpr3_info(vreg, "Cold temperature adjustment not required.\n"); ++ return 0; ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ cpr3_err(vreg, "Missing %s required for %s\n", ++ prop_str, req_prop_str); ++ return -EINVAL; ++ } ++ ++ rc = of_property_read_u32(vreg->of_node, prop_str, &temp); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_str, rc); ++ return rc; ++ } ++ ++ *cold_temp = temp; ++ return 0; ++} ++ ++/** ++ * cpr3_adjust_fused_open_loop_voltages() - adjust the fused open-loop voltages ++ * for each fuse corner according to device tree values ++ * @vreg: Pointer to the CPR3 regulator ++ * @fuse_volt: Pointer to an array of the fused open-loop voltage ++ * values ++ * ++ * Voltage values in fuse_volt are modified in place. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, ++ int *fuse_volt) ++{ ++ int i, rc, prev_volt; ++ int *volt_adjust; ++ char prop_str[75]; ++ int soc_version_major = read_ipq_soc_version_major(); ++ ++ BUG_ON(soc_version_major <= 0); ++ ++ if (vreg->part_type_supported) { ++ if (soc_version_major > 1) ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-open-loop-voltage-fuse-adjustment-v2-%d", ++ vreg->part_type); ++ else ++ snprintf(prop_str, sizeof(prop_str), ++ "qcom,cpr-open-loop-voltage-fuse-adjustment-%d", ++ vreg->part_type); ++ } else { ++ strlcpy(prop_str, "qcom,cpr-open-loop-voltage-fuse-adjustment", ++ sizeof(prop_str)); ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* No adjustment required. */ ++ return 0; ++ } ++ ++ volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ if (!volt_adjust) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, ++ prop_str, vreg->fuse_corner_count, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load open-loop fused voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (volt_adjust[i]) { ++ prev_volt = fuse_volt[i]; ++ fuse_volt[i] += volt_adjust[i]; ++ cpr3_debug(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", ++ i, prev_volt, fuse_volt[i]); ++ } ++ } ++ ++done: ++ kfree(volt_adjust); ++ return rc; ++} ++ ++/** ++ * cpr3_adjust_open_loop_voltages() - adjust the open-loop voltages for each ++ * corner according to device tree values ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) ++{ ++ int i, rc, prev_volt, min_volt; ++ int *volt_adjust, *volt_diff; ++ ++ if (!of_find_property(vreg->of_node, ++ "qcom,cpr-open-loop-voltage-adjustment", NULL)) { ++ /* No adjustment required. */ ++ return 0; ++ } ++ ++ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ volt_diff = kcalloc(vreg->corner_count, sizeof(*volt_diff), GFP_KERNEL); ++ if (!volt_adjust || !volt_diff) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-open-loop-voltage-adjustment", 1, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load open-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (volt_adjust[i]) { ++ prev_volt = vreg->corner[i].open_loop_volt; ++ vreg->corner[i].open_loop_volt += volt_adjust[i]; ++ cpr3_debug(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", ++ i, prev_volt, vreg->corner[i].open_loop_volt); ++ } ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-open-loop-voltage-min-diff", NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-open-loop-voltage-min-diff", 1, volt_diff); ++ if (rc) { ++ cpr3_err(vreg, "could not load minimum open-loop voltage differences, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ /* ++ * Ensure that open-loop voltages increase monotonically with respect ++ * to configurable minimum allowed differences. ++ */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ min_volt = vreg->corner[i - 1].open_loop_volt + volt_diff[i]; ++ if (vreg->corner[i].open_loop_volt < min_volt) { ++ cpr3_debug(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", ++ i, vreg->corner[i].open_loop_volt, ++ i - 1, vreg->corner[i - 1].open_loop_volt, ++ volt_diff[i], i, min_volt); ++ vreg->corner[i].open_loop_volt = min_volt; ++ } ++ } ++ ++done: ++ kfree(volt_diff); ++ kfree(volt_adjust); ++ return rc; ++} ++ ++/** ++ * cpr3_quot_adjustment() - returns the quotient adjustment value resulting from ++ * the specified voltage adjustment and RO scaling factor ++ * @ro_scale: The CPR ring oscillator (RO) scaling factor with units ++ * of QUOT/V ++ * @volt_adjust: The amount to adjust the voltage by in units of ++ * microvolts. This value may be positive or negative. ++ */ ++int cpr3_quot_adjustment(int ro_scale, int volt_adjust) ++{ ++ unsigned long long temp; ++ int quot_adjust; ++ int sign = 1; ++ ++ if (ro_scale < 0) { ++ sign = -sign; ++ ro_scale = -ro_scale; ++ } ++ ++ if (volt_adjust < 0) { ++ sign = -sign; ++ volt_adjust = -volt_adjust; ++ } ++ ++ temp = (unsigned long long)ro_scale * (unsigned long long)volt_adjust; ++ do_div(temp, 1000000); ++ ++ quot_adjust = temp; ++ quot_adjust *= sign; ++ ++ return quot_adjust; ++} ++ ++/** ++ * cpr3_voltage_adjustment() - returns the voltage adjustment value resulting ++ * from the specified quotient adjustment and RO scaling factor ++ * @ro_scale: The CPR ring oscillator (RO) scaling factor with units ++ * of QUOT/V ++ * @quot_adjust: The amount to adjust the quotient by in units of ++ * QUOT. This value may be positive or negative. ++ */ ++int cpr3_voltage_adjustment(int ro_scale, int quot_adjust) ++{ ++ unsigned long long temp; ++ int volt_adjust; ++ int sign = 1; ++ ++ if (ro_scale < 0) { ++ sign = -sign; ++ ro_scale = -ro_scale; ++ } ++ ++ if (quot_adjust < 0) { ++ sign = -sign; ++ quot_adjust = -quot_adjust; ++ } ++ ++ if (ro_scale == 0) ++ return 0; ++ ++ temp = (unsigned long long)quot_adjust * 1000000; ++ do_div(temp, ro_scale); ++ ++ volt_adjust = temp; ++ volt_adjust *= sign; ++ ++ return volt_adjust; ++} ++ ++/** ++ * cpr3_parse_closed_loop_voltage_adjustments() - load per-fuse-corner and ++ * per-corner closed-loop adjustment values from device tree ++ * @vreg: Pointer to the CPR3 regulator ++ * @ro_sel: Array of ring oscillator values selected for each ++ * fuse corner ++ * @volt_adjust: Pointer to array which will be filled with the ++ * per-corner closed-loop adjustment voltages ++ * @volt_adjust_fuse: Pointer to array which will be filled with the ++ * per-fuse-corner closed-loop adjustment voltages ++ * @ro_scale: Pointer to array which will be filled with the ++ * per-fuse-corner RO scaling factor values with units of ++ * QUOT/V ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_parse_closed_loop_voltage_adjustments( ++ struct cpr3_regulator *vreg, u64 *ro_sel, ++ int *volt_adjust, int *volt_adjust_fuse, int *ro_scale) ++{ ++ int i, rc; ++ u32 *ro_all_scale; ++ ++ char volt_adj[] = "qcom,cpr-closed-loop-voltage-adjustment"; ++ char volt_fuse_adj[] = "qcom,cpr-closed-loop-voltage-fuse-adjustment"; ++ char ro_scaling[] = "qcom,cpr-ro-scaling-factor"; ++ ++ if (!of_find_property(vreg->of_node, volt_adj, NULL) ++ && !of_find_property(vreg->of_node, volt_fuse_adj, NULL) ++ && !vreg->aging_allowed) { ++ /* No adjustment required. */ ++ return 0; ++ } else if (!of_find_property(vreg->of_node, ro_scaling, NULL)) { ++ cpr3_err(vreg, "Missing %s required for closed-loop voltage adjustment.\n", ++ ro_scaling); ++ return -EINVAL; ++ } ++ ++ ro_all_scale = kcalloc(vreg->fuse_corner_count * CPR3_RO_COUNT, ++ sizeof(*ro_all_scale), GFP_KERNEL); ++ if (!ro_all_scale) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, ro_scaling, ++ vreg->fuse_corner_count * CPR3_RO_COUNT, ro_all_scale); ++ if (rc) { ++ cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) ++ ro_scale[i] = ro_all_scale[i * CPR3_RO_COUNT + ro_sel[i]]; ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ memcpy(vreg->corner[i].ro_scale, ++ &ro_all_scale[vreg->corner[i].cpr_fuse_corner * CPR3_RO_COUNT], ++ sizeof(*ro_all_scale) * CPR3_RO_COUNT); ++ ++ if (of_find_property(vreg->of_node, volt_fuse_adj, NULL)) { ++ rc = cpr3_parse_array_property(vreg, volt_fuse_adj, ++ vreg->fuse_corner_count, volt_adjust_fuse); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop fused voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ if (of_find_property(vreg->of_node, volt_adj, NULL)) { ++ rc = cpr3_parse_corner_array_property(vreg, volt_adj, ++ 1, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++done: ++ kfree(ro_all_scale); ++ return rc; ++} ++ ++/** ++ * cpr3_apm_init() - initialize APM data for a CPR3 controller ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * This function loads memory array power mux (APM) data from device tree ++ * if it is present and requests a handle to the appropriate APM controller ++ * device. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_apm_init(struct cpr3_controller *ctrl) ++{ ++ struct device_node *node = ctrl->dev->of_node; ++ int rc; ++ ++ if (!of_find_property(node, "qcom,apm-ctrl", NULL)) { ++ /* No APM used */ ++ return 0; ++ } ++ ++ ctrl->apm = msm_apm_ctrl_dev_get(ctrl->dev); ++ if (IS_ERR(ctrl->apm)) { ++ rc = PTR_ERR(ctrl->apm); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "APM get failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32(node, "qcom,apm-threshold-voltage", ++ &ctrl->apm_threshold_volt); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,apm-threshold-voltage, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ctrl->apm_threshold_volt ++ = CPR3_ROUND(ctrl->apm_threshold_volt, ctrl->step_volt); ++ ++ /* No error check since this is an optional property. */ ++ of_property_read_u32(node, "qcom,apm-hysteresis-voltage", ++ &ctrl->apm_adj_volt); ++ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); ++ ++ ctrl->apm_high_supply = MSM_APM_SUPPLY_APCC; ++ ctrl->apm_low_supply = MSM_APM_SUPPLY_MX; ++ ++ return 0; ++} ++ ++/** ++ * cpr3_mem_acc_init() - initialize mem-acc regulator data for ++ * a CPR3 regulator ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_mem_acc_init(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ u32 *temp; ++ int i, rc; ++ ++ if (!ctrl->mem_acc_regulator) { ++ cpr3_info(ctrl, "not using memory accelerator regulator\n"); ++ return 0; ++ } ++ ++ temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); ++ if (!temp) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, "qcom,mem-acc-voltage", ++ 1, temp); ++ if (rc) { ++ cpr3_err(ctrl, "could not load mem-acc corners, rc=%d\n", rc); ++ } else { ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].mem_acc_volt = temp[i]; ++ } ++ ++ kfree(temp); ++ return rc; ++} ++ ++/** ++ * cpr4_load_core_and_temp_adj() - parse amount of voltage adjustment for ++ * per-online-core and per-temperature voltage adjustment for a ++ * given corner or corner band from device tree. ++ * @vreg: Pointer to the CPR3 regulator ++ * @num: Corner number or corner band number ++ * @use_corner_band: Boolean indicating if the CPR3 regulator supports ++ * adjustments per corner band ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_load_core_and_temp_adj(struct cpr3_regulator *vreg, ++ int num, bool use_corner_band) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr4_sdelta *sdelta; ++ int sdelta_size, i, j, pos, rc = 0; ++ char str[75]; ++ size_t buflen; ++ char *buf; ++ ++ sdelta = use_corner_band ? vreg->corner_band[num].sdelta : ++ vreg->corner[num].sdelta; ++ ++ if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) { ++ /* corner doesn't need sdelta table */ ++ sdelta->max_core_count = 0; ++ sdelta->temp_band_count = 0; ++ return rc; ++ } ++ ++ sdelta_size = sdelta->max_core_count * sdelta->temp_band_count; ++ if (use_corner_band) ++ snprintf(str, sizeof(str), ++ "corner_band=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n", ++ num, sdelta->max_core_count, ++ sdelta->temp_band_count, sdelta_size); ++ else ++ snprintf(str, sizeof(str), ++ "corner=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n", ++ num, sdelta->max_core_count, ++ sdelta->temp_band_count, sdelta_size); ++ ++ cpr3_debug(vreg, "%s", str); ++ ++ sdelta->table = devm_kcalloc(ctrl->dev, sdelta_size, ++ sizeof(*sdelta->table), GFP_KERNEL); ++ if (!sdelta->table) ++ return -ENOMEM; ++ ++ if (use_corner_band) ++ snprintf(str, sizeof(str), ++ "qcom,cpr-corner-band%d-temp-core-voltage-adjustment", ++ num + CPR3_CORNER_OFFSET); ++ else ++ snprintf(str, sizeof(str), ++ "qcom,cpr-corner%d-temp-core-voltage-adjustment", ++ num + CPR3_CORNER_OFFSET); ++ ++ rc = cpr3_parse_array_property(vreg, str, sdelta_size, ++ sdelta->table); ++ if (rc) { ++ cpr3_err(vreg, "could not load %s, rc=%d\n", str, rc); ++ return rc; ++ } ++ ++ /* ++ * Convert sdelta margins from uV to PMIC steps and apply negation to ++ * follow the SDELTA register semantics. ++ */ ++ for (i = 0; i < sdelta_size; i++) ++ sdelta->table[i] = -(sdelta->table[i] / ctrl->step_volt); ++ ++ buflen = sizeof(*buf) * sdelta_size * (MAX_CHARS_PER_INT + 2); ++ buf = kzalloc(buflen, GFP_KERNEL); ++ if (!buf) ++ return rc; ++ ++ for (i = 0; i < sdelta->max_core_count; i++) { ++ for (j = 0, pos = 0; j < sdelta->temp_band_count; j++) ++ pos += scnprintf(buf + pos, buflen - pos, " %u", ++ sdelta->table[i * sdelta->temp_band_count + j]); ++ cpr3_debug(vreg, "sdelta[%d]:%s\n", i, buf); ++ } ++ ++ kfree(buf); ++ return rc; ++} ++ ++/** ++ * cpr4_parse_core_count_temp_voltage_adj() - parse configuration data for ++ * per-online-core and per-temperature voltage adjustment for ++ * a CPR3 regulator from device tree. ++ * @vreg: Pointer to the CPR3 regulator ++ * @use_corner_band: Boolean indicating if the CPR3 regulator supports ++ * adjustments per corner band ++ * ++ * This function supports parsing of per-online-core and per-temperature ++ * adjustments per corner or per corner band. CPR controllers which support ++ * corner bands apply the same adjustments to all corners within a corner band. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr4_parse_core_count_temp_voltage_adj( ++ struct cpr3_regulator *vreg, bool use_corner_band) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct device_node *node = vreg->of_node; ++ struct cpr3_corner *corner; ++ struct cpr4_sdelta *sdelta; ++ int i, sdelta_table_count, rc = 0; ++ int *allow_core_count_adj = NULL, *allow_temp_adj = NULL; ++ char prop_str[75]; ++ ++ if (of_find_property(node, use_corner_band ? ++ "qcom,corner-band-allow-temp-adjustment" ++ : "qcom,corner-allow-temp-adjustment", NULL)) { ++ if (!ctrl->allow_temp_adj) { ++ cpr3_err(ctrl, "Temperature adjustment configurations missing\n"); ++ return -EINVAL; ++ } ++ ++ vreg->allow_temp_adj = true; ++ } ++ ++ if (of_find_property(node, use_corner_band ? ++ "qcom,corner-band-allow-core-count-adjustment" ++ : "qcom,corner-allow-core-count-adjustment", ++ NULL)) { ++ rc = of_property_read_u32(node, "qcom,max-core-count", ++ &vreg->max_core_count); ++ if (rc) { ++ cpr3_err(vreg, "error reading qcom,max-core-count, rc=%d\n", ++ rc); ++ return -EINVAL; ++ } ++ ++ vreg->allow_core_count_adj = true; ++ ctrl->allow_core_count_adj = true; ++ } ++ ++ if (!vreg->allow_temp_adj && !vreg->allow_core_count_adj) { ++ /* ++ * Both per-online-core and temperature based adjustments are ++ * disabled for this regulator. ++ */ ++ return 0; ++ } else if (!vreg->allow_core_count_adj) { ++ /* ++ * Only per-temperature voltage adjusments are allowed. ++ * Keep max core count value as 1 to allocate SDELTA. ++ */ ++ vreg->max_core_count = 1; ++ } ++ ++ if (vreg->allow_core_count_adj) { ++ allow_core_count_adj = kcalloc(vreg->corner_count, ++ sizeof(*allow_core_count_adj), ++ GFP_KERNEL); ++ if (!allow_core_count_adj) ++ return -ENOMEM; ++ ++ snprintf(prop_str, sizeof(prop_str), "%s", use_corner_band ? ++ "qcom,corner-band-allow-core-count-adjustment" : ++ "qcom,corner-allow-core-count-adjustment"); ++ ++ rc = use_corner_band ? ++ cpr3_parse_corner_band_array_property(vreg, prop_str, ++ 1, allow_core_count_adj) : ++ cpr3_parse_corner_array_property(vreg, prop_str, ++ 1, allow_core_count_adj); ++ if (rc) { ++ cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, ++ rc); ++ goto done; ++ } ++ } ++ ++ if (vreg->allow_temp_adj) { ++ allow_temp_adj = kcalloc(vreg->corner_count, ++ sizeof(*allow_temp_adj), GFP_KERNEL); ++ if (!allow_temp_adj) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ snprintf(prop_str, sizeof(prop_str), "%s", use_corner_band ? ++ "qcom,corner-band-allow-temp-adjustment" : ++ "qcom,corner-allow-temp-adjustment"); ++ ++ rc = use_corner_band ? ++ cpr3_parse_corner_band_array_property(vreg, prop_str, ++ 1, allow_temp_adj) : ++ cpr3_parse_corner_array_property(vreg, prop_str, ++ 1, allow_temp_adj); ++ if (rc) { ++ cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, ++ rc); ++ goto done; ++ } ++ } ++ ++ sdelta_table_count = use_corner_band ? vreg->corner_band_count : ++ vreg->corner_count; ++ ++ for (i = 0; i < sdelta_table_count; i++) { ++ sdelta = devm_kzalloc(ctrl->dev, sizeof(*corner->sdelta), ++ GFP_KERNEL); ++ if (!sdelta) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ if (allow_core_count_adj) ++ sdelta->allow_core_count_adj = allow_core_count_adj[i]; ++ if (allow_temp_adj) ++ sdelta->allow_temp_adj = allow_temp_adj[i]; ++ sdelta->max_core_count = vreg->max_core_count; ++ sdelta->temp_band_count = ctrl->temp_band_count; ++ ++ if (use_corner_band) ++ vreg->corner_band[i].sdelta = sdelta; ++ else ++ vreg->corner[i].sdelta = sdelta; ++ ++ rc = cpr4_load_core_and_temp_adj(vreg, i, use_corner_band); ++ if (rc) { ++ cpr3_err(vreg, "corner/band %d core and temp adjustment loading failed, rc=%d\n", ++ i, rc); ++ goto done; ++ } ++ } ++ ++done: ++ kfree(allow_core_count_adj); ++ kfree(allow_temp_adj); ++ ++ return rc; ++} ++ ++/** ++ * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages ++ * so that they do not overlap the APM threshold voltage. ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * The memory array power mux (APM) must be configured for a specific supply ++ * based upon where the VDD voltage lies with respect to the APM threshold ++ * voltage. When using CPR hardware closed-loop, the voltage may vary anywhere ++ * between the floor and ceiling voltage without software notification. ++ * Therefore, it is required that the floor to ceiling range for every corner ++ * not intersect the APM threshold voltage. This function adjusts the floor to ++ * ceiling range for each corner which violates this requirement. ++ * ++ * The following algorithm is applied: ++ * if floor < threshold <= ceiling: ++ * if open_loop >= threshold, then floor = threshold - adj ++ * else ceiling = threshold - step ++ * where: ++ * adj = APM hysteresis voltage established to minimize the number of ++ * corners with artificially increased floor voltages ++ * step = voltage in microvolts of a single step of the VDD supply ++ * ++ * The open-loop voltage is also bounded by the new floor or ceiling value as ++ * needed. ++ * ++ * Return: none ++ */ ++void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr3_corner *corner; ++ int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop; ++ ++ if (!ctrl->apm_threshold_volt) { ++ /* APM not being used. */ ++ return; ++ } ++ ++ ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt, ++ ctrl->step_volt); ++ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); ++ ++ threshold = ctrl->apm_threshold_volt; ++ adj = ctrl->apm_adj_volt; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ ++ if (threshold <= corner->floor_volt ++ || threshold > corner->ceiling_volt) ++ continue; ++ ++ prev_floor = corner->floor_volt; ++ prev_ceiling = corner->ceiling_volt; ++ prev_open_loop = corner->open_loop_volt; ++ ++ if (corner->open_loop_volt >= threshold) { ++ corner->floor_volt = max(corner->floor_volt, ++ threshold - adj); ++ if (corner->open_loop_volt < corner->floor_volt) ++ corner->open_loop_volt = corner->floor_volt; ++ } else { ++ corner->ceiling_volt = threshold - ctrl->step_volt; ++ } ++ ++ if (corner->floor_volt != prev_floor ++ || corner->ceiling_volt != prev_ceiling ++ || corner->open_loop_volt != prev_open_loop) ++ cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", ++ threshold, adj, i, prev_floor, prev_ceiling, ++ prev_open_loop, corner->floor_volt, ++ corner->ceiling_volt, corner->open_loop_volt); ++ } ++} ++ ++/** ++ * cprh_adjust_voltages_for_mem_acc() - adjust per-corner floor and ceiling ++ * voltages so that they do not intersect the MEM ACC threshold ++ * voltage ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * The following algorithm is applied: ++ * if floor < threshold <= ceiling: ++ * if open_loop >= threshold, then floor = threshold ++ * else ceiling = threshold - step ++ * where: ++ * step = voltage in microvolts of a single step of the VDD supply ++ * ++ * The open-loop voltage is also bounded by the new floor or ceiling value as ++ * needed. ++ * ++ * Return: none ++ */ ++void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr3_corner *corner; ++ int i, threshold, prev_ceiling, prev_floor, prev_open_loop; ++ ++ if (!ctrl->mem_acc_threshold_volt) { ++ /* MEM ACC not being used. */ ++ return; ++ } ++ ++ ctrl->mem_acc_threshold_volt = CPR3_ROUND(ctrl->mem_acc_threshold_volt, ++ ctrl->step_volt); ++ ++ threshold = ctrl->mem_acc_threshold_volt; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ ++ if (threshold <= corner->floor_volt ++ || threshold > corner->ceiling_volt) ++ continue; ++ ++ prev_floor = corner->floor_volt; ++ prev_ceiling = corner->ceiling_volt; ++ prev_open_loop = corner->open_loop_volt; ++ ++ if (corner->open_loop_volt >= threshold) { ++ corner->floor_volt = max(corner->floor_volt, threshold); ++ if (corner->open_loop_volt < corner->floor_volt) ++ corner->open_loop_volt = corner->floor_volt; ++ } else { ++ corner->ceiling_volt = threshold - ctrl->step_volt; ++ } ++ ++ if (corner->floor_volt != prev_floor ++ || corner->ceiling_volt != prev_ceiling ++ || corner->open_loop_volt != prev_open_loop) ++ cpr3_debug(vreg, "MEM ACC threshold=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", ++ threshold, i, prev_floor, prev_ceiling, ++ prev_open_loop, corner->floor_volt, ++ corner->ceiling_volt, corner->open_loop_volt); ++ } ++} ++ ++/** ++ * cpr3_apply_closed_loop_offset_voltages() - modify the closed-loop voltage ++ * adjustments by the amounts that are needed for this ++ * fuse combo ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Array of closed-loop voltage adjustment values of length ++ * vreg->corner_count which is further adjusted based upon ++ * offset voltage fuse values. ++ * @fuse_volt_adjust: Fused closed-loop voltage adjustment values of length ++ * vreg->fuse_corner_count. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr3_apply_closed_loop_offset_voltages(struct cpr3_regulator *vreg, ++ int *volt_adjust, int *fuse_volt_adjust) ++{ ++ u32 *corner_map; ++ int rc = 0, i; ++ ++ if (!of_find_property(vreg->of_node, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) { ++ /* No closed-loop offset required. */ ++ return 0; ++ } ++ ++ corner_map = kcalloc(vreg->corner_count, sizeof(*corner_map), ++ GFP_KERNEL); ++ if (!corner_map) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", ++ 1, corner_map); ++ if (rc) ++ goto done; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ if (corner_map[i] == 0) { ++ continue; ++ } else if (corner_map[i] > vreg->fuse_corner_count) { ++ cpr3_err(vreg, "corner %d mapped to invalid fuse corner: %u\n", ++ i, corner_map[i]); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ volt_adjust[i] += fuse_volt_adjust[corner_map[i] - 1]; ++ } ++ ++done: ++ kfree(corner_map); ++ return rc; ++} ++ ++/** ++ * cpr3_enforce_inc_quotient_monotonicity() - Ensure that target quotients ++ * increase monotonically from lower to higher corners ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static void cpr3_enforce_inc_quotient_monotonicity(struct cpr3_regulator *vreg) ++{ ++ int i, j; ++ ++ for (i = 1; i < vreg->corner_count; i++) { ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j] ++ && vreg->corner[i].target_quot[j] ++ < vreg->corner[i - 1].target_quot[j]) { ++ cpr3_debug(vreg, "corner %d RO%u target quot=%u < corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", ++ i, j, ++ vreg->corner[i].target_quot[j], ++ i - 1, j, ++ vreg->corner[i - 1].target_quot[j], ++ i, j, ++ vreg->corner[i - 1].target_quot[j]); ++ vreg->corner[i].target_quot[j] ++ = vreg->corner[i - 1].target_quot[j]; ++ } ++ } ++ } ++} ++ ++/** ++ * cpr3_enforce_dec_quotient_monotonicity() - Ensure that target quotients ++ * decrease monotonically from higher to lower corners ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static void cpr3_enforce_dec_quotient_monotonicity(struct cpr3_regulator *vreg) ++{ ++ int i, j; ++ ++ for (i = vreg->corner_count - 2; i >= 0; i--) { ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i + 1].target_quot[j] ++ && vreg->corner[i].target_quot[j] ++ > vreg->corner[i + 1].target_quot[j]) { ++ cpr3_debug(vreg, "corner %d RO%u target quot=%u > corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", ++ i, j, ++ vreg->corner[i].target_quot[j], ++ i + 1, j, ++ vreg->corner[i + 1].target_quot[j], ++ i, j, ++ vreg->corner[i + 1].target_quot[j]); ++ vreg->corner[i].target_quot[j] ++ = vreg->corner[i + 1].target_quot[j]; ++ } ++ } ++ } ++} ++ ++/** ++ * _cpr3_adjust_target_quotients() - adjust the target quotients for each ++ * corner of the regulator according to input adjustment and ++ * scaling arrays ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Pointer to an array of closed-loop voltage adjustments ++ * with units of microvolts. The array must have ++ * vreg->corner_count number of elements. ++ * @ro_scale: Pointer to a flattened 2D array of RO scaling factors. ++ * The array must have an inner dimension of CPR3_RO_COUNT ++ * and an outer dimension of vreg->corner_count ++ * @label: Null terminated string providing a label for the type ++ * of adjustment. ++ * ++ * Return: true if any corners received a positive voltage adjustment (> 0), ++ * else false ++ */ ++static bool _cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ const int *volt_adjust, const int *ro_scale, const char *label) ++{ ++ int i, j, quot_adjust; ++ bool is_increasing = false; ++ u32 prev_quot; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ for (j = 0; j < CPR3_RO_COUNT; j++) { ++ if (vreg->corner[i].target_quot[j]) { ++ quot_adjust = cpr3_quot_adjustment( ++ ro_scale[i * CPR3_RO_COUNT + j], ++ volt_adjust[i]); ++ if (quot_adjust) { ++ prev_quot = vreg->corner[i]. ++ target_quot[j]; ++ vreg->corner[i].target_quot[j] ++ += quot_adjust; ++ cpr3_debug(vreg, "adjusted corner %d RO%d target quot %s: %u --> %u (%d uV)\n", ++ i, j, label, prev_quot, ++ vreg->corner[i].target_quot[j], ++ volt_adjust[i]); ++ } ++ } ++ } ++ if (volt_adjust[i] > 0) ++ is_increasing = true; ++ } ++ ++ return is_increasing; ++} ++ ++/** ++ * cpr3_adjust_target_quotients() - adjust the target quotients for each ++ * corner according to device tree values and fuse values ++ * @vreg: Pointer to the CPR3 regulator ++ * @fuse_volt_adjust: Fused closed-loop voltage adjustment values of length ++ * vreg->fuse_corner_count. This parameter could be null ++ * pointer when no fused adjustments are needed. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, ++ int *fuse_volt_adjust) ++{ ++ int i, rc; ++ int *volt_adjust, *ro_scale; ++ bool explicit_adjustment, fused_adjustment, is_increasing; ++ ++ explicit_adjustment = of_find_property(vreg->of_node, ++ "qcom,cpr-closed-loop-voltage-adjustment", NULL); ++ fused_adjustment = of_find_property(vreg->of_node, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL); ++ ++ if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { ++ /* No adjustment required. */ ++ return 0; ++ } else if (!of_find_property(vreg->of_node, ++ "qcom,cpr-ro-scaling-factor", NULL)) { ++ cpr3_err(vreg, "qcom,cpr-ro-scaling-factor is required for closed-loop voltage adjustment, but is missing\n"); ++ return -EINVAL; ++ } ++ ++ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ ro_scale = kcalloc(vreg->corner_count * CPR3_RO_COUNT, ++ sizeof(*ro_scale), GFP_KERNEL); ++ if (!volt_adjust || !ro_scale) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-ro-scaling-factor", CPR3_RO_COUNT, ro_scale); ++ if (rc) { ++ cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ memcpy(vreg->corner[i].ro_scale, &ro_scale[i * CPR3_RO_COUNT], ++ sizeof(*ro_scale) * CPR3_RO_COUNT); ++ ++ if (explicit_adjustment) { ++ rc = cpr3_parse_corner_array_property(vreg, ++ "qcom,cpr-closed-loop-voltage-adjustment", ++ 1, volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ _cpr3_adjust_target_quotients(vreg, volt_adjust, ro_scale, ++ "from DT"); ++ cpr3_enforce_inc_quotient_monotonicity(vreg); ++ } ++ ++ if (fused_adjustment && fuse_volt_adjust) { ++ memset(volt_adjust, 0, ++ sizeof(*volt_adjust) * vreg->corner_count); ++ ++ rc = cpr3_apply_closed_loop_offset_voltages(vreg, volt_adjust, ++ fuse_volt_adjust); ++ if (rc) { ++ cpr3_err(vreg, "could not apply fused closed-loop voltage reductions, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ is_increasing = _cpr3_adjust_target_quotients(vreg, volt_adjust, ++ ro_scale, "from fuse"); ++ if (is_increasing) ++ cpr3_enforce_inc_quotient_monotonicity(vreg); ++ else ++ cpr3_enforce_dec_quotient_monotonicity(vreg); ++ } ++ ++done: ++ kfree(volt_adjust); ++ kfree(ro_scale); ++ return rc; ++} +diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c +new file mode 100644 +index 0000000000000..114e2acb25ff3 +--- /dev/null ++++ b/drivers/regulator/cpr4-apss-regulator.c +@@ -0,0 +1,1819 @@ ++/* ++ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "%s: " fmt, __func__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpr3-regulator.h" ++ ++#define IPQ807x_APSS_FUSE_CORNERS 4 ++#define IPQ817x_APPS_FUSE_CORNERS 2 ++#define IPQ6018_APSS_FUSE_CORNERS 4 ++#define IPQ9574_APSS_FUSE_CORNERS 4 ++ ++u32 g_valid_fuse_count = IPQ807x_APSS_FUSE_CORNERS; ++ ++/** ++ * struct cpr4_ipq807x_apss_fuses - APSS specific fuse data for IPQ807x ++ * @ro_sel: Ring oscillator select fuse parameter value for each ++ * fuse corner ++ * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value ++ * for each fuse corner (raw, not converted to a voltage) ++ * @target_quot: CPR target quotient fuse parameter value for each fuse ++ * corner ++ * @quot_offset: CPR target quotient offset fuse parameter value for each ++ * fuse corner (raw, not unpacked) used for target quotient ++ * interpolation ++ * @speed_bin: Application processor speed bin fuse parameter value for ++ * the given chip ++ * @cpr_fusing_rev: CPR fusing revision fuse parameter value ++ * @boost_cfg: CPR boost configuration fuse parameter value ++ * @boost_voltage: CPR boost voltage fuse parameter value (raw, not ++ * converted to a voltage) ++ * ++ * This struct holds the values for all of the fuses read from memory. ++ */ ++struct cpr4_ipq807x_apss_fuses { ++ u64 ro_sel[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 init_voltage[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 target_quot[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 quot_offset[IPQ807x_APSS_FUSE_CORNERS]; ++ u64 speed_bin; ++ u64 cpr_fusing_rev; ++ u64 boost_cfg; ++ u64 boost_voltage; ++ u64 misc; ++}; ++ ++/* ++ * fuse combo = fusing revision + 8 * (speed bin) ++ * where: fusing revision = 0 - 7 and speed bin = 0 - 7 ++ */ ++#define CPR4_IPQ807x_APSS_FUSE_COMBO_COUNT 64 ++ ++/* ++ * Constants which define the name of each fuse corner. ++ */ ++enum cpr4_ipq807x_apss_fuse_corner { ++ CPR4_IPQ807x_APSS_FUSE_CORNER_SVS = 0, ++ CPR4_IPQ807x_APSS_FUSE_CORNER_NOM = 1, ++ CPR4_IPQ807x_APSS_FUSE_CORNER_TURBO = 2, ++ CPR4_IPQ807x_APSS_FUSE_CORNER_STURBO = 3, ++}; ++ ++static const char * const cpr4_ipq807x_apss_fuse_corner_name[] = { ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_SVS] = "SVS", ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_NOM] = "NOM", ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_TURBO] = "TURBO", ++ [CPR4_IPQ807x_APSS_FUSE_CORNER_STURBO] = "STURBO", ++}; ++ ++/* ++ * IPQ807x APSS fuse parameter locations: ++ * ++ * Structs are organized with the following dimensions: ++ * Outer: 0 to 3 for fuse corners from lowest to highest corner ++ * Inner: large enough to hold the longest set of parameter segments which ++ * fully defines a fuse parameter, +1 (for NULL termination). ++ * Each segment corresponds to a contiguous group of bits from a ++ * single fuse row. These segments are concatentated together in ++ * order to form the full fuse parameter value. The segments for ++ * a given parameter may correspond to different fuse rows. ++ */ ++static struct cpr3_fuse_param ++ipq807x_apss_ro_sel_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{73, 8, 11}, {} }, ++ {{73, 4, 7}, {} }, ++ {{73, 0, 3}, {} }, ++ {{73, 12, 15}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq807x_apss_init_voltage_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{71, 18, 23}, {} }, ++ {{71, 12, 17}, {} }, ++ {{71, 6, 11}, {} }, ++ {{71, 0, 5}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq807x_apss_target_quot_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{72, 32, 43}, {} }, ++ {{72, 20, 31}, {} }, ++ {{72, 8, 19}, {} }, ++ {{72, 44, 55}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq807x_apss_quot_offset_param[IPQ807x_APSS_FUSE_CORNERS][2] = { ++ {{} }, ++ {{71, 46, 52}, {} }, ++ {{71, 39, 45}, {} }, ++ {{71, 32, 38}, {} }, ++}; ++ ++static struct cpr3_fuse_param ipq807x_cpr_fusing_rev_param[] = { ++ {71, 53, 55}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_apss_speed_bin_param[] = { ++ {36, 40, 42}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_cpr_boost_fuse_cfg_param[] = { ++ {36, 43, 45}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_apss_boost_fuse_volt_param[] = { ++ {71, 0, 5}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq807x_misc_fuse_volt_adj_param[] = { ++ {36, 54, 54}, ++ {}, ++}; ++ ++static struct cpr3_fuse_parameters ipq807x_fuse_params = { ++ .apss_ro_sel_param = ipq807x_apss_ro_sel_param, ++ .apss_init_voltage_param = ipq807x_apss_init_voltage_param, ++ .apss_target_quot_param = ipq807x_apss_target_quot_param, ++ .apss_quot_offset_param = ipq807x_apss_quot_offset_param, ++ .cpr_fusing_rev_param = ipq807x_cpr_fusing_rev_param, ++ .apss_speed_bin_param = ipq807x_apss_speed_bin_param, ++ .cpr_boost_fuse_cfg_param = ipq807x_cpr_boost_fuse_cfg_param, ++ .apss_boost_fuse_volt_param = ipq807x_apss_boost_fuse_volt_param, ++ .misc_fuse_volt_adj_param = ipq807x_misc_fuse_volt_adj_param ++}; ++ ++/* ++ * The number of possible values for misc fuse is ++ * 2^(#bits defined for misc fuse) ++ */ ++#define IPQ807x_MISC_FUSE_VAL_COUNT BIT(1) ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ807x ++ */ ++static int ipq807x_apss_fuse_ref_volt ++ [IPQ807x_APSS_FUSE_CORNERS] = { ++ 720000, ++ 864000, ++ 992000, ++ 1064000, ++}; ++ ++#define IPQ807x_APSS_FUSE_STEP_VOLT 8000 ++#define IPQ807x_APSS_VOLTAGE_FUSE_SIZE 6 ++#define IPQ807x_APSS_QUOT_OFFSET_SCALE 5 ++ ++#define IPQ807x_APSS_CPR_SENSOR_COUNT 6 ++ ++#define IPQ807x_APSS_CPR_CLOCK_RATE 19200000 ++ ++#define IPQ807x_APSS_MAX_TEMP_POINTS 3 ++#define IPQ807x_APSS_TEMP_SENSOR_ID_START 4 ++#define IPQ807x_APSS_TEMP_SENSOR_ID_END 13 ++/* ++ * Boost voltage fuse reference and ceiling voltages in microvolts for ++ * IPQ807x. ++ */ ++#define IPQ807x_APSS_BOOST_FUSE_REF_VOLT 1140000 ++#define IPQ807x_APSS_BOOST_CEILING_VOLT 1140000 ++#define IPQ807x_APSS_BOOST_FLOOR_VOLT 900000 ++#define MAX_BOOST_CONFIG_FUSE_VALUE 8 ++ ++#define IPQ807x_APSS_CPR_SDELTA_CORE_COUNT 15 ++ ++#define IPQ807x_APSS_CPR_TCSR_START 8 ++#define IPQ807x_APSS_CPR_TCSR_END 9 ++ ++/* ++ * Array of integer values mapped to each of the boost config fuse values to ++ * indicate boost enable/disable status. ++ */ ++static bool boost_fuse[MAX_BOOST_CONFIG_FUSE_VALUE] = {0, 1, 1, 1, 1, 1, 1, 1}; ++ ++/* ++ * IPQ6018 (Few parameters are changed, remaining are same as IPQ807x) ++ */ ++#define IPQ6018_APSS_FUSE_STEP_VOLT 12500 ++#define IPQ6018_APSS_CPR_CLOCK_RATE 24000000 ++ ++static struct cpr3_fuse_param ++ipq6018_apss_ro_sel_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{75, 8, 11}, {} }, ++ {{75, 4, 7}, {} }, ++ {{75, 0, 3}, {} }, ++ {{75, 12, 15}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq6018_apss_init_voltage_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{73, 18, 23}, {} }, ++ {{73, 12, 17}, {} }, ++ {{73, 6, 11}, {} }, ++ {{73, 0, 5}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq6018_apss_target_quot_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{74, 32, 43}, {} }, ++ {{74, 20, 31}, {} }, ++ {{74, 8, 19}, {} }, ++ {{74, 44, 55}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq6018_apss_quot_offset_param[IPQ6018_APSS_FUSE_CORNERS][2] = { ++ {{} }, ++ {{73, 48, 55}, {} }, ++ {{73, 40, 47}, {} }, ++ {{73, 32, 39}, {} }, ++}; ++ ++static struct cpr3_fuse_param ipq6018_cpr_fusing_rev_param[] = { ++ {75, 16, 18}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_apss_speed_bin_param[] = { ++ {36, 40, 42}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_cpr_boost_fuse_cfg_param[] = { ++ {36, 43, 45}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_apss_boost_fuse_volt_param[] = { ++ {73, 0, 5}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq6018_misc_fuse_volt_adj_param[] = { ++ {36, 54, 54}, ++ {}, ++}; ++ ++static struct cpr3_fuse_parameters ipq6018_fuse_params = { ++ .apss_ro_sel_param = ipq6018_apss_ro_sel_param, ++ .apss_init_voltage_param = ipq6018_apss_init_voltage_param, ++ .apss_target_quot_param = ipq6018_apss_target_quot_param, ++ .apss_quot_offset_param = ipq6018_apss_quot_offset_param, ++ .cpr_fusing_rev_param = ipq6018_cpr_fusing_rev_param, ++ .apss_speed_bin_param = ipq6018_apss_speed_bin_param, ++ .cpr_boost_fuse_cfg_param = ipq6018_cpr_boost_fuse_cfg_param, ++ .apss_boost_fuse_volt_param = ipq6018_apss_boost_fuse_volt_param, ++ .misc_fuse_volt_adj_param = ipq6018_misc_fuse_volt_adj_param ++}; ++ ++ ++/* ++ * Boost voltage fuse reference and ceiling voltages in microvolts for ++ * IPQ6018. ++ */ ++#define IPQ6018_APSS_BOOST_FUSE_REF_VOLT 1140000 ++#define IPQ6018_APSS_BOOST_CEILING_VOLT 1140000 ++#define IPQ6018_APSS_BOOST_FLOOR_VOLT 900000 ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ807x ++ */ ++static int ipq6018_apss_fuse_ref_volt ++ [IPQ6018_APSS_FUSE_CORNERS] = { ++ 725000, ++ 862500, ++ 987500, ++ 1062500, ++}; ++ ++/* ++ * IPQ6018 Memory ACC settings on TCSR ++ * ++ * Turbo_L1: write TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x10 ++ * write TCSR_CUSTOM_VDDAPC0_ACC_1 0x1 ++ * Other modes: write TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x0 ++ * write TCSR_CUSTOM_VDDAPC0_ACC_1 0x0 ++ * ++ */ ++#define IPQ6018_APSS_MEM_ACC_TCSR_COUNT 2 ++#define TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x1946178 ++#define TCSR_CUSTOM_VDDAPC0_ACC_1 0x1946124 ++ ++struct mem_acc_tcsr { ++ u32 phy_addr; ++ void __iomem *ioremap_addr; ++ u32 value; ++}; ++ ++static struct mem_acc_tcsr ipq6018_mem_acc_tcsr[IPQ6018_APSS_MEM_ACC_TCSR_COUNT] = { ++ {TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0, NULL, 0x10}, ++ {TCSR_CUSTOM_VDDAPC0_ACC_1, NULL, 0x1}, ++}; ++ ++/* ++ * IPQ9574 (Few parameters are changed, remaining are same as IPQ6018) ++ */ ++#define IPQ9574_APSS_FUSE_STEP_VOLT 10000 ++ ++static struct cpr3_fuse_param ++ipq9574_apss_ro_sel_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{107, 4, 7}, {} }, ++ {{107, 0, 3}, {} }, ++ {{106, 4, 7}, {} }, ++ {{106, 0, 3}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq9574_apss_init_voltage_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{104, 24, 29}, {} }, ++ {{104, 18, 23}, {} }, ++ {{104, 12, 17}, {} }, ++ {{104, 6, 11}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq9574_apss_target_quot_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{106, 32, 43}, {} }, ++ {{106, 20, 31}, {} }, ++ {{106, 8, 19}, {} }, ++ {{106, 44, 55}, {} }, ++}; ++ ++static struct cpr3_fuse_param ++ipq9574_apss_quot_offset_param[IPQ9574_APSS_FUSE_CORNERS][2] = { ++ {{} }, ++ {{105, 48, 55}, {} }, ++ {{105, 40, 47}, {} }, ++ {{105, 32, 39}, {} }, ++}; ++ ++static struct cpr3_fuse_param ipq9574_cpr_fusing_rev_param[] = { ++ {107, 8, 10}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_apss_speed_bin_param[] = { ++ {0, 40, 42}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_cpr_boost_fuse_cfg_param[] = { ++ {0, 43, 45}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_apss_boost_fuse_volt_param[] = { ++ {104, 0, 5}, ++ {}, ++}; ++ ++static struct cpr3_fuse_param ipq9574_misc_fuse_volt_adj_param[] = { ++ {0, 54, 54}, ++ {}, ++}; ++ ++static struct cpr3_fuse_parameters ipq9574_fuse_params = { ++ .apss_ro_sel_param = ipq9574_apss_ro_sel_param, ++ .apss_init_voltage_param = ipq9574_apss_init_voltage_param, ++ .apss_target_quot_param = ipq9574_apss_target_quot_param, ++ .apss_quot_offset_param = ipq9574_apss_quot_offset_param, ++ .cpr_fusing_rev_param = ipq9574_cpr_fusing_rev_param, ++ .apss_speed_bin_param = ipq9574_apss_speed_bin_param, ++ .cpr_boost_fuse_cfg_param = ipq9574_cpr_boost_fuse_cfg_param, ++ .apss_boost_fuse_volt_param = ipq9574_apss_boost_fuse_volt_param, ++ .misc_fuse_volt_adj_param = ipq9574_misc_fuse_volt_adj_param ++}; ++ ++/* ++ * Open loop voltage fuse reference voltages in microvolts for IPQ9574 ++ */ ++static int ipq9574_apss_fuse_ref_volt ++ [IPQ9574_APSS_FUSE_CORNERS] = { ++ 725000, ++ 862500, ++ 987500, ++ 1062500, ++}; ++ ++/** ++ * cpr4_ipq807x_apss_read_fuse_data() - load APSS specific fuse parameter values ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * This function allocates a cpr4_ipq807x_apss_fuses struct, fills it with ++ * values read out of hardware fuses, and finally copies common fuse values ++ * into the CPR3 regulator struct. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_read_fuse_data(struct cpr3_regulator *vreg) ++{ ++ void __iomem *base = vreg->thread->ctrl->fuse_base; ++ struct cpr4_ipq807x_apss_fuses *fuse; ++ int i, rc; ++ ++ fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL); ++ if (!fuse) ++ return -ENOMEM; ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->apss_speed_bin_param, ++ &fuse->speed_bin); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n", rc); ++ return rc; ++ } ++ cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin); ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->cpr_fusing_rev_param, ++ &fuse->cpr_fusing_rev); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n", ++ rc); ++ return rc; ++ } ++ cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev); ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->misc_fuse_volt_adj_param, ++ &fuse->misc); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read misc voltage adjustment fuse, rc=%d\n", ++ rc); ++ return rc; ++ } ++ cpr3_info(vreg, "CPR misc fuse value = %llu\n", fuse->misc); ++ if (fuse->misc >= IPQ807x_MISC_FUSE_VAL_COUNT) { ++ cpr3_err(vreg, "CPR misc fuse value = %llu, should be < %lu\n", ++ fuse->misc, IPQ807x_MISC_FUSE_VAL_COUNT); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < g_valid_fuse_count; i++) { ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_init_voltage_param[i], ++ &fuse->init_voltage[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_target_quot_param[i], ++ &fuse->target_quot[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_ro_sel_param[i], ++ &fuse->ro_sel[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_quot_offset_param[i], ++ &fuse->quot_offset[i]); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n", ++ i, rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->cpr_boost_fuse_cfg_param, ++ &fuse->boost_cfg); ++ if (rc) { ++ cpr3_err(vreg, "Unable to read CPR boost config fuse, rc=%d\n", ++ rc); ++ return rc; ++ } ++ cpr3_info(vreg, "Voltage boost fuse config = %llu boost = %s\n", ++ fuse->boost_cfg, boost_fuse[fuse->boost_cfg] ++ ? "enable" : "disable"); ++ ++ rc = cpr3_read_fuse_param(base, ++ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_boost_fuse_volt_param, ++ &fuse->boost_voltage); ++ if (rc) { ++ cpr3_err(vreg, "failed to read boost fuse voltage, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; ++ if (vreg->fuse_combo >= CPR4_IPQ807x_APSS_FUSE_COMBO_COUNT) { ++ cpr3_err(vreg, "invalid CPR fuse combo = %d found\n", ++ vreg->fuse_combo); ++ return -EINVAL; ++ } ++ ++ vreg->speed_bin_fuse = fuse->speed_bin; ++ vreg->cpr_rev_fuse = fuse->cpr_fusing_rev; ++ vreg->fuse_corner_count = g_valid_fuse_count; ++ vreg->platform_fuses = fuse; ++ ++ return 0; ++} ++ ++/** ++ * cpr4_apss_parse_corner_data() - parse APSS corner data from device tree ++ * properties of the CPR3 regulator's device node ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_corner_data(struct cpr3_regulator *vreg) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ u32 *temp = NULL; ++ int i, rc; ++ ++ rc = cpr3_parse_common_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "error reading corner data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ /* If fuse has incorrect RO Select values and dtsi has "qcom,cpr-ro-sel" ++ * entry with RO select values other than zero, then dtsi values will ++ * be used. ++ */ ++ if (of_find_property(node, "qcom,cpr-ro-sel", NULL)) { ++ temp = kcalloc(vreg->fuse_corner_count, sizeof(*temp), ++ GFP_KERNEL); ++ if (!temp) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, "qcom,cpr-ro-sel", ++ vreg->fuse_corner_count, temp); ++ if (rc) ++ goto done; ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (temp[i] != 0) ++ fuse->ro_sel[i] = temp[i]; ++ } ++ } ++done: ++ kfree(temp); ++ return rc; ++} ++ ++/** ++ * cpr4_apss_parse_misc_fuse_voltage_adjustments() - fill an array from a ++ * portion of the voltage adjustments specified based on ++ * miscellaneous fuse bits. ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Voltage adjustment output data array which must be ++ * of size vreg->corner_count ++ * ++ * cpr3_parse_common_corner_data() must be called for vreg before this function ++ * is called so that speed bin size elements are initialized. ++ * ++ * Two formats are supported for the device tree property: ++ * 1. Length == tuple_list_size * vreg->corner_count ++ * (reading begins at index 0) ++ * 2. Length == tuple_list_size * vreg->speed_bin_corner_sum ++ * (reading begins at index tuple_list_size * vreg->speed_bin_offset) ++ * ++ * Here, tuple_list_size is the number of possible values for misc fuse. ++ * All other property lengths are treated as errors. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_misc_fuse_voltage_adjustments( ++ struct cpr3_regulator *vreg, u32 *volt_adjust) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ int tuple_list_size = IPQ807x_MISC_FUSE_VAL_COUNT; ++ int i, offset, rc, len = 0; ++ const char *prop_name = "qcom,cpr-misc-fuse-voltage-adjustment"; ++ ++ if (!of_find_property(node, prop_name, &len)) { ++ cpr3_err(vreg, "property %s is missing\n", prop_name); ++ return -EINVAL; ++ } ++ ++ if (len == tuple_list_size * vreg->corner_count * sizeof(u32)) { ++ offset = 0; ++ } else if (vreg->speed_bin_corner_sum > 0 && ++ len == tuple_list_size * vreg->speed_bin_corner_sum ++ * sizeof(u32)) { ++ offset = tuple_list_size * vreg->speed_bin_offset ++ + fuse->misc * vreg->corner_count; ++ } else { ++ if (vreg->speed_bin_corner_sum > 0) ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", ++ prop_name, len, ++ tuple_list_size * vreg->corner_count ++ * sizeof(u32), ++ tuple_list_size * vreg->speed_bin_corner_sum ++ * sizeof(u32)); ++ else ++ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu\n", ++ prop_name, len, ++ tuple_list_size * vreg->corner_count ++ * sizeof(u32)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ rc = of_property_read_u32_index(node, prop_name, offset + i, ++ &volt_adjust[i]); ++ if (rc) { ++ cpr3_err(vreg, "error reading property %s, rc=%d\n", ++ prop_name, rc); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr4_ipq807x_apss_calculate_open_loop_voltages() - calculate the open-loop ++ * voltage for each corner of a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * If open-loop voltage interpolation is allowed in device tree, then ++ * this function calculates the open-loop voltage for a given corner using ++ * linear interpolation. This interpolation is performed using the processor ++ * frequencies of the lower and higher Fmax corners along with their fused ++ * open-loop voltages. ++ * ++ * If open-loop voltage interpolation is not allowed, then this function uses ++ * the Fmax fused open-loop voltage for all of the corners associated with a ++ * given fuse corner. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_calculate_open_loop_voltages( ++ struct cpr3_regulator *vreg) ++{ ++ struct device_node *node = vreg->of_node; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ int i, j, rc = 0; ++ bool allow_interpolation; ++ u64 freq_low, volt_low, freq_high, volt_high; ++ int *fuse_volt, *misc_adj_volt; ++ int *fmax_corner; ++ ++ fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt), ++ GFP_KERNEL); ++ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), ++ GFP_KERNEL); ++ if (!fuse_volt || !fmax_corner) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->fuse_corner_count; i++) { ++ if (ctrl->cpr_global_setting == CPR_DISABLED) ++ fuse_volt[i] = vreg->cpr4_regulator_data->fuse_ref_volt[i]; ++ else ++ fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse( ++ vreg->cpr4_regulator_data->fuse_ref_volt[i], ++ vreg->cpr4_regulator_data->fuse_step_volt, ++ fuse->init_voltage[i], ++ IPQ807x_APSS_VOLTAGE_FUSE_SIZE); ++ ++ /* Log fused open-loop voltage values for debugging purposes. */ ++ cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", ++ cpr4_ipq807x_apss_fuse_corner_name[i], ++ fuse_volt[i]); ++ } ++ ++ rc = cpr3_determine_part_type(vreg, ++ fuse_volt[vreg->fuse_corner_count - 1]); ++ if (rc) { ++ cpr3_err(vreg, "fused part type detection failed failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt); ++ if (rc) { ++ cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ allow_interpolation = of_property_read_bool(node, ++ "qcom,allow-voltage-interpolation"); ++ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ if (fuse_volt[i] < fuse_volt[i - 1]) { ++ cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", ++ i, fuse_volt[i], i - 1, fuse_volt[i - 1], ++ i, fuse_volt[i - 1]); ++ fuse_volt[i] = fuse_volt[i - 1]; ++ } ++ } ++ ++ if (!allow_interpolation) { ++ /* Use fused open-loop voltage for lower frequencies. */ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].open_loop_volt ++ = fuse_volt[vreg->corner[i].cpr_fuse_corner]; ++ goto done; ++ } ++ ++ /* Determine highest corner mapped to each fuse corner */ ++ j = vreg->fuse_corner_count - 1; ++ for (i = vreg->corner_count - 1; i >= 0; i--) { ++ if (vreg->corner[i].cpr_fuse_corner == j) { ++ fmax_corner[j] = i; ++ j--; ++ } ++ } ++ if (j >= 0) { ++ cpr3_err(vreg, "invalid fuse corner mapping\n"); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ /* ++ * Interpolation is not possible for corners mapped to the lowest fuse ++ * corner so use the fuse corner value directly. ++ */ ++ for (i = 0; i <= fmax_corner[0]; i++) ++ vreg->corner[i].open_loop_volt = fuse_volt[0]; ++ ++ /* Interpolate voltages for the higher fuse corners. */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; ++ volt_low = fuse_volt[i - 1]; ++ freq_high = vreg->corner[fmax_corner[i]].proc_freq; ++ volt_high = fuse_volt[i]; ++ ++ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) ++ vreg->corner[j].open_loop_volt = cpr3_interpolate( ++ freq_low, volt_low, freq_high, volt_high, ++ vreg->corner[j].proc_freq); ++ } ++ ++done: ++ if (rc == 0) { ++ cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n"); ++ for (i = 0; i < vreg->corner_count; i++) ++ cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i, ++ vreg->corner[i].open_loop_volt); ++ ++ rc = cpr3_adjust_open_loop_voltages(vreg); ++ if (rc) ++ cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n", ++ rc); ++ ++ if (of_find_property(node, ++ "qcom,cpr-misc-fuse-voltage-adjustment", ++ NULL)) { ++ misc_adj_volt = kcalloc(vreg->corner_count, ++ sizeof(*misc_adj_volt), GFP_KERNEL); ++ if (!misc_adj_volt) { ++ rc = -ENOMEM; ++ goto _exit; ++ } ++ ++ rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg, ++ misc_adj_volt); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n", ++ rc); ++ kfree(misc_adj_volt); ++ goto _exit; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ vreg->corner[i].open_loop_volt ++ += misc_adj_volt[i]; ++ kfree(misc_adj_volt); ++ } ++ } ++ ++_exit: ++ kfree(fuse_volt); ++ kfree(fmax_corner); ++ return rc; ++} ++ ++/** ++ * cpr4_ipq807x_apss_set_no_interpolation_quotients() - use the fused target ++ * quotient values for lower frequencies. ++ * @vreg: Pointer to the CPR3 regulator ++ * @volt_adjust: Pointer to array of per-corner closed-loop adjustment ++ * voltages ++ * @volt_adjust_fuse: Pointer to array of per-fuse-corner closed-loop ++ * adjustment voltages ++ * @ro_scale: Pointer to array of per-fuse-corner RO scaling factor ++ * values with units of QUOT/V ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_set_no_interpolation_quotients( ++ struct cpr3_regulator *vreg, int *volt_adjust, ++ int *volt_adjust_fuse, int *ro_scale) ++{ ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ u32 quot, ro; ++ int quot_adjust; ++ int i, fuse_corner; ++ ++ for (i = 0; i < vreg->corner_count; i++) { ++ fuse_corner = vreg->corner[i].cpr_fuse_corner; ++ quot = fuse->target_quot[fuse_corner]; ++ quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner], ++ volt_adjust_fuse[fuse_corner] + ++ volt_adjust[i]); ++ ro = fuse->ro_sel[fuse_corner]; ++ vreg->corner[i].target_quot[ro] = quot + quot_adjust; ++ cpr3_debug(vreg, "corner=%d RO=%u target quot=%u\n", ++ i, ro, quot); ++ ++ if (quot_adjust) ++ cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n", ++ i, ro, quot, vreg->corner[i].target_quot[ro], ++ volt_adjust_fuse[fuse_corner] + ++ volt_adjust[i]); ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr4_ipq807x_apss_calculate_target_quotients() - calculate the CPR target ++ * quotient for each corner of a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * If target quotient interpolation is allowed in device tree, then this ++ * function calculates the target quotient for a given corner using linear ++ * interpolation. This interpolation is performed using the processor ++ * frequencies of the lower and higher Fmax corners along with the fused ++ * target quotient and quotient offset of the higher Fmax corner. ++ * ++ * If target quotient interpolation is not allowed, then this function uses ++ * the Fmax fused target quotient for all of the corners associated with a ++ * given fuse corner. ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_ipq807x_apss_calculate_target_quotients( ++ struct cpr3_regulator *vreg) ++{ ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ int rc; ++ bool allow_interpolation; ++ u64 freq_low, freq_high, prev_quot; ++ u64 *quot_low; ++ u64 *quot_high; ++ u32 quot, ro; ++ int i, j, fuse_corner, quot_adjust; ++ int *fmax_corner; ++ int *volt_adjust, *volt_adjust_fuse, *ro_scale; ++ int *voltage_adj_misc; ++ ++ /* Log fused quotient values for debugging purposes. */ ++ for (i = CPR4_IPQ807x_APSS_FUSE_CORNER_SVS; ++ i < vreg->fuse_corner_count; i++) ++ cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu, quot_offset[%2llu]=%4llu\n", ++ cpr4_ipq807x_apss_fuse_corner_name[i], ++ fuse->ro_sel[i], fuse->target_quot[i], ++ fuse->ro_sel[i], fuse->quot_offset[i] * ++ IPQ807x_APSS_QUOT_OFFSET_SCALE); ++ ++ allow_interpolation = of_property_read_bool(vreg->of_node, ++ "qcom,allow-quotient-interpolation"); ++ ++ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), ++ GFP_KERNEL); ++ volt_adjust_fuse = kcalloc(vreg->fuse_corner_count, ++ sizeof(*volt_adjust_fuse), GFP_KERNEL); ++ ro_scale = kcalloc(vreg->fuse_corner_count, sizeof(*ro_scale), ++ GFP_KERNEL); ++ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), ++ GFP_KERNEL); ++ quot_low = kcalloc(vreg->fuse_corner_count, sizeof(*quot_low), ++ GFP_KERNEL); ++ quot_high = kcalloc(vreg->fuse_corner_count, sizeof(*quot_high), ++ GFP_KERNEL); ++ if (!volt_adjust || !volt_adjust_fuse || !ro_scale || ++ !fmax_corner || !quot_low || !quot_high) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr3_parse_closed_loop_voltage_adjustments(vreg, &fuse->ro_sel[0], ++ volt_adjust, volt_adjust_fuse, ro_scale); ++ if (rc) { ++ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", ++ rc); ++ goto done; ++ } ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-misc-fuse-voltage-adjustment", NULL)) { ++ voltage_adj_misc = kcalloc(vreg->corner_count, ++ sizeof(*voltage_adj_misc), GFP_KERNEL); ++ if (!voltage_adj_misc) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg, ++ voltage_adj_misc); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n", ++ rc); ++ kfree(voltage_adj_misc); ++ goto done; ++ } ++ ++ for (i = 0; i < vreg->corner_count; i++) ++ volt_adjust[i] += voltage_adj_misc[i]; ++ ++ kfree(voltage_adj_misc); ++ } ++ ++ if (!allow_interpolation) { ++ /* Use fused target quotients for lower frequencies. */ ++ return cpr4_ipq807x_apss_set_no_interpolation_quotients( ++ vreg, volt_adjust, volt_adjust_fuse, ro_scale); ++ } ++ ++ /* Determine highest corner mapped to each fuse corner */ ++ j = vreg->fuse_corner_count - 1; ++ for (i = vreg->corner_count - 1; i >= 0; i--) { ++ if (vreg->corner[i].cpr_fuse_corner == j) { ++ fmax_corner[j] = i; ++ j--; ++ } ++ } ++ if (j >= 0) { ++ cpr3_err(vreg, "invalid fuse corner mapping\n"); ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ /* ++ * Interpolation is not possible for corners mapped to the lowest fuse ++ * corner so use the fuse corner value directly. ++ */ ++ i = CPR4_IPQ807x_APSS_FUSE_CORNER_SVS; ++ quot_adjust = cpr3_quot_adjustment(ro_scale[i], volt_adjust_fuse[i]); ++ quot = fuse->target_quot[i] + quot_adjust; ++ quot_high[i] = quot_low[i] = quot; ++ ro = fuse->ro_sel[i]; ++ if (quot_adjust) ++ cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n", ++ i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]); ++ ++ for (i = 0; i <= fmax_corner[CPR4_IPQ807x_APSS_FUSE_CORNER_SVS]; ++ i++) ++ vreg->corner[i].target_quot[ro] = quot; ++ ++ for (i = CPR4_IPQ807x_APSS_FUSE_CORNER_NOM; ++ i < vreg->fuse_corner_count; i++) { ++ quot_high[i] = fuse->target_quot[i]; ++ if (fuse->ro_sel[i] == fuse->ro_sel[i - 1]) ++ quot_low[i] = quot_high[i - 1]; ++ else ++ quot_low[i] = quot_high[i] ++ - fuse->quot_offset[i] ++ * IPQ807x_APSS_QUOT_OFFSET_SCALE; ++ if (quot_high[i] < quot_low[i]) { ++ cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n", ++ i, quot_high[i], i, quot_low[i], ++ i, quot_low[i]); ++ quot_high[i] = quot_low[i]; ++ } ++ } ++ ++ /* Perform per-fuse-corner target quotient adjustment */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ quot_adjust = cpr3_quot_adjustment(ro_scale[i], ++ volt_adjust_fuse[i]); ++ if (quot_adjust) { ++ prev_quot = quot_high[i]; ++ quot_high[i] += quot_adjust; ++ cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n", ++ i, fuse->ro_sel[i], prev_quot, quot_high[i], ++ volt_adjust_fuse[i]); ++ } ++ ++ if (fuse->ro_sel[i] == fuse->ro_sel[i - 1]) ++ quot_low[i] = quot_high[i - 1]; ++ else ++ quot_low[i] += cpr3_quot_adjustment(ro_scale[i], ++ volt_adjust_fuse[i - 1]); ++ ++ if (quot_high[i] < quot_low[i]) { ++ cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n", ++ i, quot_high[i], i, quot_low[i], ++ i, quot_low[i]); ++ quot_high[i] = quot_low[i]; ++ } ++ } ++ ++ /* Interpolate voltages for the higher fuse corners. */ ++ for (i = 1; i < vreg->fuse_corner_count; i++) { ++ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; ++ freq_high = vreg->corner[fmax_corner[i]].proc_freq; ++ ++ ro = fuse->ro_sel[i]; ++ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) ++ vreg->corner[j].target_quot[ro] = cpr3_interpolate( ++ freq_low, quot_low[i], freq_high, quot_high[i], ++ vreg->corner[j].proc_freq); ++ } ++ ++ /* Perform per-corner target quotient adjustment */ ++ for (i = 0; i < vreg->corner_count; i++) { ++ fuse_corner = vreg->corner[i].cpr_fuse_corner; ++ ro = fuse->ro_sel[fuse_corner]; ++ quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner], ++ volt_adjust[i]); ++ if (quot_adjust) { ++ prev_quot = vreg->corner[i].target_quot[ro]; ++ vreg->corner[i].target_quot[ro] += quot_adjust; ++ cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n", ++ i, ro, prev_quot, ++ vreg->corner[i].target_quot[ro], ++ volt_adjust[i]); ++ } ++ } ++ ++ /* Ensure that target quotients increase monotonically */ ++ for (i = 1; i < vreg->corner_count; i++) { ++ ro = fuse->ro_sel[vreg->corner[i].cpr_fuse_corner]; ++ if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro ++ && vreg->corner[i].target_quot[ro] ++ < vreg->corner[i - 1].target_quot[ro]) { ++ cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", ++ i, ro, vreg->corner[i].target_quot[ro], ++ i - 1, ro, vreg->corner[i - 1].target_quot[ro], ++ i, ro, vreg->corner[i - 1].target_quot[ro]); ++ vreg->corner[i].target_quot[ro] ++ = vreg->corner[i - 1].target_quot[ro]; ++ } ++ } ++ ++done: ++ kfree(volt_adjust); ++ kfree(volt_adjust_fuse); ++ kfree(ro_scale); ++ kfree(fmax_corner); ++ kfree(quot_low); ++ kfree(quot_high); ++ return rc; ++} ++ ++/** ++ * cpr4_apss_print_settings() - print out APSS CPR configuration settings into ++ * the kernel log for debugging purposes ++ * @vreg: Pointer to the CPR3 regulator ++ */ ++static void cpr4_apss_print_settings(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_corner *corner; ++ int i; ++ ++ cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n"); ++ for (i = 0; i < vreg->corner_count; i++) { ++ corner = &vreg->corner[i]; ++ cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n", ++ i, corner->proc_freq, corner->cpr_fuse_corner, ++ corner->floor_volt, corner->open_loop_volt, ++ corner->ceiling_volt); ++ } ++ ++ if (vreg->thread->ctrl->apm) ++ cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n", ++ vreg->thread->ctrl->apm_threshold_volt, ++ vreg->thread->ctrl->apm_adj_volt); ++} ++ ++/** ++ * cpr4_apss_init_thread() - perform steps necessary to initialize the ++ * configuration data for a CPR3 thread ++ * @thread: Pointer to the CPR3 thread ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_init_thread(struct cpr3_thread *thread) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_thread_data(thread); ++ if (rc) { ++ cpr3_err(thread->ctrl, "thread %u unable to read CPR thread data from device tree, rc=%d\n", ++ thread->thread_id, rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/** ++ * cpr4_apss_parse_temp_adj_properties() - parse temperature based ++ * adjustment properties from device tree. ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_temp_adj_properties(struct cpr3_controller *ctrl) ++{ ++ struct device_node *of_node = ctrl->dev->of_node; ++ int rc, i, len, temp_point_count; ++ ++ if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) { ++ /* ++ * Temperature based adjustments are not defined. Single ++ * temperature band is still valid for per-online-core ++ * adjustments. ++ */ ++ ctrl->temp_band_count = 1; ++ return 0; ++ } ++ ++ temp_point_count = len / sizeof(u32); ++ if (temp_point_count <= 0 || ++ temp_point_count > IPQ807x_APSS_MAX_TEMP_POINTS) { ++ cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n", ++ temp_point_count, IPQ807x_APSS_MAX_TEMP_POINTS); ++ return -EINVAL; ++ } ++ ++ ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count, ++ sizeof(*ctrl->temp_points), GFP_KERNEL); ++ if (!ctrl->temp_points) ++ return -ENOMEM; ++ ++ rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map", ++ ctrl->temp_points, temp_point_count); ++ if (rc) { ++ cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ for (i = 0; i < temp_point_count; i++) ++ cpr3_debug(ctrl, "Temperature Point %d=%d\n", i, ++ ctrl->temp_points[i]); ++ ++ /* ++ * If t1, t2, and t3 are the temperature points, then the temperature ++ * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). ++ */ ++ ctrl->temp_band_count = temp_point_count + 1; ++ cpr3_debug(ctrl, "Number of temp bands =%d\n", ctrl->temp_band_count); ++ ++ rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band", ++ &ctrl->initial_temp_band); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->initial_temp_band >= ctrl->temp_band_count) { ++ cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n", ++ ctrl->initial_temp_band, ctrl->temp_band_count - 1); ++ return -EINVAL; ++ } ++ ++ ctrl->temp_sensor_id_start = IPQ807x_APSS_TEMP_SENSOR_ID_START; ++ ctrl->temp_sensor_id_end = IPQ807x_APSS_TEMP_SENSOR_ID_END; ++ ctrl->allow_temp_adj = true; ++ return rc; ++} ++ ++/** ++ * cpr4_apss_parse_boost_properties() - parse configuration data for boost ++ * voltage adjustment for CPR3 regulator from device tree. ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_parse_boost_properties(struct cpr3_regulator *vreg) ++{ ++ struct cpr3_controller *ctrl = vreg->thread->ctrl; ++ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; ++ struct cpr3_corner *corner; ++ int i, boost_voltage, final_boost_volt, rc = 0; ++ int *boost_table = NULL, *boost_temp_adj = NULL; ++ int boost_voltage_adjust = 0, boost_num_cores = 0; ++ u32 boost_allowed = 0; ++ ++ if (!boost_fuse[fuse->boost_cfg]) ++ /* Voltage boost is disabled in fuse */ ++ return 0; ++ ++ if (of_find_property(vreg->of_node, "qcom,allow-boost", NULL)) { ++ rc = cpr3_parse_array_property(vreg, "qcom,allow-boost", 1, ++ &boost_allowed); ++ if (rc) ++ return rc; ++ } ++ ++ if (!boost_allowed) { ++ /* Voltage boost is not enabled for this regulator */ ++ return 0; ++ } ++ ++ boost_voltage = cpr3_convert_open_loop_voltage_fuse( ++ vreg->cpr4_regulator_data->boost_fuse_ref_volt, ++ vreg->cpr4_regulator_data->fuse_step_volt, ++ fuse->boost_voltage, ++ IPQ807x_APSS_VOLTAGE_FUSE_SIZE); ++ ++ /* Log boost voltage value for debugging purposes. */ ++ cpr3_info(vreg, "Boost open-loop=%7d uV\n", boost_voltage); ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-boost-voltage-fuse-adjustment", NULL)) { ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-boost-voltage-fuse-adjustment", ++ 1, &boost_voltage_adjust); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-boost-voltage-fuse-adjustment reading failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ boost_voltage += boost_voltage_adjust; ++ /* Log boost voltage value for debugging purposes. */ ++ cpr3_info(vreg, "Adjusted boost open-loop=%7d uV\n", ++ boost_voltage); ++ } ++ ++ /* Limit boost voltage value between ceiling and floor voltage limits */ ++ boost_voltage = min(boost_voltage, vreg->cpr4_regulator_data->boost_ceiling_volt); ++ boost_voltage = max(boost_voltage, vreg->cpr4_regulator_data->boost_floor_volt); ++ ++ /* ++ * The boost feature can only be used for the highest voltage corner. ++ * Also, keep core-count adjustments disabled when the boost feature ++ * is enabled. ++ */ ++ corner = &vreg->corner[vreg->corner_count - 1]; ++ if (!corner->sdelta) { ++ /* ++ * If core-count/temp adjustments are not defined, the cpr4 ++ * sdelta for this corner will not be allocated. Allocate it ++ * here for boost configuration. ++ */ ++ corner->sdelta = devm_kzalloc(ctrl->dev, ++ sizeof(*corner->sdelta), GFP_KERNEL); ++ if (!corner->sdelta) ++ return -ENOMEM; ++ } ++ corner->sdelta->temp_band_count = ctrl->temp_band_count; ++ ++ rc = of_property_read_u32(vreg->of_node, "qcom,cpr-num-boost-cores", ++ &boost_num_cores); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-num-boost-cores reading failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (boost_num_cores <= 0 || ++ boost_num_cores > IPQ807x_APSS_CPR_SDELTA_CORE_COUNT) { ++ cpr3_err(vreg, "Invalid boost number of cores = %d\n", ++ boost_num_cores); ++ return -EINVAL; ++ } ++ corner->sdelta->boost_num_cores = boost_num_cores; ++ ++ boost_table = devm_kcalloc(ctrl->dev, corner->sdelta->temp_band_count, ++ sizeof(*boost_table), GFP_KERNEL); ++ if (!boost_table) ++ return -ENOMEM; ++ ++ if (of_find_property(vreg->of_node, ++ "qcom,cpr-boost-temp-adjustment", NULL)) { ++ boost_temp_adj = kcalloc(corner->sdelta->temp_band_count, ++ sizeof(*boost_temp_adj), GFP_KERNEL); ++ if (!boost_temp_adj) ++ return -ENOMEM; ++ ++ rc = cpr3_parse_array_property(vreg, ++ "qcom,cpr-boost-temp-adjustment", ++ corner->sdelta->temp_band_count, ++ boost_temp_adj); ++ if (rc) { ++ cpr3_err(vreg, "qcom,cpr-boost-temp-adjustment reading failed, rc=%d\n", ++ rc); ++ goto done; ++ } ++ } ++ ++ for (i = 0; i < corner->sdelta->temp_band_count; i++) { ++ /* Apply static adjustments to boost voltage */ ++ final_boost_volt = boost_voltage + (boost_temp_adj == NULL ++ ? 0 : boost_temp_adj[i]); ++ /* ++ * Limit final adjusted boost voltage value between ceiling ++ * and floor voltage limits ++ */ ++ final_boost_volt = min(final_boost_volt, ++ vreg->cpr4_regulator_data->boost_ceiling_volt); ++ final_boost_volt = max(final_boost_volt, ++ vreg->cpr4_regulator_data->boost_floor_volt); ++ ++ boost_table[i] = (corner->open_loop_volt - final_boost_volt) ++ / ctrl->step_volt; ++ cpr3_debug(vreg, "Adjusted boost voltage margin for temp band %d = %d steps\n", ++ i, boost_table[i]); ++ } ++ ++ corner->ceiling_volt = vreg->cpr4_regulator_data->boost_ceiling_volt; ++ corner->sdelta->boost_table = boost_table; ++ corner->sdelta->allow_boost = true; ++ corner->sdelta->allow_core_count_adj = false; ++ vreg->allow_boost = true; ++ ctrl->allow_boost = true; ++done: ++ kfree(boost_temp_adj); ++ return rc; ++} ++ ++/** ++ * cpr4_apss_init_regulator() - perform all steps necessary to initialize the ++ * configuration data for a CPR3 regulator ++ * @vreg: Pointer to the CPR3 regulator ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_init_regulator(struct cpr3_regulator *vreg) ++{ ++ struct cpr4_ipq807x_apss_fuses *fuse; ++ int rc; ++ ++ rc = cpr4_ipq807x_apss_read_fuse_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc); ++ return rc; ++ } ++ ++ fuse = vreg->platform_fuses; ++ ++ rc = cpr4_apss_parse_corner_data(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_mem_acc_init(vreg); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(vreg, "unable to initialize mem-acc regulator settings, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_ipq807x_apss_calculate_open_loop_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_limit_open_loop_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ cpr3_open_loop_voltage_as_ceiling(vreg); ++ ++ rc = cpr3_limit_floor_voltages(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc); ++ return rc; ++ } ++ ++ rc = cpr4_ipq807x_apss_calculate_target_quotients(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to calculate target quotients, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false); ++ if (rc) { ++ cpr3_err(vreg, "unable to parse temperature and core count voltage adjustments, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (vreg->allow_core_count_adj && (vreg->max_core_count <= 0 ++ || vreg->max_core_count > ++ IPQ807x_APSS_CPR_SDELTA_CORE_COUNT)) { ++ cpr3_err(vreg, "qcom,max-core-count has invalid value = %d\n", ++ vreg->max_core_count); ++ return -EINVAL; ++ } ++ ++ rc = cpr4_apss_parse_boost_properties(vreg); ++ if (rc) { ++ cpr3_err(vreg, "unable to parse boost adjustments, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ cpr4_apss_print_settings(vreg); ++ ++ return rc; ++} ++ ++/** ++ * cpr4_apss_init_controller() - perform APSS CPR4 controller specific ++ * initializations ++ * @ctrl: Pointer to the CPR3 controller ++ * ++ * Return: 0 on success, errno on failure ++ */ ++static int cpr4_apss_init_controller(struct cpr3_controller *ctrl) ++{ ++ int rc; ++ ++ rc = cpr3_parse_common_ctrl_data(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-down-error-step-limit", ++ &ctrl->down_error_step_limit); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,cpr-down-error-step-limit, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-up-error-step-limit", ++ &ctrl->up_error_step_limit); ++ if (rc) { ++ cpr3_err(ctrl, "error reading qcom,cpr-up-error-step-limit, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ /* ++ * Use fixed step quotient if specified otherwise use dynamic ++ * calculated per RO step quotient ++ */ ++ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed", ++ &ctrl->step_quot_fixed); ++ ctrl->use_dynamic_step_quot = ctrl->step_quot_fixed ? false : true; ++ ++ ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node, ++ "qcom,cpr-saw-use-unit-mV"); ++ ++ of_property_read_u32(ctrl->dev->of_node, ++ "qcom,cpr-voltage-settling-time", ++ &ctrl->voltage_settling_time); ++ ++ if (of_find_property(ctrl->dev->of_node, "vdd-limit-supply", NULL)) { ++ ctrl->vdd_limit_regulator = ++ devm_regulator_get(ctrl->dev, "vdd-limit"); ++ if (IS_ERR(ctrl->vdd_limit_regulator)) { ++ rc = PTR_ERR(ctrl->vdd_limit_regulator); ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to request vdd-limit regulator, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ rc = cpr3_apm_init(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "unable to initialize APM settings, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_apss_parse_temp_adj_properties(ctrl); ++ if (rc) { ++ cpr3_err(ctrl, "unable to parse temperature adjustment properties, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ ctrl->sensor_count = IPQ807x_APSS_CPR_SENSOR_COUNT; ++ ++ /* ++ * APSS only has one thread (0) per controller so the zeroed ++ * array does not need further modification. ++ */ ++ ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count, ++ sizeof(*ctrl->sensor_owner), GFP_KERNEL); ++ if (!ctrl->sensor_owner) ++ return -ENOMEM; ++ ++ ctrl->ctrl_type = CPR_CTRL_TYPE_CPR4; ++ ctrl->supports_hw_closed_loop = false; ++ ctrl->use_hw_closed_loop = of_property_read_bool(ctrl->dev->of_node, ++ "qcom,cpr-hw-closed-loop"); ++ return 0; ++} ++ ++static int cpr4_apss_regulator_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_regulator_suspend(ctrl); ++} ++ ++static int cpr4_apss_regulator_resume(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_regulator_resume(ctrl); ++} ++ ++static void ipq6018_set_mem_acc(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ ipq6018_mem_acc_tcsr[0].ioremap_addr = ++ ioremap(ipq6018_mem_acc_tcsr[0].phy_addr, 0x4); ++ ipq6018_mem_acc_tcsr[1].ioremap_addr = ++ ioremap(ipq6018_mem_acc_tcsr[1].phy_addr, 0x4); ++ ++ if ((ipq6018_mem_acc_tcsr[0].ioremap_addr != NULL) && ++ (ipq6018_mem_acc_tcsr[1].ioremap_addr != NULL) && ++ (vreg->current_corner == (vreg->corner_count - CPR3_CORNER_OFFSET))) { ++ ++ writel_relaxed(ipq6018_mem_acc_tcsr[0].value, ++ ipq6018_mem_acc_tcsr[0].ioremap_addr); ++ writel_relaxed(ipq6018_mem_acc_tcsr[1].value, ++ ipq6018_mem_acc_tcsr[1].ioremap_addr); ++ } ++} ++ ++static void ipq6018_clr_mem_acc(struct regulator_dev *rdev) ++{ ++ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); ++ ++ if ((ipq6018_mem_acc_tcsr[0].ioremap_addr != NULL) && ++ (ipq6018_mem_acc_tcsr[1].ioremap_addr != NULL) && ++ (vreg->current_corner != vreg->corner_count - CPR3_CORNER_OFFSET)) { ++ writel_relaxed(0x0, ipq6018_mem_acc_tcsr[0].ioremap_addr); ++ writel_relaxed(0x0, ipq6018_mem_acc_tcsr[1].ioremap_addr); ++ } ++ ++ iounmap(ipq6018_mem_acc_tcsr[0].ioremap_addr); ++ iounmap(ipq6018_mem_acc_tcsr[1].ioremap_addr); ++} ++ ++static struct cpr4_mem_acc_func ipq6018_mem_acc_funcs = { ++ .set_mem_acc = ipq6018_set_mem_acc, ++ .clear_mem_acc = ipq6018_clr_mem_acc ++}; ++ ++static const struct cpr4_reg_data ipq807x_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ807x_APSS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq807x_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt= IPQ807x_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt= IPQ807x_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt= IPQ807x_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq807x_fuse_params, ++ .mem_acc_funcs = NULL, ++}; ++ ++static const struct cpr4_reg_data ipq817x_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ817x_APPS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq807x_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ807x_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ807x_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt= IPQ807x_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt= IPQ807x_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt= IPQ807x_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq807x_fuse_params, ++ .mem_acc_funcs = NULL, ++}; ++ ++static const struct cpr4_reg_data ipq6018_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ6018_APSS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq6018_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ6018_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ6018_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt = IPQ6018_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt = IPQ6018_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt = IPQ6018_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq6018_fuse_params, ++ .mem_acc_funcs = &ipq6018_mem_acc_funcs, ++}; ++ ++static const struct cpr4_reg_data ipq9574_cpr_apss = { ++ .cpr_valid_fuse_count = IPQ9574_APSS_FUSE_CORNERS, ++ .fuse_ref_volt = ipq9574_apss_fuse_ref_volt, ++ .fuse_step_volt = IPQ9574_APSS_FUSE_STEP_VOLT, ++ .cpr_clk_rate = IPQ6018_APSS_CPR_CLOCK_RATE, ++ .boost_fuse_ref_volt = IPQ6018_APSS_BOOST_FUSE_REF_VOLT, ++ .boost_ceiling_volt = IPQ6018_APSS_BOOST_CEILING_VOLT, ++ .boost_floor_volt = IPQ6018_APSS_BOOST_FLOOR_VOLT, ++ .cpr3_fuse_params = &ipq9574_fuse_params, ++ .mem_acc_funcs = NULL, ++}; ++ ++static struct of_device_id cpr4_regulator_match_table[] = { ++ { ++ .compatible = "qcom,cpr4-ipq807x-apss-regulator", ++ .data = &ipq807x_cpr_apss ++ }, ++ { ++ .compatible = "qcom,cpr4-ipq817x-apss-regulator", ++ .data = &ipq817x_cpr_apss ++ }, ++ { ++ .compatible = "qcom,cpr4-ipq6018-apss-regulator", ++ .data = &ipq6018_cpr_apss ++ }, ++ { ++ .compatible = "qcom,cpr4-ipq9574-apss-regulator", ++ .data = &ipq9574_cpr_apss ++ }, ++ {} ++}; ++ ++static int cpr4_apss_regulator_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct cpr3_controller *ctrl; ++ const struct of_device_id *match; ++ struct cpr4_reg_data *cpr_data; ++ int i, rc; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "Device tree node is missing\n"); ++ return -EINVAL; ++ } ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ match = of_match_device(cpr4_regulator_match_table, &pdev->dev); ++ if (!match) ++ return -ENODEV; ++ ++ cpr_data = (struct cpr4_reg_data *)match->data; ++ g_valid_fuse_count = cpr_data->cpr_valid_fuse_count; ++ dev_info(dev, "CPR valid fuse count: %d\n", g_valid_fuse_count); ++ ctrl->cpr_clock_rate = cpr_data->cpr_clk_rate; ++ ++ ctrl->dev = dev; ++ /* Set to false later if anything precludes CPR operation. */ ++ ctrl->cpr_allowed_hw = true; ++ ++ rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name", ++ &ctrl->name); ++ if (rc) { ++ cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr3_map_fuse_base(ctrl, pdev); ++ if (rc) { ++ cpr3_err(ctrl, "could not map fuse base address\n"); ++ return rc; ++ } ++ ++ rc = cpr3_read_tcsr_setting(ctrl, pdev, IPQ807x_APSS_CPR_TCSR_START, ++ IPQ807x_APSS_CPR_TCSR_END); ++ if (rc) { ++ cpr3_err(ctrl, "could not read CPR tcsr setting\n"); ++ return rc; ++ } ++ ++ rc = cpr3_allocate_threads(ctrl, 0, 0); ++ if (rc) { ++ cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (ctrl->thread_count != 1) { ++ cpr3_err(ctrl, "expected 1 thread but found %d\n", ++ ctrl->thread_count); ++ return -EINVAL; ++ } ++ ++ rc = cpr4_apss_init_controller(ctrl); ++ if (rc) { ++ if (rc != -EPROBE_DEFER) ++ cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n", ++ rc); ++ return rc; ++ } ++ ++ rc = cpr4_apss_init_thread(&ctrl->thread[0]); ++ if (rc) { ++ cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc); ++ return rc; ++ } ++ ++ for (i = 0; i < ctrl->thread[0].vreg_count; i++) { ++ ctrl->thread[0].vreg[i].cpr4_regulator_data = cpr_data; ++ rc = cpr4_apss_init_regulator(&ctrl->thread[0].vreg[i]); ++ if (rc) { ++ cpr3_err(&ctrl->thread[0].vreg[i], "regulator initialization failed, rc=%d\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ platform_set_drvdata(pdev, ctrl); ++ ++ return cpr3_regulator_register(pdev, ctrl); ++} ++ ++static int cpr4_apss_regulator_remove(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return cpr3_regulator_unregister(ctrl); ++} ++ ++static struct platform_driver cpr4_apss_regulator_driver = { ++ .driver = { ++ .name = "qcom,cpr4-apss-regulator", ++ .of_match_table = cpr4_regulator_match_table, ++ .owner = THIS_MODULE, ++ }, ++ .probe = cpr4_apss_regulator_probe, ++ .remove = cpr4_apss_regulator_remove, ++ .suspend = cpr4_apss_regulator_suspend, ++ .resume = cpr4_apss_regulator_resume, ++}; ++ ++static int cpr4_regulator_init(void) ++{ ++ return platform_driver_register(&cpr4_apss_regulator_driver); ++} ++ ++static void cpr4_regulator_exit(void) ++{ ++ platform_driver_unregister(&cpr4_apss_regulator_driver); ++} ++ ++MODULE_DESCRIPTION("CPR4 APSS regulator driver"); ++MODULE_LICENSE("GPL v2"); ++ ++arch_initcall(cpr4_regulator_init); ++module_exit(cpr4_regulator_exit); +diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h +new file mode 100644 +index 0000000000000..db43446163a9a +--- /dev/null ++++ b/include/soc/qcom/socinfo.h +@@ -0,0 +1,463 @@ ++/* Copyright (c) 2009-2014, 2016, 2020, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++ ++#include ++ ++#define CPU_IPQ8074 323 ++#define CPU_IPQ8072 342 ++#define CPU_IPQ8076 343 ++#define CPU_IPQ8078 344 ++#define CPU_IPQ8070 375 ++#define CPU_IPQ8071 376 ++ ++#define CPU_IPQ8072A 389 ++#define CPU_IPQ8074A 390 ++#define CPU_IPQ8076A 391 ++#define CPU_IPQ8078A 392 ++#define CPU_IPQ8070A 395 ++#define CPU_IPQ8071A 396 ++ ++#define CPU_IPQ8172 397 ++#define CPU_IPQ8173 398 ++#define CPU_IPQ8174 399 ++ ++#define CPU_IPQ6018 402 ++#define CPU_IPQ6028 403 ++#define CPU_IPQ6000 421 ++#define CPU_IPQ6010 422 ++#define CPU_IPQ6005 453 ++ ++#define CPU_IPQ5010 446 ++#define CPU_IPQ5018 447 ++#define CPU_IPQ5028 448 ++#define CPU_IPQ5000 503 ++#define CPU_IPQ0509 504 ++#define CPU_IPQ0518 505 ++ ++#define CPU_IPQ9514 510 ++#define CPU_IPQ9554 512 ++#define CPU_IPQ9570 513 ++#define CPU_IPQ9574 514 ++#define CPU_IPQ9550 511 ++#define CPU_IPQ9510 521 ++ ++static inline int read_ipq_soc_version_major(void) ++{ ++ const int *prop; ++ prop = of_get_property(of_find_node_by_path("/"), "soc_version_major", ++ NULL); ++ ++ if (!prop) ++ return -EINVAL; ++ ++ return le32_to_cpu(*prop); ++} ++ ++static inline int read_ipq_cpu_type(void) ++{ ++ const int *prop; ++ prop = of_get_property(of_find_node_by_path("/"), "cpu_type", NULL); ++ /* ++ * Return Default CPU type if "cpu_type" property is not found in DTSI ++ */ ++ if (!prop) ++ return CPU_IPQ8074; ++ ++ return le32_to_cpu(*prop); ++} ++ ++static inline int cpu_is_ipq8070(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8070a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8172(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8172; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8173(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8173; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8174(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8174; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6005(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6005; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq0509(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ0509; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq0518(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ0518; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9514(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9514; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9554(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9554; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9570(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9570; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9574(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9574; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9550(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9550; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9510(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9510; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq807x(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq8072() || cpu_is_ipq8074() || ++ cpu_is_ipq8076() || cpu_is_ipq8078() || ++ cpu_is_ipq8070() || cpu_is_ipq8071() || ++ cpu_is_ipq8072a() || cpu_is_ipq8074a() || ++ cpu_is_ipq8076a() || cpu_is_ipq8078a() || ++ cpu_is_ipq8070a() || cpu_is_ipq8071a() || ++ cpu_is_ipq8172() || cpu_is_ipq8173() || ++ cpu_is_ipq8174(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq60xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq6018() || cpu_is_ipq6028() || ++ cpu_is_ipq6000() || cpu_is_ipq6010() || ++ cpu_is_ipq6005(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq50xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq5010() || cpu_is_ipq5018() || ++ cpu_is_ipq5028() || cpu_is_ipq5000() || ++ cpu_is_ipq0509() || cpu_is_ipq0518(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq95xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq9514() || cpu_is_ipq9554() || ++ cpu_is_ipq9570() || cpu_is_ipq9574() || ++ cpu_is_ipq9550() || cpu_is_ipq9510(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_nss_crypto_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq50xx() || cpu_is_ipq9570() || ++ cpu_is_ipq9550() || cpu_is_ipq9574() || ++ cpu_is_ipq9554(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_internal_wifi_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq50xx() || cpu_is_ipq9514() || ++ cpu_is_ipq9554() || cpu_is_ipq9574(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_uniphy1_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq9554() || cpu_is_ipq9570() || ++ cpu_is_ipq9574() || cpu_is_ipq9550(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_uniphy2_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq9570() || ++ cpu_is_ipq9574(); ++#else ++ return 0; ++#endif ++} ++ ++#endif /* _ARCH_ARM_MACH_MSM_SOCINFO_H_ */ +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0143-arm64-dts-ipq8074-add-CPU-clock.patch b/target/linux/ipq807x/patches-5.15/0143-arm64-dts-ipq8074-add-CPU-clock.patch new file mode 100644 index 00000000000000..cf2991bda29db5 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0143-arm64-dts-ipq8074-add-CPU-clock.patch @@ -0,0 +1,64 @@ +From 9869a576f99a0365925b3acc1bcc770102bd54d5 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 31 Dec 2021 17:56:14 +0100 +Subject: [PATCH 143/158] arm64: dts: ipq8074: add CPU clock + +Now that CPU clock is exposed and can be controlled, add the necessary +properties to the CPU nodes. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 789fec7c6aa47..a56f0ae28d392 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -4,6 +4,7 @@ + */ + + #include ++#include + #include + + / { +@@ -34,6 +35,8 @@ CPU0: cpu@0 { + reg = <0x0>; + next-level-cache = <&L2_0>; + enable-method = "psci"; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + CPU1: cpu@1 { +@@ -42,6 +45,8 @@ CPU1: cpu@1 { + enable-method = "psci"; + reg = <0x1>; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + CPU2: cpu@2 { +@@ -50,6 +55,8 @@ CPU2: cpu@2 { + enable-method = "psci"; + reg = <0x2>; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + CPU3: cpu@3 { +@@ -58,6 +65,8 @@ CPU3: cpu@3 { + enable-method = "psci"; + reg = <0x3>; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ clock-names = "cpu"; + }; + + L2_0: l2-cache { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0144-arm64-dts-ipq8074-add-label-to-cpus.patch b/target/linux/ipq807x/patches-5.15/0144-arm64-dts-ipq8074-add-label-to-cpus.patch new file mode 100644 index 00000000000000..b418f5b13a5b11 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0144-arm64-dts-ipq8074-add-label-to-cpus.patch @@ -0,0 +1,30 @@ +From 21b69be68b8125ba3172aea939d250d334c97df7 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 31 Dec 2021 18:42:53 +0100 +Subject: [PATCH 144/158] arm64: dts: ipq8074: add label to cpus + +Add label to cpus node as that makes it easy to add OPP table in SoC model +specific DTSI as IPQ8074 family has differing clocks and voltages based on +the specific model. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index a56f0ae28d392..83280e8858634 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -25,7 +25,7 @@ xo: xo { + }; + }; + +- cpus { ++ cpus: cpus { + #address-cells = <0x1>; + #size-cells = <0x0>; + +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0145-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch b/target/linux/ipq807x/patches-5.15/0145-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch new file mode 100644 index 00000000000000..972d82e9c25c5f --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0145-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch @@ -0,0 +1,53 @@ +From 4322e53e3d1b61a9faa06c2e12feb1605bee2795 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 31 Dec 2021 20:38:06 +0100 +Subject: [PATCH 145/158] arm64: dts: ipq8074: add cooling cells to CPU nodes + +Since there is CPU Freq support as well as thermal sensor support +now for the IPQ8074, add cooling cells to CPU nodes so that they can +be used as cooling devices using CPU Freq. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 83280e8858634..eb66a0cc2b294 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -37,6 +37,7 @@ CPU0: cpu@0 { + enable-method = "psci"; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + CPU1: cpu@1 { +@@ -47,6 +48,7 @@ CPU1: cpu@1 { + next-level-cache = <&L2_0>; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + CPU2: cpu@2 { +@@ -57,6 +59,7 @@ CPU2: cpu@2 { + next-level-cache = <&L2_0>; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + CPU3: cpu@3 { +@@ -67,6 +70,7 @@ CPU3: cpu@3 { + next-level-cache = <&L2_0>; + clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; + clock-names = "cpu"; ++ #cooling-cells = <2>; + }; + + L2_0: l2-cache { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0146-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch b/target/linux/ipq807x/patches-5.15/0146-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch new file mode 100644 index 00000000000000..3a9df9f9976d6a --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0146-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch @@ -0,0 +1,31 @@ +From 0107789cad3ffec3bb974e2c6f8a436a42078c08 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:10 +0530 +Subject: [PATCH 146/158] dt-bindings: clock: qcom: Add reset for WCSSAON + +Add binding for WCSSAON reset required for Q6v5 reset on IPQ8074 SoC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +Acked-by: Rob Herring +Acked-by: Stephen Boyd +--- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq8074.h b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +index e4991d3037080..7b425cede7074 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -367,6 +367,7 @@ + #define GCC_PCIE1_AHB_ARES 129 + #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 + #define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 ++#define GCC_WCSSAON_RESET 132 + + #define USB0_GDSC 0 + #define USB1_GDSC 1 +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0147-clk-qcom-Add-WCSSAON-reset.patch b/target/linux/ipq807x/patches-5.15/0147-clk-qcom-Add-WCSSAON-reset.patch new file mode 100644 index 00000000000000..e4df6040ffec80 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0147-clk-qcom-Add-WCSSAON-reset.patch @@ -0,0 +1,30 @@ +From 64886239bfdd9f10b6c6ed4ab4a36feb07beb5da Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:11 +0530 +Subject: [PATCH 147/158] clk: qcom: Add WCSSAON reset + +Add WCSSAON reset required for Q6v5 on IPQ8074 SoC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +Acked-by: Stephen Boyd +--- + drivers/clk/qcom/gcc-ipq8074.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index 42d185fe19c8c..7954a730aad29 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4826,6 +4826,7 @@ static const struct qcom_reset_map gcc_ipq8074_resets[] = { + [GCC_PCIE1_AXI_SLAVE_ARES] = { 0x76040, 4 }, + [GCC_PCIE1_AHB_ARES] = { 0x76040, 5 }, + [GCC_PCIE1_AXI_MASTER_STICKY_ARES] = { 0x76040, 6 }, ++ [GCC_WCSSAON_RESET] = { 0x59010, 0 }, + }; + + static struct gdsc *gcc_ipq8074_gdscs[] = { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0148-remoteproc-qcom-Add-PRNG-proxy-clock.patch b/target/linux/ipq807x/patches-5.15/0148-remoteproc-qcom-Add-PRNG-proxy-clock.patch new file mode 100644 index 00000000000000..1ca816ec1300a0 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0148-remoteproc-qcom-Add-PRNG-proxy-clock.patch @@ -0,0 +1,160 @@ +From 8e3ad3e9034fd1ec8dc22c80d861860f30a2cccc Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:05 +0530 +Subject: [PATCH 148/158] remoteproc: qcom: Add PRNG proxy clock + +PRNG clock is needed by the secure PIL, support for the same +is added in subsequent patches. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 65 +++++++++++++++++++++-------- + 1 file changed, 47 insertions(+), 18 deletions(-) + +diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c +index 20d50ec7eff1b..0e54840202967 100644 +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -91,19 +91,6 @@ enum { + WCSS_QCS404, + }; + +-struct wcss_data { +- const char *firmware_name; +- unsigned int crash_reason_smem; +- u32 version; +- bool aon_reset_required; +- bool wcss_q6_reset_required; +- const char *ssr_name; +- const char *sysmon_name; +- int ssctl_id; +- const struct rproc_ops *ops; +- bool requires_force_stop; +-}; +- + struct q6v5_wcss { + struct device *dev; + +@@ -128,6 +115,7 @@ struct q6v5_wcss { + struct clk *qdsp6ss_xo_cbcr; + struct clk *qdsp6ss_core_gfmux; + struct clk *lcc_bcr_sleep; ++ struct clk *prng_clk; + struct regulator *cx_supply; + struct qcom_sysmon *sysmon; + +@@ -151,6 +139,21 @@ struct q6v5_wcss { + struct qcom_rproc_ssr ssr_subdev; + }; + ++struct wcss_data { ++ int (*init_clock)(struct q6v5_wcss *wcss); ++ int (*init_regulator)(struct q6v5_wcss *wcss); ++ const char *firmware_name; ++ unsigned int crash_reason_smem; ++ u32 version; ++ bool aon_reset_required; ++ bool wcss_q6_reset_required; ++ const char *ssr_name; ++ const char *sysmon_name; ++ int ssctl_id; ++ const struct rproc_ops *ops; ++ bool requires_force_stop; ++}; ++ + static int q6v5_wcss_reset(struct q6v5_wcss *wcss) + { + int ret; +@@ -240,6 +243,12 @@ static int q6v5_wcss_start(struct rproc *rproc) + struct q6v5_wcss *wcss = rproc->priv; + int ret; + ++ ret = clk_prepare_enable(wcss->prng_clk); ++ if (ret) { ++ dev_err(wcss->dev, "prng clock enable failed\n"); ++ return ret; ++ } ++ + qcom_q6v5_prepare(&wcss->q6v5); + + /* Release Q6 and WCSS reset */ +@@ -732,6 +741,7 @@ static int q6v5_wcss_stop(struct rproc *rproc) + return ret; + } + ++ clk_disable_unprepare(wcss->prng_clk); + qcom_q6v5_unprepare(&wcss->q6v5); + + return 0; +@@ -896,7 +906,21 @@ static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss) + return 0; + } + +-static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss) ++static int ipq8074_init_clock(struct q6v5_wcss *wcss) ++{ ++ int ret; ++ ++ wcss->prng_clk = devm_clk_get(wcss->dev, "prng"); ++ if (IS_ERR(wcss->prng_clk)) { ++ ret = PTR_ERR(wcss->prng_clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(wcss->dev, "Failed to get prng clock\n"); ++ return ret; ++ } ++ return 0; ++} ++ ++static int qcs404_init_clock(struct q6v5_wcss *wcss) + { + int ret; + +@@ -986,7 +1010,7 @@ static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss) + return 0; + } + +-static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss) ++static int qcs404_init_regulator(struct q6v5_wcss *wcss) + { + wcss->cx_supply = devm_regulator_get(wcss->dev, "cx"); + if (IS_ERR(wcss->cx_supply)) +@@ -1030,12 +1054,14 @@ static int q6v5_wcss_probe(struct platform_device *pdev) + if (ret) + goto free_rproc; + +- if (wcss->version == WCSS_QCS404) { +- ret = q6v5_wcss_init_clock(wcss); ++ if (desc->init_clock) { ++ ret = desc->init_clock(wcss); + if (ret) + goto free_rproc; ++ } + +- ret = q6v5_wcss_init_regulator(wcss); ++ if (desc->init_regulator) { ++ ret = desc->init_regulator(wcss); + if (ret) + goto free_rproc; + } +@@ -1082,6 +1108,7 @@ static int q6v5_wcss_remove(struct platform_device *pdev) + } + + static const struct wcss_data wcss_ipq8074_res_init = { ++ .init_clock = ipq8074_init_clock, + .firmware_name = "IPQ8074/q6_fw.mdt", + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, +@@ -1091,6 +1118,8 @@ static const struct wcss_data wcss_ipq8074_res_init = { + }; + + static const struct wcss_data wcss_qcs404_res_init = { ++ .init_clock = qcs404_init_clock, ++ .init_regulator = qcs404_init_regulator, + .crash_reason_smem = WCSS_CRASH_REASON, + .firmware_name = "wcnss.mdt", + .version = WCSS_QCS404, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0149-remoteproc-qcom-Add-secure-PIL-support.patch b/target/linux/ipq807x/patches-5.15/0149-remoteproc-qcom-Add-secure-PIL-support.patch new file mode 100644 index 00000000000000..0f29091dd6002d --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0149-remoteproc-qcom-Add-secure-PIL-support.patch @@ -0,0 +1,148 @@ +From 745d35c0f94dd64ea2f3781a78a515d13f98afe2 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:06 +0530 +Subject: [PATCH 149/158] remoteproc: qcom: Add secure PIL support + +IPQ8074 uses secure PIL. Hence, adding the support for the same. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 43 +++++++++++++++++++++++++++-- + 1 file changed, 40 insertions(+), 3 deletions(-) + +diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c +index 0e54840202967..7d173b7816b87 100644 +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include "qcom_common.h" + #include "qcom_pil_info.h" + #include "qcom_q6v5.h" +@@ -86,6 +87,9 @@ + #define TCSR_WCSS_CLK_ENABLE 0x14 + + #define MAX_HALT_REG 3 ++ ++#define WCNSS_PAS_ID 6 ++ + enum { + WCSS_IPQ8074, + WCSS_QCS404, +@@ -134,6 +138,7 @@ struct q6v5_wcss { + unsigned int crash_reason_smem; + u32 version; + bool requires_force_stop; ++ bool need_mem_protection; + + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_ssr ssr_subdev; +@@ -152,6 +157,7 @@ struct wcss_data { + int ssctl_id; + const struct rproc_ops *ops; + bool requires_force_stop; ++ bool need_mem_protection; + }; + + static int q6v5_wcss_reset(struct q6v5_wcss *wcss) +@@ -251,6 +257,15 @@ static int q6v5_wcss_start(struct rproc *rproc) + + qcom_q6v5_prepare(&wcss->q6v5); + ++ if (wcss->need_mem_protection) { ++ ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID); ++ if (ret) { ++ dev_err(wcss->dev, "wcss_reset failed\n"); ++ return ret; ++ } ++ goto wait_for_reset; ++ } ++ + /* Release Q6 and WCSS reset */ + ret = reset_control_deassert(wcss->wcss_reset); + if (ret) { +@@ -285,6 +300,7 @@ static int q6v5_wcss_start(struct rproc *rproc) + if (ret) + goto wcss_q6_reset; + ++wait_for_reset: + ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ); + if (ret == -ETIMEDOUT) + dev_err(wcss->dev, "start timed out\n"); +@@ -717,6 +733,15 @@ static int q6v5_wcss_stop(struct rproc *rproc) + struct q6v5_wcss *wcss = rproc->priv; + int ret; + ++ if (wcss->need_mem_protection) { ++ ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID); ++ if (ret) { ++ dev_err(wcss->dev, "not able to shutdown\n"); ++ return ret; ++ } ++ goto pas_done; ++ } ++ + /* WCSS powerdown */ + if (wcss->requires_force_stop) { + ret = qcom_q6v5_request_stop(&wcss->q6v5, NULL); +@@ -741,6 +766,7 @@ static int q6v5_wcss_stop(struct rproc *rproc) + return ret; + } + ++pas_done: + clk_disable_unprepare(wcss->prng_clk); + qcom_q6v5_unprepare(&wcss->q6v5); + +@@ -764,9 +790,15 @@ static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw) + struct q6v5_wcss *wcss = rproc->priv; + int ret; + +- ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, +- 0, wcss->mem_region, wcss->mem_phys, +- wcss->mem_size, &wcss->mem_reloc); ++ if (wcss->need_mem_protection) ++ ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, ++ WCNSS_PAS_ID, wcss->mem_region, ++ wcss->mem_phys, wcss->mem_size, ++ &wcss->mem_reloc); ++ else ++ ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, ++ 0, wcss->mem_region, wcss->mem_phys, ++ wcss->mem_size, &wcss->mem_reloc); + if (ret) + return ret; + +@@ -1032,6 +1064,9 @@ static int q6v5_wcss_probe(struct platform_device *pdev) + if (!desc) + return -EINVAL; + ++ if (desc->need_mem_protection && !qcom_scm_is_available()) ++ return -EPROBE_DEFER; ++ + rproc = rproc_alloc(&pdev->dev, pdev->name, desc->ops, + desc->firmware_name, sizeof(*wcss)); + if (!rproc) { +@@ -1045,6 +1080,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev) + + wcss->version = desc->version; + wcss->requires_force_stop = desc->requires_force_stop; ++ wcss->need_mem_protection = desc->need_mem_protection; + + ret = q6v5_wcss_init_mmio(wcss, pdev); + if (ret) +@@ -1115,6 +1151,7 @@ static const struct wcss_data wcss_ipq8074_res_init = { + .wcss_q6_reset_required = true, + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, ++ .need_mem_protection = true, + }; + + static const struct wcss_data wcss_qcs404_res_init = { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0150-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch b/target/linux/ipq807x/patches-5.15/0150-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch new file mode 100644 index 00000000000000..37630b895f46d6 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0150-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch @@ -0,0 +1,109 @@ +From 825cae1fcdbe5c292dc2a1616b72fe3691613bb4 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:07 +0530 +Subject: [PATCH 150/158] remoteproc: qcom: Add support for split q6 + m3 wlan + firmware + +IPQ8074 supports split firmware for q6 and m3 as well. +So add support for loading the m3 firmware before q6. +Now the drivers works fine for both split and unified +firmwares. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 33 +++++++++++++++++++++++++---- + 1 file changed, 29 insertions(+), 4 deletions(-) + +diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c +index 7d173b7816b87..60ed0c0466935 100644 +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -139,6 +139,7 @@ struct q6v5_wcss { + u32 version; + bool requires_force_stop; + bool need_mem_protection; ++ const char *m3_firmware_name; + + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_ssr ssr_subdev; +@@ -147,7 +148,8 @@ struct q6v5_wcss { + struct wcss_data { + int (*init_clock)(struct q6v5_wcss *wcss); + int (*init_regulator)(struct q6v5_wcss *wcss); +- const char *firmware_name; ++ const char *q6_firmware_name; ++ const char *m3_firmware_name; + unsigned int crash_reason_smem; + u32 version; + bool aon_reset_required; +@@ -788,8 +790,29 @@ static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *i + static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw) + { + struct q6v5_wcss *wcss = rproc->priv; ++ const struct firmware *m3_fw; + int ret; + ++ if (wcss->m3_firmware_name) { ++ ret = request_firmware(&m3_fw, wcss->m3_firmware_name, ++ wcss->dev); ++ if (ret) ++ goto skip_m3; ++ ++ ret = qcom_mdt_load_no_init(wcss->dev, m3_fw, ++ wcss->m3_firmware_name, 0, ++ wcss->mem_region, wcss->mem_phys, ++ wcss->mem_size, &wcss->mem_reloc); ++ ++ release_firmware(m3_fw); ++ ++ if (ret) { ++ dev_err(wcss->dev, "can't load m3_fw.bXX\n"); ++ return ret; ++ } ++ } ++ ++skip_m3: + if (wcss->need_mem_protection) + ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, + WCNSS_PAS_ID, wcss->mem_region, +@@ -1068,7 +1091,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev) + return -EPROBE_DEFER; + + rproc = rproc_alloc(&pdev->dev, pdev->name, desc->ops, +- desc->firmware_name, sizeof(*wcss)); ++ desc->q6_firmware_name, sizeof(*wcss)); + if (!rproc) { + dev_err(&pdev->dev, "failed to allocate rproc\n"); + return -ENOMEM; +@@ -1081,6 +1104,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev) + wcss->version = desc->version; + wcss->requires_force_stop = desc->requires_force_stop; + wcss->need_mem_protection = desc->need_mem_protection; ++ wcss->m3_firmware_name = desc->m3_firmware_name; + + ret = q6v5_wcss_init_mmio(wcss, pdev); + if (ret) +@@ -1145,7 +1169,8 @@ static int q6v5_wcss_remove(struct platform_device *pdev) + + static const struct wcss_data wcss_ipq8074_res_init = { + .init_clock = ipq8074_init_clock, +- .firmware_name = "IPQ8074/q6_fw.mdt", ++ .q6_firmware_name = "IPQ8074/q6_fw.mdt", ++ .m3_firmware_name = "IPQ8074/m3_fw.mdt", + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, + .wcss_q6_reset_required = true, +@@ -1158,7 +1183,7 @@ static const struct wcss_data wcss_qcs404_res_init = { + .init_clock = qcs404_init_clock, + .init_regulator = qcs404_init_regulator, + .crash_reason_smem = WCSS_CRASH_REASON, +- .firmware_name = "wcnss.mdt", ++ .q6_firmware_name = "wcnss.mdt", + .version = WCSS_QCS404, + .aon_reset_required = false, + .wcss_q6_reset_required = false, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0151-remoteproc-qcom-Add-ssr-subdevice-identifier.patch b/target/linux/ipq807x/patches-5.15/0151-remoteproc-qcom-Add-ssr-subdevice-identifier.patch new file mode 100644 index 00000000000000..d3e750049e64d0 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0151-remoteproc-qcom-Add-ssr-subdevice-identifier.patch @@ -0,0 +1,29 @@ +From 79b295e9f5fc9d4061081411d47b749156c52dfa Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:08 +0530 +Subject: [PATCH 151/158] remoteproc: qcom: Add ssr subdevice identifier + +Add name for ssr subdevice on IPQ8074 SoC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c +index 60ed0c0466935..e32efdc660d21 100644 +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -1174,6 +1174,7 @@ static const struct wcss_data wcss_ipq8074_res_init = { + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, + .wcss_q6_reset_required = true, ++ .ssr_name = "q6wcss", + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, + .need_mem_protection = true, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0152-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch b/target/linux/ipq807x/patches-5.15/0152-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch new file mode 100644 index 00000000000000..bc8ee486d19db3 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0152-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch @@ -0,0 +1,85 @@ +From 629110b65cffbdbb6fb47fd69b987547e41e43fd Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:09 +0530 +Subject: [PATCH 152/158] remoteproc: qcom: Update regmap offsets for halt + register + +Fixed issue in reading halt-regs parameter from device-tree. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 22 ++++++++++++++-------- + 1 file changed, 14 insertions(+), 8 deletions(-) + +diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c +index e32efdc660d21..16fc5a33adaf8 100644 +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -86,7 +86,7 @@ + #define TCSR_WCSS_CLK_MASK 0x1F + #define TCSR_WCSS_CLK_ENABLE 0x14 + +-#define MAX_HALT_REG 3 ++#define MAX_HALT_REG 4 + + #define WCNSS_PAS_ID 6 + +@@ -154,6 +154,7 @@ struct wcss_data { + u32 version; + bool aon_reset_required; + bool wcss_q6_reset_required; ++ bool bcr_reset_required; + const char *ssr_name; + const char *sysmon_name; + int ssctl_id; +@@ -874,10 +875,13 @@ static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss, + } + } + +- wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_bcr_reset"); +- if (IS_ERR(wcss->wcss_q6_bcr_reset)) { +- dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); +- return PTR_ERR(wcss->wcss_q6_bcr_reset); ++ if (desc->bcr_reset_required) { ++ wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, ++ "wcss_q6_bcr_reset"); ++ if (IS_ERR(wcss->wcss_q6_bcr_reset)) { ++ dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); ++ return PTR_ERR(wcss->wcss_q6_bcr_reset); ++ } + } + + return 0; +@@ -925,9 +929,9 @@ static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss, + return -EINVAL; + } + +- wcss->halt_q6 = halt_reg[0]; +- wcss->halt_wcss = halt_reg[1]; +- wcss->halt_nc = halt_reg[2]; ++ wcss->halt_q6 = halt_reg[1]; ++ wcss->halt_wcss = halt_reg[2]; ++ wcss->halt_nc = halt_reg[3]; + + return 0; + } +@@ -1174,6 +1178,7 @@ static const struct wcss_data wcss_ipq8074_res_init = { + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, + .wcss_q6_reset_required = true, ++ .bcr_reset_required = false, + .ssr_name = "q6wcss", + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, +@@ -1188,6 +1193,7 @@ static const struct wcss_data wcss_qcs404_res_init = { + .version = WCSS_QCS404, + .aon_reset_required = false, + .wcss_q6_reset_required = false, ++ .bcr_reset_required = true, + .ssr_name = "mpss", + .sysmon_name = "wcnss", + .ssctl_id = 0x12, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0153-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch b/target/linux/ipq807x/patches-5.15/0153-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch new file mode 100644 index 00000000000000..cbf6d88b8940ef --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0153-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch @@ -0,0 +1,52 @@ +From 4fc8837cdc11b0d544f5e6331a592597099e190d Mon Sep 17 00:00:00 2001 +From: Sivaprakash Murugesan +Date: Fri, 17 Apr 2020 16:37:10 +0530 +Subject: [PATCH 153/158] remoteproc: wcss: disable auto boot for IPQ8074 + +auto boot is disabled for IPQ8074 the wifi driver brings up the wcss. + +Signed-off-by: Sivaprakash Murugesan +Change-Id: Ia82edb7ee52f2bd010c099f151179d69a953ac88 +--- + drivers/remoteproc/qcom_q6v5_wcss.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c +index 16fc5a33adaf8..92c240976f55d 100644 +--- a/drivers/remoteproc/qcom_q6v5_wcss.c ++++ b/drivers/remoteproc/qcom_q6v5_wcss.c +@@ -161,6 +161,7 @@ struct wcss_data { + const struct rproc_ops *ops; + bool requires_force_stop; + bool need_mem_protection; ++ bool need_auto_boot; + }; + + static int q6v5_wcss_reset(struct q6v5_wcss *wcss) +@@ -1147,6 +1148,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev) + desc->sysmon_name, + desc->ssctl_id); + ++ rproc->auto_boot = desc->need_auto_boot; + ret = rproc_add(rproc); + if (ret) + goto free_rproc; +@@ -1183,6 +1185,7 @@ static const struct wcss_data wcss_ipq8074_res_init = { + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, + .need_mem_protection = true, ++ .need_auto_boot = false, + }; + + static const struct wcss_data wcss_qcs404_res_init = { +@@ -1199,6 +1202,7 @@ static const struct wcss_data wcss_qcs404_res_init = { + .ssctl_id = 0x12, + .ops = &q6v5_wcss_qcs404_ops, + .requires_force_stop = false, ++ .need_auto_boot = true, + }; + + static const struct of_device_id q6v5_wcss_of_match[] = { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0154-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch b/target/linux/ipq807x/patches-5.15/0154-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch new file mode 100644 index 00000000000000..1cf0cce3606f04 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0154-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch @@ -0,0 +1,125 @@ +From 6039d5f031dde961d4d12992b8cc501ad44ac8cf Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Sat, 30 Jan 2021 10:50:13 +0530 +Subject: [PATCH 154/158] arm64: dts: qcom: Enable Q6v5 WCSS for ipq8074 SoC + +Enable remoteproc WCSS PIL driver with glink and ssr subdevices. +Also enables smp2p and mailboxes required for IPC. + +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: Sricharan R +Signed-off-by: Nikhil Prakash V +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 81 +++++++++++++++++++++++++++ + 1 file changed, 81 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index eb66a0cc2b294..f78e5aa1b4742 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -149,6 +149,32 @@ scm { + }; + }; + ++ wcss: smp2p-wcss { ++ compatible = "qcom,smp2p"; ++ qcom,smem = <435>, <428>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <0 322 1>; ++ ++ mboxes = <&apcs_glb 9>; ++ ++ qcom,local-pid = <0>; ++ qcom,remote-pid = <1>; ++ ++ wcss_smp2p_out: master-kernel { ++ qcom,entry-name = "master-kernel"; ++ qcom,smp2p-feature-ssr-ack; ++ #qcom,smem-state-cells = <1>; ++ }; ++ ++ wcss_smp2p_in: slave-kernel { ++ qcom,entry-name = "slave-kernel"; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ }; ++ + soc: soc { + #address-cells = <0x1>; + #size-cells = <0x1>; +@@ -417,6 +443,11 @@ tcsr_mutex: hwlock@1905000 { + #hwlock-cells = <1>; + }; + ++ tcsr_q6: syscon@1945000 { ++ compatible = "syscon"; ++ reg = <0x01945000 0xe000>; ++ }; ++ + spmi_bus: spmi@200f000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0x0200f000 0x001000>, +@@ -928,6 +959,56 @@ apcs_glb: mailbox@b111000 { + #clock-cells = <1>; + #mbox-cells = <1>; + }; ++ ++ q6v5_wcss: q6v5_wcss@cd00000 { ++ compatible = "qcom,ipq8074-wcss-pil"; ++ reg = <0x0cd00000 0x4040>, ++ <0x004ab000 0x20>; ++ reg-names = "qdsp6", ++ "rmb"; ++ qca,auto-restart; ++ qca,extended-intc; ++ interrupts-extended = <&intc 0 325 1>, ++ <&wcss_smp2p_in 0 0>, ++ <&wcss_smp2p_in 1 0>, ++ <&wcss_smp2p_in 2 0>, ++ <&wcss_smp2p_in 3 0>; ++ interrupt-names = "wdog", ++ "fatal", ++ "ready", ++ "handover", ++ "stop-ack"; ++ ++ resets = <&gcc GCC_WCSSAON_RESET>, ++ <&gcc GCC_WCSS_BCR>, ++ <&gcc GCC_WCSS_Q6_BCR>; ++ ++ reset-names = "wcss_aon_reset", ++ "wcss_reset", ++ "wcss_q6_reset"; ++ ++ clocks = <&gcc GCC_PRNG_AHB_CLK>; ++ clock-names = "prng"; ++ ++ qcom,halt-regs = <&tcsr_q6 0xa000 0xd000 0x0>; ++ ++ qcom,smem-states = <&wcss_smp2p_out 0>, ++ <&wcss_smp2p_out 1>; ++ qcom,smem-state-names = "shutdown", ++ "stop"; ++ ++ memory-region = <&q6_region>; ++ ++ glink-edge { ++ interrupts = ; ++ qcom,remote-pid = <1>; ++ mboxes = <&apcs_glb 8>; ++ ++ rpm_requests { ++ qcom,glink-channels = "IPCRTR"; ++ }; ++ }; ++ }; + }; + + thermal-zones { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0155-arm64-dts-ipq8074-Add-WLAN-node.patch b/target/linux/ipq807x/patches-5.15/0155-arm64-dts-ipq8074-Add-WLAN-node.patch new file mode 100644 index 00000000000000..585f52a599798c --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0155-arm64-dts-ipq8074-Add-WLAN-node.patch @@ -0,0 +1,140 @@ +From fb7d9b7295284240a7e235bd185c724557c8f70e Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 21 Dec 2021 14:49:36 +0100 +Subject: [PATCH 155/158] arm64: dts: ipq8074: Add WLAN node + +IPQ8074 has a AHB based Q6v5 802.11ax radios that are supported +by the ath11k. + +Add the required DT node to enable the built-in radios. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 111 ++++++++++++++++++++++++++ + 1 file changed, 111 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index f78e5aa1b4742..38a114fc89dd3 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -1009,6 +1009,117 @@ rpm_requests { + }; + }; + }; ++ ++ wifi: wifi@c0000000 { ++ compatible = "qcom,ipq8074-wifi"; ++ reg = <0xc000000 0x2000000>; ++ ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ ++ interrupt-names = "misc-pulse1", ++ "misc-latch", ++ "sw-exception", ++ "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"; ++ qcom,rproc = <&q6v5_wcss>; ++ status = "disabled"; ++ }; + }; + + thermal-zones { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0156-clk-ipq-support-for-resetting-multiple-bits.patch b/target/linux/ipq807x/patches-5.15/0156-clk-ipq-support-for-resetting-multiple-bits.patch new file mode 100644 index 00000000000000..1503244e30a27c --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0156-clk-ipq-support-for-resetting-multiple-bits.patch @@ -0,0 +1,63 @@ +From 4ad71511c5560010cbfdea23a0314846d63e8ded Mon Sep 17 00:00:00 2001 +From: Rajkumar Ayyasamy +Date: Wed, 18 Mar 2020 17:08:11 +0530 +Subject: [PATCH 156/158] clk: ipq: support for resetting multiple bits + +Current reset structure takes only one reset bit and +calculates the bitmask in its reset operation. Some of the +reset registers contains multiple bits in which each bit +will be associated with subsystem reset inside the block. To +reset properly the complete block, all the subsystem reset +should be triggered at same time i.e the register write +should go in one AHB write. + +This patch adds the support for giving the complete bitmask +in reset structure and reset operation will use this bitmask +for all reset operations. + +Change-Id: Ief49f8746624a0fc1e067d815725ae7c254c2c6f +Signed-off-by: Abhishek Sahu +(cherry picked from commit ef555fc1cffa6e823a9d929711cacae0821b35ec) +Signed-off-by: Rajkumar Ayyasamy +--- + drivers/clk/qcom/reset.c | 4 ++-- + drivers/clk/qcom/reset.h | 1 + + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/clk/qcom/reset.c b/drivers/clk/qcom/reset.c +index 819d194be8f7b..8ad7b50dd5346 100644 +--- a/drivers/clk/qcom/reset.c ++++ b/drivers/clk/qcom/reset.c +@@ -28,7 +28,7 @@ qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) + + rst = to_qcom_reset_controller(rcdev); + map = &rst->reset_map[id]; +- mask = BIT(map->bit); ++ mask = map->bitmask ? map->bitmask : BIT(map->bit); + + return regmap_update_bits(rst->regmap, map->reg, mask, mask); + } +@@ -42,7 +42,7 @@ qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) + + rst = to_qcom_reset_controller(rcdev); + map = &rst->reset_map[id]; +- mask = BIT(map->bit); ++ mask = map->bitmask ? map->bitmask : BIT(map->bit); + + return regmap_update_bits(rst->regmap, map->reg, mask, 0); + } +diff --git a/drivers/clk/qcom/reset.h b/drivers/clk/qcom/reset.h +index 2a08b5e282c77..0410f83bf2bb0 100644 +--- a/drivers/clk/qcom/reset.h ++++ b/drivers/clk/qcom/reset.h +@@ -11,6 +11,7 @@ + struct qcom_reset_map { + unsigned int reg; + u8 bit; ++ u32 bitmask; + }; + + struct regmap; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0157-clk-qcom-ipq8074-add-missing-networking-resets.patch b/target/linux/ipq807x/patches-5.15/0157-clk-qcom-ipq8074-add-missing-networking-resets.patch new file mode 100644 index 00000000000000..7f88e84504b3d6 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0157-clk-qcom-ipq8074-add-missing-networking-resets.patch @@ -0,0 +1,70 @@ +From 7ea9e4f9786d6447aad832775cfae7de4f1a532e Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 1 Jan 2022 18:15:03 +0100 +Subject: [PATCH 157/158] clk: qcom: ipq8074: add missing networking resets + +Downstream QCA 5.4 kernel defines networking resets which are not present +in the mainline kernel but are required for the networking drivers. + +So, port the downstream resets and avoid using magic values for mask, +construct mask for resets which require multiple bits to be set/cleared. + +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 14 ++++++++++++++ + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 14 ++++++++++++++ + 2 files changed, 28 insertions(+) + +diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c +index 7954a730aad29..069ad3b5bca0b 100644 +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4827,6 +4827,20 @@ static const struct qcom_reset_map gcc_ipq8074_resets[] = { + [GCC_PCIE1_AHB_ARES] = { 0x76040, 5 }, + [GCC_PCIE1_AXI_MASTER_STICKY_ARES] = { 0x76040, 6 }, + [GCC_WCSSAON_RESET] = { 0x59010, 0 }, ++ [GCC_PPE_FULL_RESET] = { 0x68014, 0, GENMASK(19, 16) }, ++ [GCC_UNIPHY0_SOFT_RESET] = { 0x56004, 0, GENMASK(13, 4) | BIT(1) }, ++ [GCC_UNIPHY0_XPCS_RESET] = { 0x56004, 2 }, ++ [GCC_UNIPHY1_SOFT_RESET] = { 0x56104, 0, GENMASK(5, 4) | BIT(1) }, ++ [GCC_UNIPHY1_XPCS_RESET] = { 0x56104, 2 }, ++ [GCC_UNIPHY2_SOFT_RESET] = { 0x56204, 0, GENMASK(5, 4) | BIT(1) }, ++ [GCC_UNIPHY2_XPCS_RESET] = { 0x56204, 2 }, ++ [GCC_EDMA_HW_RESET] = { 0x68014, 0, GENMASK(21, 20) }, ++ [GCC_NSSPORT1_RESET] = { 0x68014, 0, BIT(24) | GENMASK(1, 0) }, ++ [GCC_NSSPORT2_RESET] = { 0x68014, 0, BIT(25) | GENMASK(3, 2) }, ++ [GCC_NSSPORT3_RESET] = { 0x68014, 0, BIT(26) | GENMASK(5, 4) }, ++ [GCC_NSSPORT4_RESET] = { 0x68014, 0, BIT(27) | GENMASK(9, 8) }, ++ [GCC_NSSPORT5_RESET] = { 0x68014, 0, BIT(28) | GENMASK(11, 10) }, ++ [GCC_NSSPORT6_RESET] = { 0x68014, 0, BIT(29) | GENMASK(13, 12) }, + }; + + static struct gdsc *gcc_ipq8074_gdscs[] = { +diff --git a/include/dt-bindings/clock/qcom,gcc-ipq8074.h b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +index 7b425cede7074..1e9ef05a36ddd 100644 +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -368,6 +368,20 @@ + #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 + #define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 + #define GCC_WCSSAON_RESET 132 ++#define GCC_PPE_FULL_RESET 133 ++#define GCC_UNIPHY0_SOFT_RESET 134 ++#define GCC_UNIPHY0_XPCS_RESET 135 ++#define GCC_UNIPHY1_SOFT_RESET 136 ++#define GCC_UNIPHY1_XPCS_RESET 137 ++#define GCC_UNIPHY2_SOFT_RESET 138 ++#define GCC_UNIPHY2_XPCS_RESET 139 ++#define GCC_EDMA_HW_RESET 140 ++#define GCC_NSSPORT1_RESET 141 ++#define GCC_NSSPORT2_RESET 142 ++#define GCC_NSSPORT3_RESET 143 ++#define GCC_NSSPORT4_RESET 144 ++#define GCC_NSSPORT5_RESET 145 ++#define GCC_NSSPORT6_RESET 146 + + #define USB0_GDSC 0 + #define USB1_GDSC 1 +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0158-arm64-dts-ipq8074-add-label-to-clocks.patch b/target/linux/ipq807x/patches-5.15/0158-arm64-dts-ipq8074-add-label-to-clocks.patch new file mode 100644 index 00000000000000..56f7d26a904a1b --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0158-arm64-dts-ipq8074-add-label-to-clocks.patch @@ -0,0 +1,29 @@ +From cf1d321595d1da7b9d460b6e8e98d0f04728f1ca Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 9 Feb 2022 23:13:26 +0100 +Subject: [PATCH 158/158] arm64: dts: ipq8074: add label to clocks + +Add label to clocks node as that makes it easy to add the NSS fixed +clocks that are required in their DTSI. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index 38a114fc89dd3..ff842f7df4126 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -11,7 +11,7 @@ / { + model = "Qualcomm Technologies, Inc. IPQ8074"; + compatible = "qcom,ipq8074"; + +- clocks { ++ clocks: clocks { + sleep_clk: sleep_clk { + compatible = "fixed-clock"; + clock-frequency = <32000>; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0159-arm64-dts-ipq8074-add-reset-to-SDHCI.patch b/target/linux/ipq807x/patches-5.15/0159-arm64-dts-ipq8074-add-reset-to-SDHCI.patch new file mode 100644 index 00000000000000..06109811e96c1d --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0159-arm64-dts-ipq8074-add-reset-to-SDHCI.patch @@ -0,0 +1,28 @@ +From 7117b0c9f1f6ecd630e29ed8b6bf530dd38370cb Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 19 May 2022 23:10:40 +0200 +Subject: [PATCH] arm64: dts: ipq8074: add reset to SDHCI + +Add reset to SDHCI controller so it can be reset to avoid timeout issues +after software reset due to bootloader set configuration. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +index ff842f7df4126..83717423f4d99 100644 +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -480,6 +480,7 @@ sdhc_1: sdhci@7824900 { + <&gcc GCC_SDCC1_AHB_CLK>, + <&gcc GCC_SDCC1_APPS_CLK>; + clock-names = "xo", "iface", "core"; ++ resets = <&gcc GCC_SDCC1_BCR>; + max-frequency = <384000000>; + mmc-ddr-1_8v; + mmc-hs200-1_8v; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0160-arm64-dts-pmp8074-add-RTC-node.patch b/target/linux/ipq807x/patches-5.15/0160-arm64-dts-pmp8074-add-RTC-node.patch new file mode 100644 index 00000000000000..3fbdad8d63bcad --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0160-arm64-dts-pmp8074-add-RTC-node.patch @@ -0,0 +1,39 @@ +From cedb36fcb06bdedba556c07d15b13c33e540ba4f Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 20 May 2022 20:57:46 +0200 +Subject: [PATCH 160/162] arm64: dts: pmp8074: add RTC node + +PMP8074 has a PM8941 compatible RTC, so add the required DT node for it. + +It is left disabled by default as PMIC itself has no built-in battery +backup and thus losing power means losing time, so allow boards that +have the optional battery backup connected to enable the RTC. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/pmp8074.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pmp8074.dtsi b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +index 20122d614dd1..4618bb6cd5dc 100644 +--- a/arch/arm64/boot/dts/qcom/pmp8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +@@ -28,6 +28,15 @@ pmp8074_gpios: gpio@c000 { + <0 0xca 0 IRQ_TYPE_NONE>, + <0 0xcb 0 IRQ_TYPE_NONE>; + }; ++ ++ pmp8074_rtc:rtc@6000 { ++ compatible = "qcom,pm8941-rtc"; ++ reg = <0x6000>; ++ reg-names = "rtc", "alarm"; ++ interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>; ++ ++ status = "disabled"; ++ }; + }; + + pmic@1 { +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0161-iio-adc-qcom-spmi-adc5-add-VREF-VADC-channel-to-ADC5.patch b/target/linux/ipq807x/patches-5.15/0161-iio-adc-qcom-spmi-adc5-add-VREF-VADC-channel-to-ADC5.patch new file mode 100644 index 00000000000000..025d440f0a0889 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0161-iio-adc-qcom-spmi-adc5-add-VREF-VADC-channel-to-ADC5.patch @@ -0,0 +1,32 @@ +From fd6e79633c24c8501f6b554cdd0dc4b490ceb468 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 20 May 2022 21:48:04 +0200 +Subject: [PATCH 161/162] iio: adc: qcom-spmi-adc5: add VREF VADC channel to + ADC5 rev2 + +ADC5 rev2 channel 2 is the ADC5_VREF_VADC channel which measures the +ADC LDO that is used is the reference for ADC-s. + +Add support for it so it can be utilized. + +Signed-off-by: Robert Marko +--- + drivers/iio/adc/qcom-spmi-adc5.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c +index 87438d1e5c0b..7bd3745884f0 100644 +--- a/drivers/iio/adc/qcom-spmi-adc5.c ++++ b/drivers/iio/adc/qcom-spmi-adc5.c +@@ -589,6 +589,8 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = { + SCALE_HW_CALIB_DEFAULT) + [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0, + SCALE_HW_CALIB_DEFAULT) ++ [ADC5_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0, ++ SCALE_HW_CALIB_DEFAULT) + [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1, +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/0162-arm64-dts-pmp8074-add-ADC5-node.patch b/target/linux/ipq807x/patches-5.15/0162-arm64-dts-pmp8074-add-ADC5-node.patch new file mode 100644 index 00000000000000..339c9a39e976a9 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/0162-arm64-dts-pmp8074-add-ADC5-node.patch @@ -0,0 +1,99 @@ +From 3d4eb6c9f5fcb27aacba63fc320ef08e60337610 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 20 May 2022 21:15:19 +0200 +Subject: [PATCH 162/162] arm64: dts: pmp8074: add ADC5 node + +PMP8074 has a built in rev2 of the SPMI ADC5 that provides ADC readings +for the reference ground, 1.2V reference, VPH_PWR, PMIC die temperature, +XO temperature, 3 PA thermistors. + +GPIO-s 2, 4, 6 and 10 can also be configured as ADC inputs. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/pmp8074.dtsi | 63 +++++++++++++++++++++++++++ + 1 file changed, 63 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pmp8074.dtsi b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +index 4618bb6cd5dc..5e067babe6bb 100644 +--- a/arch/arm64/boot/dts/qcom/pmp8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +@@ -1,5 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0-only + ++#include + #include + + &spmi_bus { +@@ -29,6 +30,68 @@ pmp8074_gpios: gpio@c000 { + <0 0xcb 0 IRQ_TYPE_NONE>; + }; + ++ pmp8074_adc: adc@3100 { ++ compatible = "qcom,spmi-adc-rev2"; ++ reg = <0x3100>; ++ interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #io-channel-cells = <1>; ++ ++ ref_gnd@0 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vref_1p25@1 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vref_vadc@2 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ die_temp@6 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ xo_therm@4c { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm1@4d { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm2@4e { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm3@4f { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vph_pwr@83 { ++ reg = ; ++ qcom,pre-scaling = <1 3>; ++ }; ++ }; ++ + pmp8074_rtc:rtc@6000 { + compatible = "qcom,pm8941-rtc"; + reg = <0x6000>; +-- +2.36.1 + diff --git a/target/linux/ipq807x/patches-5.15/900-arm64-dts-add-OpenWrt-DTS-files.patch b/target/linux/ipq807x/patches-5.15/900-arm64-dts-add-OpenWrt-DTS-files.patch new file mode 100644 index 00000000000000..d209a8754dc1e8 --- /dev/null +++ b/target/linux/ipq807x/patches-5.15/900-arm64-dts-add-OpenWrt-DTS-files.patch @@ -0,0 +1,27 @@ +From 8aec79b6d4d59616eb6ce4fbfb94658b3e79d9ce Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 11 May 2021 13:29:33 +0200 +Subject: [PATCH] arm64: dts: add OpenWrt DTS files + +Lets add custom OpenWrt DTS files to Makefile. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/Makefile | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/qcom/Makefile ++++ b/arch/arm64/boot/dts/qcom/Makefile +@@ -4,6 +4,12 @@ dtb-$(CONFIG_ARCH_QCOM) += apq8094-sony- + dtb-$(CONFIG_ARCH_QCOM) += apq8096-db820c.dtb + dtb-$(CONFIG_ARCH_QCOM) += apq8096-ifc6640.dtb + dtb-$(CONFIG_ARCH_QCOM) += ipq6018-cp01-c1.dtb ++dtb-$(CONFIG_ARCH_QCOM) += ipq8071-eap102.dtb ++dtb-$(CONFIG_ARCH_QCOM) += ipq8070-cax1800.dtb ++dtb-$(CONFIG_ARCH_QCOM) += ipq8072-301w.dtb ++dtb-$(CONFIG_ARCH_QCOM) += ipq8071-ax6.dtb ++dtb-$(CONFIG_ARCH_QCOM) += ipq8071-ax3600.dtb ++dtb-$(CONFIG_ARCH_QCOM) += ipq8072-ax9000.dtb + dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk01.dtb + dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk10-c1.dtb + dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk10-c2.dtb